162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * USB IR Dongle driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Copyright (C) 2001-2002	Greg Kroah-Hartman (greg@kroah.com)
662306a36Sopenharmony_ci *	Copyright (C) 2002	Gary Brubaker (xavyer@ix.netcom.com)
762306a36Sopenharmony_ci *	Copyright (C) 2010	Johan Hovold (jhovold@gmail.com)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This driver allows a USB IrDA device to be used as a "dumb" serial device.
1062306a36Sopenharmony_ci * This can be useful if you do not have access to a full IrDA stack on the
1162306a36Sopenharmony_ci * other side of the connection.  If you do have an IrDA stack on both devices,
1262306a36Sopenharmony_ci * please use the usb-irda driver, as it contains the proper error checking and
1362306a36Sopenharmony_ci * other goodness of a full IrDA stack.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Portions of this driver were taken from drivers/net/irda/irda-usb.c, which
1662306a36Sopenharmony_ci * was written by Roman Weissgaerber <weissg@vienna.at>, Dag Brattli
1762306a36Sopenharmony_ci * <dag@brattli.net>, and Jean Tourrilhes <jt@hpl.hp.com>
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this
2062306a36Sopenharmony_ci * driver
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/kernel.h>
2462306a36Sopenharmony_ci#include <linux/errno.h>
2562306a36Sopenharmony_ci#include <linux/init.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/tty.h>
2862306a36Sopenharmony_ci#include <linux/tty_driver.h>
2962306a36Sopenharmony_ci#include <linux/tty_flip.h>
3062306a36Sopenharmony_ci#include <linux/module.h>
3162306a36Sopenharmony_ci#include <linux/spinlock.h>
3262306a36Sopenharmony_ci#include <linux/uaccess.h>
3362306a36Sopenharmony_ci#include <linux/usb.h>
3462306a36Sopenharmony_ci#include <linux/usb/serial.h>
3562306a36Sopenharmony_ci#include <linux/usb/irda.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Johan Hovold <jhovold@gmail.com>"
3862306a36Sopenharmony_ci#define DRIVER_DESC "USB IR Dongle driver"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* if overridden by the user, then use their value for the size of the read and
4162306a36Sopenharmony_ci * write urbs */
4262306a36Sopenharmony_cistatic int buffer_size;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* if overridden by the user, then use the specified number of XBOFs */
4562306a36Sopenharmony_cistatic int xbof = -1;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int  ir_startup (struct usb_serial *serial);
4862306a36Sopenharmony_cistatic int ir_write(struct tty_struct *tty, struct usb_serial_port *port,
4962306a36Sopenharmony_ci		const unsigned char *buf, int count);
5062306a36Sopenharmony_cistatic unsigned int ir_write_room(struct tty_struct *tty);
5162306a36Sopenharmony_cistatic void ir_write_bulk_callback(struct urb *urb);
5262306a36Sopenharmony_cistatic void ir_process_read_urb(struct urb *urb);
5362306a36Sopenharmony_cistatic void ir_set_termios(struct tty_struct *tty,
5462306a36Sopenharmony_ci			   struct usb_serial_port *port,
5562306a36Sopenharmony_ci			   const struct ktermios *old_termios);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Not that this lot means you can only have one per system */
5862306a36Sopenharmony_cistatic u8 ir_baud;
5962306a36Sopenharmony_cistatic u8 ir_xbof;
6062306a36Sopenharmony_cistatic u8 ir_add_bof;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct usb_device_id ir_id_table[] = {
6362306a36Sopenharmony_ci	{ USB_DEVICE(0x050f, 0x0180) },		/* KC Technology, KC-180 */
6462306a36Sopenharmony_ci	{ USB_DEVICE(0x08e9, 0x0100) },		/* XTNDAccess */
6562306a36Sopenharmony_ci	{ USB_DEVICE(0x09c4, 0x0011) },		/* ACTiSys ACT-IR2000U */
6662306a36Sopenharmony_ci	{ USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, USB_SUBCLASS_IRDA, 0) },
6762306a36Sopenharmony_ci	{ }					/* Terminating entry */
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ir_id_table);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic struct usb_serial_driver ir_device = {
7362306a36Sopenharmony_ci	.driver	= {
7462306a36Sopenharmony_ci		.owner	= THIS_MODULE,
7562306a36Sopenharmony_ci		.name	= "ir-usb",
7662306a36Sopenharmony_ci	},
7762306a36Sopenharmony_ci	.description		= "IR Dongle",
7862306a36Sopenharmony_ci	.id_table		= ir_id_table,
7962306a36Sopenharmony_ci	.num_ports		= 1,
8062306a36Sopenharmony_ci	.num_bulk_in		= 1,
8162306a36Sopenharmony_ci	.num_bulk_out		= 1,
8262306a36Sopenharmony_ci	.set_termios		= ir_set_termios,
8362306a36Sopenharmony_ci	.attach			= ir_startup,
8462306a36Sopenharmony_ci	.write			= ir_write,
8562306a36Sopenharmony_ci	.write_room		= ir_write_room,
8662306a36Sopenharmony_ci	.write_bulk_callback	= ir_write_bulk_callback,
8762306a36Sopenharmony_ci	.process_read_urb	= ir_process_read_urb,
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
9162306a36Sopenharmony_ci	&ir_device, NULL
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic inline void irda_usb_dump_class_desc(struct usb_serial *serial,
9562306a36Sopenharmony_ci					    struct usb_irda_cs_descriptor *desc)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct device *dev = &serial->dev->dev;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	dev_dbg(dev, "bLength=%x\n", desc->bLength);
10062306a36Sopenharmony_ci	dev_dbg(dev, "bDescriptorType=%x\n", desc->bDescriptorType);
10162306a36Sopenharmony_ci	dev_dbg(dev, "bcdSpecRevision=%x\n", __le16_to_cpu(desc->bcdSpecRevision));
10262306a36Sopenharmony_ci	dev_dbg(dev, "bmDataSize=%x\n", desc->bmDataSize);
10362306a36Sopenharmony_ci	dev_dbg(dev, "bmWindowSize=%x\n", desc->bmWindowSize);
10462306a36Sopenharmony_ci	dev_dbg(dev, "bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime);
10562306a36Sopenharmony_ci	dev_dbg(dev, "wBaudRate=%x\n", __le16_to_cpu(desc->wBaudRate));
10662306a36Sopenharmony_ci	dev_dbg(dev, "bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs);
10762306a36Sopenharmony_ci	dev_dbg(dev, "bIrdaRateSniff=%x\n", desc->bIrdaRateSniff);
10862306a36Sopenharmony_ci	dev_dbg(dev, "bMaxUnicastList=%x\n", desc->bMaxUnicastList);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*------------------------------------------------------------------*/
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * Function irda_usb_find_class_desc(dev, ifnum)
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci *    Returns instance of IrDA class descriptor, or NULL if not found
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * The class descriptor is some extra info that IrDA USB devices will
11862306a36Sopenharmony_ci * offer to us, describing their IrDA characteristics. We will use that in
11962306a36Sopenharmony_ci * irda_usb_init_qos()
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci * Based on the same function in drivers/net/irda/irda-usb.c
12262306a36Sopenharmony_ci */
12362306a36Sopenharmony_cistatic struct usb_irda_cs_descriptor *
12462306a36Sopenharmony_ciirda_usb_find_class_desc(struct usb_serial *serial, unsigned int ifnum)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct usb_device *dev = serial->dev;
12762306a36Sopenharmony_ci	struct usb_irda_cs_descriptor *desc;
12862306a36Sopenharmony_ci	int ret;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
13162306a36Sopenharmony_ci	if (!desc)
13262306a36Sopenharmony_ci		return NULL;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
13562306a36Sopenharmony_ci			USB_REQ_CS_IRDA_GET_CLASS_DESC,
13662306a36Sopenharmony_ci			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
13762306a36Sopenharmony_ci			0, ifnum, desc, sizeof(*desc), 1000);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	dev_dbg(&serial->dev->dev, "%s -  ret=%d\n", __func__, ret);
14062306a36Sopenharmony_ci	if (ret < (int)sizeof(*desc)) {
14162306a36Sopenharmony_ci		dev_dbg(&serial->dev->dev,
14262306a36Sopenharmony_ci			"%s - class descriptor read %s (%d)\n", __func__,
14362306a36Sopenharmony_ci			(ret < 0) ? "failed" : "too short", ret);
14462306a36Sopenharmony_ci		goto error;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci	if (desc->bDescriptorType != USB_DT_CS_IRDA) {
14762306a36Sopenharmony_ci		dev_dbg(&serial->dev->dev, "%s - bad class descriptor type\n",
14862306a36Sopenharmony_ci			__func__);
14962306a36Sopenharmony_ci		goto error;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	irda_usb_dump_class_desc(serial, desc);
15362306a36Sopenharmony_ci	return desc;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cierror:
15662306a36Sopenharmony_ci	kfree(desc);
15762306a36Sopenharmony_ci	return NULL;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic u8 ir_xbof_change(u8 xbof)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	u8 result;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* reference irda-usb.c */
16562306a36Sopenharmony_ci	switch (xbof) {
16662306a36Sopenharmony_ci	case 48:
16762306a36Sopenharmony_ci		result = 0x10;
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci	case 28:
17062306a36Sopenharmony_ci	case 24:
17162306a36Sopenharmony_ci		result = 0x20;
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	default:
17462306a36Sopenharmony_ci	case 12:
17562306a36Sopenharmony_ci		result = 0x30;
17662306a36Sopenharmony_ci		break;
17762306a36Sopenharmony_ci	case  5:
17862306a36Sopenharmony_ci	case  6:
17962306a36Sopenharmony_ci		result = 0x40;
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	case  3:
18262306a36Sopenharmony_ci		result = 0x50;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case  2:
18562306a36Sopenharmony_ci		result = 0x60;
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	case  1:
18862306a36Sopenharmony_ci		result = 0x70;
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case  0:
19162306a36Sopenharmony_ci		result = 0x80;
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return(result);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int ir_startup(struct usb_serial *serial)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct usb_irda_cs_descriptor *irda_desc;
20162306a36Sopenharmony_ci	int rates;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	irda_desc = irda_usb_find_class_desc(serial, 0);
20462306a36Sopenharmony_ci	if (!irda_desc) {
20562306a36Sopenharmony_ci		dev_err(&serial->dev->dev,
20662306a36Sopenharmony_ci			"IRDA class descriptor not found, device not bound\n");
20762306a36Sopenharmony_ci		return -ENODEV;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	rates = le16_to_cpu(irda_desc->wBaudRate);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	dev_dbg(&serial->dev->dev,
21362306a36Sopenharmony_ci		"%s - Baud rates supported:%s%s%s%s%s%s%s%s%s\n",
21462306a36Sopenharmony_ci		__func__,
21562306a36Sopenharmony_ci		(rates & USB_IRDA_BR_2400) ? " 2400" : "",
21662306a36Sopenharmony_ci		(rates & USB_IRDA_BR_9600) ? " 9600" : "",
21762306a36Sopenharmony_ci		(rates & USB_IRDA_BR_19200) ? " 19200" : "",
21862306a36Sopenharmony_ci		(rates & USB_IRDA_BR_38400) ? " 38400" : "",
21962306a36Sopenharmony_ci		(rates & USB_IRDA_BR_57600) ? " 57600" : "",
22062306a36Sopenharmony_ci		(rates & USB_IRDA_BR_115200) ? " 115200" : "",
22162306a36Sopenharmony_ci		(rates & USB_IRDA_BR_576000) ? " 576000" : "",
22262306a36Sopenharmony_ci		(rates & USB_IRDA_BR_1152000) ? " 1152000" : "",
22362306a36Sopenharmony_ci		(rates & USB_IRDA_BR_4000000) ? " 4000000" : "");
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	switch (irda_desc->bmAdditionalBOFs) {
22662306a36Sopenharmony_ci	case USB_IRDA_AB_48:
22762306a36Sopenharmony_ci		ir_add_bof = 48;
22862306a36Sopenharmony_ci		break;
22962306a36Sopenharmony_ci	case USB_IRDA_AB_24:
23062306a36Sopenharmony_ci		ir_add_bof = 24;
23162306a36Sopenharmony_ci		break;
23262306a36Sopenharmony_ci	case USB_IRDA_AB_12:
23362306a36Sopenharmony_ci		ir_add_bof = 12;
23462306a36Sopenharmony_ci		break;
23562306a36Sopenharmony_ci	case USB_IRDA_AB_6:
23662306a36Sopenharmony_ci		ir_add_bof = 6;
23762306a36Sopenharmony_ci		break;
23862306a36Sopenharmony_ci	case USB_IRDA_AB_3:
23962306a36Sopenharmony_ci		ir_add_bof = 3;
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	case USB_IRDA_AB_2:
24262306a36Sopenharmony_ci		ir_add_bof = 2;
24362306a36Sopenharmony_ci		break;
24462306a36Sopenharmony_ci	case USB_IRDA_AB_1:
24562306a36Sopenharmony_ci		ir_add_bof = 1;
24662306a36Sopenharmony_ci		break;
24762306a36Sopenharmony_ci	case USB_IRDA_AB_0:
24862306a36Sopenharmony_ci		ir_add_bof = 0;
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	default:
25162306a36Sopenharmony_ci		break;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	kfree(irda_desc);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int ir_write(struct tty_struct *tty, struct usb_serial_port *port,
26062306a36Sopenharmony_ci		const unsigned char *buf, int count)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct urb *urb = NULL;
26362306a36Sopenharmony_ci	unsigned long flags;
26462306a36Sopenharmony_ci	int ret;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (port->bulk_out_size == 0)
26762306a36Sopenharmony_ci		return -EINVAL;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (count == 0)
27062306a36Sopenharmony_ci		return 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	count = min(count, port->bulk_out_size - 1);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
27562306a36Sopenharmony_ci	if (__test_and_clear_bit(0, &port->write_urbs_free)) {
27662306a36Sopenharmony_ci		urb = port->write_urbs[0];
27762306a36Sopenharmony_ci		port->tx_bytes += count;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (!urb)
28262306a36Sopenharmony_ci		return 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/*
28562306a36Sopenharmony_ci	 * The first byte of the packet we send to the device contains an
28662306a36Sopenharmony_ci	 * outbound header which indicates an additional number of BOFs and
28762306a36Sopenharmony_ci	 * a baud rate change.
28862306a36Sopenharmony_ci	 *
28962306a36Sopenharmony_ci	 * See section 5.4.2.2 of the USB IrDA spec.
29062306a36Sopenharmony_ci	 */
29162306a36Sopenharmony_ci	*(u8 *)urb->transfer_buffer = ir_xbof | ir_baud;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	memcpy(urb->transfer_buffer + 1, buf, count);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	urb->transfer_buffer_length = count + 1;
29662306a36Sopenharmony_ci	urb->transfer_flags = URB_ZERO_PACKET;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	ret = usb_submit_urb(urb, GFP_ATOMIC);
29962306a36Sopenharmony_ci	if (ret) {
30062306a36Sopenharmony_ci		dev_err(&port->dev, "failed to submit write urb: %d\n", ret);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		spin_lock_irqsave(&port->lock, flags);
30362306a36Sopenharmony_ci		__set_bit(0, &port->write_urbs_free);
30462306a36Sopenharmony_ci		port->tx_bytes -= count;
30562306a36Sopenharmony_ci		spin_unlock_irqrestore(&port->lock, flags);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		return ret;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return count;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void ir_write_bulk_callback(struct urb *urb)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct usb_serial_port *port = urb->context;
31662306a36Sopenharmony_ci	int status = urb->status;
31762306a36Sopenharmony_ci	unsigned long flags;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
32062306a36Sopenharmony_ci	__set_bit(0, &port->write_urbs_free);
32162306a36Sopenharmony_ci	port->tx_bytes -= urb->transfer_buffer_length - 1;
32262306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	switch (status) {
32562306a36Sopenharmony_ci	case 0:
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	case -ENOENT:
32862306a36Sopenharmony_ci	case -ECONNRESET:
32962306a36Sopenharmony_ci	case -ESHUTDOWN:
33062306a36Sopenharmony_ci		dev_dbg(&port->dev, "write urb stopped: %d\n", status);
33162306a36Sopenharmony_ci		return;
33262306a36Sopenharmony_ci	case -EPIPE:
33362306a36Sopenharmony_ci		dev_err(&port->dev, "write urb stopped: %d\n", status);
33462306a36Sopenharmony_ci		return;
33562306a36Sopenharmony_ci	default:
33662306a36Sopenharmony_ci		dev_err(&port->dev, "nonzero write-urb status: %d\n", status);
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	usb_serial_port_softint(port);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic unsigned int ir_write_room(struct tty_struct *tty)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
34662306a36Sopenharmony_ci	unsigned int count = 0;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (port->bulk_out_size == 0)
34962306a36Sopenharmony_ci		return 0;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (test_bit(0, &port->write_urbs_free))
35262306a36Sopenharmony_ci		count = port->bulk_out_size - 1;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return count;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void ir_process_read_urb(struct urb *urb)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct usb_serial_port *port = urb->context;
36062306a36Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (!urb->actual_length)
36362306a36Sopenharmony_ci		return;
36462306a36Sopenharmony_ci	/*
36562306a36Sopenharmony_ci	 * The first byte of the packet we get from the device
36662306a36Sopenharmony_ci	 * contains a busy indicator and baud rate change.
36762306a36Sopenharmony_ci	 * See section 5.4.1.2 of the USB IrDA spec.
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	if (*data & 0x0f)
37062306a36Sopenharmony_ci		ir_baud = *data & 0x0f;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (urb->actual_length == 1)
37362306a36Sopenharmony_ci		return;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	tty_insert_flip_string(&port->port, data + 1, urb->actual_length - 1);
37662306a36Sopenharmony_ci	tty_flip_buffer_push(&port->port);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void ir_set_termios(struct tty_struct *tty,
38062306a36Sopenharmony_ci			   struct usb_serial_port *port,
38162306a36Sopenharmony_ci			   const struct ktermios *old_termios)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct usb_device *udev = port->serial->dev;
38462306a36Sopenharmony_ci	unsigned char *transfer_buffer;
38562306a36Sopenharmony_ci	int actual_length;
38662306a36Sopenharmony_ci	speed_t baud;
38762306a36Sopenharmony_ci	int ir_baud;
38862306a36Sopenharmony_ci	int ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	baud = tty_get_baud_rate(tty);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/*
39362306a36Sopenharmony_ci	 * FIXME, we should compare the baud request against the
39462306a36Sopenharmony_ci	 * capability stated in the IR header that we got in the
39562306a36Sopenharmony_ci	 * startup function.
39662306a36Sopenharmony_ci	 */
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	switch (baud) {
39962306a36Sopenharmony_ci	case 2400:
40062306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_2400;
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	case 9600:
40362306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_9600;
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	case 19200:
40662306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_19200;
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case 38400:
40962306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_38400;
41062306a36Sopenharmony_ci		break;
41162306a36Sopenharmony_ci	case 57600:
41262306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_57600;
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	case 115200:
41562306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_115200;
41662306a36Sopenharmony_ci		break;
41762306a36Sopenharmony_ci	case 576000:
41862306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_576000;
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	case 1152000:
42162306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_1152000;
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci	case 4000000:
42462306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_4000000;
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci	default:
42762306a36Sopenharmony_ci		ir_baud = USB_IRDA_LS_9600;
42862306a36Sopenharmony_ci		baud = 9600;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (xbof == -1)
43262306a36Sopenharmony_ci		ir_xbof = ir_xbof_change(ir_add_bof);
43362306a36Sopenharmony_ci	else
43462306a36Sopenharmony_ci		ir_xbof = ir_xbof_change(xbof) ;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/* Only speed changes are supported */
43762306a36Sopenharmony_ci	tty_termios_copy_hw(&tty->termios, old_termios);
43862306a36Sopenharmony_ci	tty_encode_baud_rate(tty, baud, baud);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * send the baud change out on an "empty" data packet
44262306a36Sopenharmony_ci	 */
44362306a36Sopenharmony_ci	transfer_buffer = kmalloc(1, GFP_KERNEL);
44462306a36Sopenharmony_ci	if (!transfer_buffer)
44562306a36Sopenharmony_ci		return;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	*transfer_buffer = ir_xbof | ir_baud;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	ret = usb_bulk_msg(udev,
45062306a36Sopenharmony_ci			usb_sndbulkpipe(udev, port->bulk_out_endpointAddress),
45162306a36Sopenharmony_ci			transfer_buffer, 1, &actual_length, 5000);
45262306a36Sopenharmony_ci	if (ret || actual_length != 1) {
45362306a36Sopenharmony_ci		if (!ret)
45462306a36Sopenharmony_ci			ret = -EIO;
45562306a36Sopenharmony_ci		dev_err(&port->dev, "failed to change line speed: %d\n", ret);
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	kfree(transfer_buffer);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int __init ir_init(void)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	if (buffer_size) {
46462306a36Sopenharmony_ci		ir_device.bulk_in_size = buffer_size;
46562306a36Sopenharmony_ci		ir_device.bulk_out_size = buffer_size;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ir_id_table);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic void __exit ir_exit(void)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	usb_serial_deregister_drivers(serial_drivers);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cimodule_init(ir_init);
47862306a36Sopenharmony_cimodule_exit(ir_exit);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
48162306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
48262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cimodule_param(xbof, int, 0);
48562306a36Sopenharmony_ciMODULE_PARM_DESC(xbof, "Force specific number of XBOFs");
48662306a36Sopenharmony_cimodule_param(buffer_size, int, 0);
48762306a36Sopenharmony_ciMODULE_PARM_DESC(buffer_size, "Size of the transfer buffers");
48862306a36Sopenharmony_ci
489