xref: /openbmc/linux/drivers/usb/serial/kl5kusb105.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * KLSI KL5KUSB105 chip RS232 converter driver
41da177e4SLinus Torvalds  *
560b3013cSJohan Hovold  *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
61da177e4SLinus Torvalds  *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * All information about the device was acquired using SniffUSB ans snoopUSB
91da177e4SLinus Torvalds  * on Windows98.
101da177e4SLinus Torvalds  * It was written out of frustration with the PalmConnect USB Serial adapter
111da177e4SLinus Torvalds  * sold by Palm Inc.
121da177e4SLinus Torvalds  * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
131da177e4SLinus Torvalds  * information that was not already available.
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  * It seems that KLSI bought some silicon-design information from ScanLogic,
161da177e4SLinus Torvalds  * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
171da177e4SLinus Torvalds  * KLSI has firmware available for their devices; it is probable that the
181da177e4SLinus Torvalds  * firmware differs from that used by KLSI in their products. If you have an
191da177e4SLinus Torvalds  * original KLSI device and can provide some information on it, I would be
201da177e4SLinus Torvalds  * most interested in adding support for it here. If you have any information
211da177e4SLinus Torvalds  * on the protocol used (or find errors in my reverse-engineered stuff), please
221da177e4SLinus Torvalds  * let me know.
231da177e4SLinus Torvalds  *
241da177e4SLinus Torvalds  * The code was only tested with a PalmConnect USB adapter; if you
251da177e4SLinus Torvalds  * are adventurous, try it with any KLSI-based device and let me know how it
261da177e4SLinus Torvalds  * breaks so that I can fix it!
271da177e4SLinus Torvalds  */
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /* TODO:
301da177e4SLinus Torvalds  *	check modem line signals
311da177e4SLinus Torvalds  *	implement handshaking or decide that we do not support it
321da177e4SLinus Torvalds  */
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #include <linux/kernel.h>
351da177e4SLinus Torvalds #include <linux/errno.h>
361da177e4SLinus Torvalds #include <linux/slab.h>
371da177e4SLinus Torvalds #include <linux/tty.h>
381da177e4SLinus Torvalds #include <linux/tty_driver.h>
391da177e4SLinus Torvalds #include <linux/tty_flip.h>
401da177e4SLinus Torvalds #include <linux/module.h>
410c265f4eSAlan Cox #include <linux/uaccess.h>
42fd05e720SAl Viro #include <asm/unaligned.h>
431da177e4SLinus Torvalds #include <linux/usb.h>
44a969888cSGreg Kroah-Hartman #include <linux/usb/serial.h>
451da177e4SLinus Torvalds #include "kl5kusb105.h"
461da177e4SLinus Torvalds 
4760b3013cSJohan Hovold #define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
481da177e4SLinus Torvalds #define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds /*
521da177e4SLinus Torvalds  * Function prototypes
531da177e4SLinus Torvalds  */
5499a6f73cSJohan Hovold static int klsi_105_port_probe(struct usb_serial_port *port);
55c5d1448fSUwe Kleine-König static void klsi_105_port_remove(struct usb_serial_port *port);
56a509a7e4SAlan Cox static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
57335f8514SAlan Cox static void klsi_105_close(struct usb_serial_port *port);
5895da310eSAlan Cox static void klsi_105_set_termios(struct tty_struct *tty,
59*f6d47fe5SIlpo Järvinen 				 struct usb_serial_port *port,
60*f6d47fe5SIlpo Järvinen 				 const struct ktermios *old_termios);
6160b33c13SAlan Cox static int  klsi_105_tiocmget(struct tty_struct *tty);
6260b3013cSJohan Hovold static void klsi_105_process_read_urb(struct urb *urb);
6360b3013cSJohan Hovold static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
6460b3013cSJohan Hovold 						void *dest, size_t size);
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds /*
671da177e4SLinus Torvalds  * All of the device info needed for the KLSI converters.
681da177e4SLinus Torvalds  */
697d40d7e8SNémeth Márton static const struct usb_device_id id_table[] = {
701da177e4SLinus Torvalds 	{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
711da177e4SLinus Torvalds 	{ }		/* Terminating entry */
721da177e4SLinus Torvalds };
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds MODULE_DEVICE_TABLE(usb, id_table);
751da177e4SLinus Torvalds 
76ea65370dSGreg Kroah-Hartman static struct usb_serial_driver kl5kusb105d_device = {
7718fcac35SGreg Kroah-Hartman 	.driver = {
781da177e4SLinus Torvalds 		.owner =	THIS_MODULE,
79269bda1cSGreg Kroah-Hartman 		.name =		"kl5kusb105d",
8018fcac35SGreg Kroah-Hartman 	},
81269bda1cSGreg Kroah-Hartman 	.description =		"KL5KUSB105D / PalmConnect",
821da177e4SLinus Torvalds 	.id_table =		id_table,
831da177e4SLinus Torvalds 	.num_ports =		1,
8460b3013cSJohan Hovold 	.bulk_out_size =	64,
851da177e4SLinus Torvalds 	.open =			klsi_105_open,
861da177e4SLinus Torvalds 	.close =		klsi_105_close,
871da177e4SLinus Torvalds 	.set_termios =		klsi_105_set_termios,
881da177e4SLinus Torvalds 	.tiocmget =		klsi_105_tiocmget,
8999a6f73cSJohan Hovold 	.port_probe =		klsi_105_port_probe,
9099a6f73cSJohan Hovold 	.port_remove =		klsi_105_port_remove,
9160b3013cSJohan Hovold 	.throttle =		usb_serial_generic_throttle,
9260b3013cSJohan Hovold 	.unthrottle =		usb_serial_generic_unthrottle,
9360b3013cSJohan Hovold 	.process_read_urb =	klsi_105_process_read_urb,
9460b3013cSJohan Hovold 	.prepare_write_buffer =	klsi_105_prepare_write_buffer,
951da177e4SLinus Torvalds };
961da177e4SLinus Torvalds 
974d2a7affSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
984d2a7affSAlan Stern 	&kl5kusb105d_device, NULL
994d2a7affSAlan Stern };
1004d2a7affSAlan Stern 
1011da177e4SLinus Torvalds struct klsi_105_port_settings {
102c2a24bb1SJohan Hovold 	u8	pktlen;		/* always 5, it seems */
103c2a24bb1SJohan Hovold 	u8	baudrate;
104c2a24bb1SJohan Hovold 	u8	databits;
105c2a24bb1SJohan Hovold 	u8	unknown1;
106c2a24bb1SJohan Hovold 	u8	unknown2;
107c2a24bb1SJohan Hovold };
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds struct klsi_105_private {
1101da177e4SLinus Torvalds 	struct klsi_105_port_settings	cfg;
1111da177e4SLinus Torvalds 	unsigned long			line_state; /* modem line settings */
1121da177e4SLinus Torvalds 	spinlock_t			lock;
1131da177e4SLinus Torvalds };
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds /*
1171da177e4SLinus Torvalds  * Handle vendor specific USB requests
1181da177e4SLinus Torvalds  */
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds #define KLSI_TIMEOUT	 5000 /* default urb timeout */
1221da177e4SLinus Torvalds 
klsi_105_chg_port_settings(struct usb_serial_port * port,struct klsi_105_port_settings * settings)1231da177e4SLinus Torvalds static int klsi_105_chg_port_settings(struct usb_serial_port *port,
1241da177e4SLinus Torvalds 				      struct klsi_105_port_settings *settings)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	int rc;
1271da177e4SLinus Torvalds 
12871b20b34SHimadri Pandya 	rc = usb_control_msg_send(port->serial->dev,
12971b20b34SHimadri Pandya 				  0,
1301da177e4SLinus Torvalds 				  KL5KUSB105A_SIO_SET_DATA,
13171b20b34SHimadri Pandya 				  USB_TYPE_VENDOR | USB_DIR_OUT |
13271b20b34SHimadri Pandya 				  USB_RECIP_INTERFACE,
1331da177e4SLinus Torvalds 				  0, /* value */
1341da177e4SLinus Torvalds 				  0, /* index */
1351da177e4SLinus Torvalds 				  settings,
1361da177e4SLinus Torvalds 				  sizeof(struct klsi_105_port_settings),
13771b20b34SHimadri Pandya 				  KLSI_TIMEOUT,
13871b20b34SHimadri Pandya 				  GFP_KERNEL);
13971b20b34SHimadri Pandya 	if (rc)
140194343d9SGreg Kroah-Hartman 		dev_err(&port->dev,
141194343d9SGreg Kroah-Hartman 			"Change port settings failed (error = %d)\n", rc);
14205465793SJohan Hovold 
14305465793SJohan Hovold 	dev_dbg(&port->dev,
14405465793SJohan Hovold 		"pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n",
145c197a8dbSGreg Kroah-Hartman 		settings->pktlen, settings->baudrate, settings->databits,
1461da177e4SLinus Torvalds 		settings->unknown1, settings->unknown2);
14705465793SJohan Hovold 
1481da177e4SLinus Torvalds 	return rc;
149ff8c195fSJohan Hovold }
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds /*
1521da177e4SLinus Torvalds  * Read line control via vendor command and return result through
153c8345c05SJohan Hovold  * the state pointer.
1541da177e4SLinus Torvalds  */
klsi_105_get_line_state(struct usb_serial_port * port,unsigned long * state)1551da177e4SLinus Torvalds static int klsi_105_get_line_state(struct usb_serial_port *port,
156c8345c05SJohan Hovold 				   unsigned long *state)
1571da177e4SLinus Torvalds {
1582e0b78daSJohan Hovold 	u16 status;
1591da177e4SLinus Torvalds 	int rc;
1601da177e4SLinus Torvalds 
16171b20b34SHimadri Pandya 	rc = usb_control_msg_recv(port->serial->dev, 0,
1621da177e4SLinus Torvalds 				  KL5KUSB105A_SIO_POLL,
1631da177e4SLinus Torvalds 				  USB_TYPE_VENDOR | USB_DIR_IN,
1641da177e4SLinus Torvalds 				  0, /* value */
1651da177e4SLinus Torvalds 				  0, /* index */
1662e0b78daSJohan Hovold 				  &status, sizeof(status),
16771b20b34SHimadri Pandya 				  10000,
16871b20b34SHimadri Pandya 				  GFP_KERNEL);
16971b20b34SHimadri Pandya 	if (rc) {
170146cc8a1SJohan Hovold 		dev_err(&port->dev, "reading line status failed: %d\n", rc);
171a692d0e6SJohan Hovold 		return rc;
172a692d0e6SJohan Hovold 	}
173a692d0e6SJohan Hovold 
1742e0b78daSJohan Hovold 	le16_to_cpus(&status);
1751da177e4SLinus Torvalds 
1762e0b78daSJohan Hovold 	dev_dbg(&port->dev, "read status %04x\n", status);
1771da177e4SLinus Torvalds 
178c8345c05SJohan Hovold 	*state = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0) |
179c8345c05SJohan Hovold 		 ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0);
180abf492e7SJohan Hovold 
181a692d0e6SJohan Hovold 	return 0;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds /*
1861da177e4SLinus Torvalds  * Driver's tty interface functions
1871da177e4SLinus Torvalds  */
1881da177e4SLinus Torvalds 
klsi_105_port_probe(struct usb_serial_port * port)18999a6f73cSJohan Hovold static int klsi_105_port_probe(struct usb_serial_port *port)
1901da177e4SLinus Torvalds {
1911da177e4SLinus Torvalds 	struct klsi_105_private *priv;
1921da177e4SLinus Torvalds 
19399a6f73cSJohan Hovold 	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
19499a6f73cSJohan Hovold 	if (!priv)
19599a6f73cSJohan Hovold 		return -ENOMEM;
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	/* set initial values for control structures */
1981da177e4SLinus Torvalds 	priv->cfg.pktlen    = 5;
1991da177e4SLinus Torvalds 	priv->cfg.baudrate  = kl5kusb105a_sio_b9600;
2001da177e4SLinus Torvalds 	priv->cfg.databits  = kl5kusb105a_dtb_8;
2011da177e4SLinus Torvalds 	priv->cfg.unknown1  = 0;
2021da177e4SLinus Torvalds 	priv->cfg.unknown2  = 1;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	priv->line_state    = 0;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 	spin_lock_init(&priv->lock);
2071da177e4SLinus Torvalds 
20899a6f73cSJohan Hovold 	usb_set_serial_port_data(port, priv);
2091da177e4SLinus Torvalds 
2109306fff1SOliver Neukum 	return 0;
211ff8c195fSJohan Hovold }
2121da177e4SLinus Torvalds 
klsi_105_port_remove(struct usb_serial_port * port)213c5d1448fSUwe Kleine-König static void klsi_105_port_remove(struct usb_serial_port *port)
214f9c99bb8SAlan Stern {
21599a6f73cSJohan Hovold 	struct klsi_105_private *priv;
216f9c99bb8SAlan Stern 
21799a6f73cSJohan Hovold 	priv = usb_get_serial_port_data(port);
21899a6f73cSJohan Hovold 	kfree(priv);
219ff8c195fSJohan Hovold }
2201da177e4SLinus Torvalds 
klsi_105_open(struct tty_struct * tty,struct usb_serial_port * port)221a509a7e4SAlan Cox static int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	struct klsi_105_private *priv = usb_get_serial_port_data(port);
2241da177e4SLinus Torvalds 	int retval = 0;
2251da177e4SLinus Torvalds 	int rc;
2261da177e4SLinus Torvalds 	unsigned long line_state;
22771b20b34SHimadri Pandya 	struct klsi_105_port_settings cfg;
2281da177e4SLinus Torvalds 	unsigned long flags;
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	/* Do a defined restart:
2311da177e4SLinus Torvalds 	 * Set up sane default baud rate and send the 'READ_ON'
2321da177e4SLinus Torvalds 	 * vendor command.
2331da177e4SLinus Torvalds 	 * FIXME: set modem line control (how?)
2341da177e4SLinus Torvalds 	 * Then read the modem line control and store values in
2351da177e4SLinus Torvalds 	 * priv->line_state.
2361da177e4SLinus Torvalds 	 */
23710c642d0SJohan Hovold 
23871b20b34SHimadri Pandya 	cfg.pktlen   = 5;
23971b20b34SHimadri Pandya 	cfg.baudrate = kl5kusb105a_sio_b9600;
24071b20b34SHimadri Pandya 	cfg.databits = kl5kusb105a_dtb_8;
24171b20b34SHimadri Pandya 	cfg.unknown1 = 0;
24271b20b34SHimadri Pandya 	cfg.unknown2 = 1;
24371b20b34SHimadri Pandya 	klsi_105_chg_port_settings(port, &cfg);
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
24671b20b34SHimadri Pandya 	priv->cfg.pktlen   = cfg.pktlen;
24771b20b34SHimadri Pandya 	priv->cfg.baudrate = cfg.baudrate;
24871b20b34SHimadri Pandya 	priv->cfg.databits = cfg.databits;
24971b20b34SHimadri Pandya 	priv->cfg.unknown1 = cfg.unknown1;
25071b20b34SHimadri Pandya 	priv->cfg.unknown2 = cfg.unknown2;
2511da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	/* READ_ON and urb submission */
25460b3013cSJohan Hovold 	rc = usb_serial_generic_open(tty, port);
2553f203f05SJohan Hovold 	if (rc)
2563f203f05SJohan Hovold 		return rc;
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	rc = usb_control_msg(port->serial->dev,
2591da177e4SLinus Torvalds 			     usb_sndctrlpipe(port->serial->dev, 0),
2601da177e4SLinus Torvalds 			     KL5KUSB105A_SIO_CONFIGURE,
2611da177e4SLinus Torvalds 			     USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
2621da177e4SLinus Torvalds 			     KL5KUSB105A_SIO_CONFIGURE_READ_ON,
2631da177e4SLinus Torvalds 			     0, /* index */
2641da177e4SLinus Torvalds 			     NULL,
2651da177e4SLinus Torvalds 			     0,
2661da177e4SLinus Torvalds 			     KLSI_TIMEOUT);
2671da177e4SLinus Torvalds 	if (rc < 0) {
268194343d9SGreg Kroah-Hartman 		dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
2691da177e4SLinus Torvalds 		retval = rc;
2703c3dd1e0SPan Bian 		goto err_generic_close;
2711da177e4SLinus Torvalds 	} else
2721ad7604fSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 	rc = klsi_105_get_line_state(port, &line_state);
2756774d5f5SJohan Hovold 	if (rc < 0) {
2766774d5f5SJohan Hovold 		retval = rc;
2776774d5f5SJohan Hovold 		goto err_disable_read;
2786774d5f5SJohan Hovold 	}
2796774d5f5SJohan Hovold 
2801da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
2811da177e4SLinus Torvalds 	priv->line_state = line_state;
2821da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
2836774d5f5SJohan Hovold 	dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
2846774d5f5SJohan Hovold 			line_state);
2851da177e4SLinus Torvalds 
2866774d5f5SJohan Hovold 	return 0;
2876774d5f5SJohan Hovold 
2886774d5f5SJohan Hovold err_disable_read:
2896774d5f5SJohan Hovold 	usb_control_msg(port->serial->dev,
2906774d5f5SJohan Hovold 			     usb_sndctrlpipe(port->serial->dev, 0),
2916774d5f5SJohan Hovold 			     KL5KUSB105A_SIO_CONFIGURE,
2926774d5f5SJohan Hovold 			     USB_TYPE_VENDOR | USB_DIR_OUT,
2936774d5f5SJohan Hovold 			     KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
2946774d5f5SJohan Hovold 			     0, /* index */
2956774d5f5SJohan Hovold 			     NULL, 0,
2966774d5f5SJohan Hovold 			     KLSI_TIMEOUT);
2973c3dd1e0SPan Bian err_generic_close:
2986774d5f5SJohan Hovold 	usb_serial_generic_close(port);
2996774d5f5SJohan Hovold 
3001da177e4SLinus Torvalds 	return retval;
301ff8c195fSJohan Hovold }
3021da177e4SLinus Torvalds 
klsi_105_close(struct usb_serial_port * port)303335f8514SAlan Cox static void klsi_105_close(struct usb_serial_port *port)
3041da177e4SLinus Torvalds {
3051da177e4SLinus Torvalds 	int rc;
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	/* send READ_OFF */
3081da177e4SLinus Torvalds 	rc = usb_control_msg(port->serial->dev,
3091da177e4SLinus Torvalds 			     usb_sndctrlpipe(port->serial->dev, 0),
3101da177e4SLinus Torvalds 			     KL5KUSB105A_SIO_CONFIGURE,
3111da177e4SLinus Torvalds 			     USB_TYPE_VENDOR | USB_DIR_OUT,
3121da177e4SLinus Torvalds 			     KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
3131da177e4SLinus Torvalds 			     0, /* index */
3141da177e4SLinus Torvalds 			     NULL, 0,
3151da177e4SLinus Torvalds 			     KLSI_TIMEOUT);
3161da177e4SLinus Torvalds 	if (rc < 0)
3179fcb2e6eSJohan Hovold 		dev_err(&port->dev, "failed to disable read: %d\n", rc);
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	/* shutdown our bulk reads and writes */
32060b3013cSJohan Hovold 	usb_serial_generic_close(port);
321ff8c195fSJohan Hovold }
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds /* We need to write a complete 64-byte data block and encode the
3241da177e4SLinus Torvalds  * number actually sent in the first double-byte, LSB-order. That
3251da177e4SLinus Torvalds  * leaves at most 62 bytes of payload.
3261da177e4SLinus Torvalds  */
32760b3013cSJohan Hovold #define KLSI_HDR_LEN		2
klsi_105_prepare_write_buffer(struct usb_serial_port * port,void * dest,size_t size)32860b3013cSJohan Hovold static int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
32960b3013cSJohan Hovold 						void *dest, size_t size)
3301da177e4SLinus Torvalds {
33160b3013cSJohan Hovold 	unsigned char *buf = dest;
33260b3013cSJohan Hovold 	int count;
3331da177e4SLinus Torvalds 
33460b3013cSJohan Hovold 	count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
33560b3013cSJohan Hovold 								&port->lock);
33660b3013cSJohan Hovold 	put_unaligned_le16(count, buf);
3371da177e4SLinus Torvalds 
33860b3013cSJohan Hovold 	return count + KLSI_HDR_LEN;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
34160b3013cSJohan Hovold /* The data received is preceded by a length double-byte in LSB-first order.
34260b3013cSJohan Hovold  */
klsi_105_process_read_urb(struct urb * urb)34360b3013cSJohan Hovold static void klsi_105_process_read_urb(struct urb *urb)
3441da177e4SLinus Torvalds {
345cdc97792SMing Lei 	struct usb_serial_port *port = urb->context;
3461da177e4SLinus Torvalds 	unsigned char *data = urb->transfer_buffer;
34760b3013cSJohan Hovold 	unsigned len;
3481da177e4SLinus Torvalds 
34960b3013cSJohan Hovold 	/* empty urbs seem to happen, we ignore them */
35060b3013cSJohan Hovold 	if (!urb->actual_length)
35160b3013cSJohan Hovold 		return;
3521da177e4SLinus Torvalds 
35360b3013cSJohan Hovold 	if (urb->actual_length <= KLSI_HDR_LEN) {
3541ad7604fSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
3551da177e4SLinus Torvalds 		return;
3561da177e4SLinus Torvalds 	}
3571da177e4SLinus Torvalds 
35860b3013cSJohan Hovold 	len = get_unaligned_le16(data);
35960b3013cSJohan Hovold 	if (len > urb->actual_length - KLSI_HDR_LEN) {
3601ad7604fSGreg Kroah-Hartman 		dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__);
36160b3013cSJohan Hovold 		len = urb->actual_length - KLSI_HDR_LEN;
3621da177e4SLinus Torvalds 	}
3631da177e4SLinus Torvalds 
36405c7cd39SJiri Slaby 	tty_insert_flip_string(&port->port, data + KLSI_HDR_LEN, len);
3652e124b4aSJiri Slaby 	tty_flip_buffer_push(&port->port);
366ff8c195fSJohan Hovold }
3671da177e4SLinus Torvalds 
klsi_105_set_termios(struct tty_struct * tty,struct usb_serial_port * port,const struct ktermios * old_termios)36895da310eSAlan Cox static void klsi_105_set_termios(struct tty_struct *tty,
36995da310eSAlan Cox 				 struct usb_serial_port *port,
370*f6d47fe5SIlpo Järvinen 				 const struct ktermios *old_termios)
3711da177e4SLinus Torvalds {
3721da177e4SLinus Torvalds 	struct klsi_105_private *priv = usb_get_serial_port_data(port);
3731ad7604fSGreg Kroah-Hartman 	struct device *dev = &port->dev;
374adc8d746SAlan Cox 	unsigned int iflag = tty->termios.c_iflag;
3751da177e4SLinus Torvalds 	unsigned int old_iflag = old_termios->c_iflag;
376adc8d746SAlan Cox 	unsigned int cflag = tty->termios.c_cflag;
3771da177e4SLinus Torvalds 	unsigned int old_cflag = old_termios->c_cflag;
378abf492e7SJohan Hovold 	struct klsi_105_port_settings *cfg;
3791da177e4SLinus Torvalds 	unsigned long flags;
380a5b6f60cSAlan Cox 	speed_t baud;
3811da177e4SLinus Torvalds 
382abf492e7SJohan Hovold 	cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
38310c642d0SJohan Hovold 	if (!cfg)
384abf492e7SJohan Hovold 		return;
385abf492e7SJohan Hovold 
3861da177e4SLinus Torvalds 	/* lock while we are modifying the settings */
3871da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	/*
3901da177e4SLinus Torvalds 	 * Update baud rate
3911da177e4SLinus Torvalds 	 */
392a5b6f60cSAlan Cox 	baud = tty_get_baud_rate(tty);
393a5b6f60cSAlan Cox 
394a5b6f60cSAlan Cox 	switch (baud) {
3953f6ff6efSAlan Cox 	case 0: /* handled below */
3961da177e4SLinus Torvalds 		break;
3973f6ff6efSAlan Cox 	case 1200:
3983f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b1200;
3991da177e4SLinus Torvalds 		break;
4003f6ff6efSAlan Cox 	case 2400:
4013f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b2400;
4021da177e4SLinus Torvalds 		break;
4033f6ff6efSAlan Cox 	case 4800:
4043f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b4800;
4051da177e4SLinus Torvalds 		break;
4063f6ff6efSAlan Cox 	case 9600:
4073f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
4081da177e4SLinus Torvalds 		break;
4093f6ff6efSAlan Cox 	case 19200:
4103f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b19200;
4111da177e4SLinus Torvalds 		break;
4123f6ff6efSAlan Cox 	case 38400:
4133f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b38400;
4141da177e4SLinus Torvalds 		break;
4153f6ff6efSAlan Cox 	case 57600:
4163f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b57600;
4171da177e4SLinus Torvalds 		break;
4183f6ff6efSAlan Cox 	case 115200:
4193f6ff6efSAlan Cox 		priv->cfg.baudrate = kl5kusb105a_sio_b115200;
4201da177e4SLinus Torvalds 		break;
4211da177e4SLinus Torvalds 	default:
422d9a38a87SJohan Hovold 		dev_dbg(dev, "unsupported baudrate, using 9600\n");
4231da177e4SLinus Torvalds 		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
424a5b6f60cSAlan Cox 		baud = 9600;
4251da177e4SLinus Torvalds 		break;
4261da177e4SLinus Torvalds 	}
4272d11f282SJohan Hovold 
4282d11f282SJohan Hovold 	/*
4292d11f282SJohan Hovold 	 * FIXME: implement B0 handling
4302d11f282SJohan Hovold 	 *
4312d11f282SJohan Hovold 	 * Maybe this should be simulated by sending read disable and read
4322d11f282SJohan Hovold 	 * enable messages?
4331da177e4SLinus Torvalds 	 */
4342d11f282SJohan Hovold 
435a5b6f60cSAlan Cox 	tty_encode_baud_rate(tty, baud, baud);
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
4381da177e4SLinus Torvalds 		/* set the number of data bits */
4391da177e4SLinus Torvalds 		switch (cflag & CSIZE) {
4401da177e4SLinus Torvalds 		case CS5:
4411ad7604fSGreg Kroah-Hartman 			dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__);
4421da177e4SLinus Torvalds 			spin_unlock_irqrestore(&priv->lock, flags);
443abf492e7SJohan Hovold 			goto err;
4441da177e4SLinus Torvalds 		case CS6:
4451ad7604fSGreg Kroah-Hartman 			dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__);
4461da177e4SLinus Torvalds 			spin_unlock_irqrestore(&priv->lock, flags);
447abf492e7SJohan Hovold 			goto err;
4481da177e4SLinus Torvalds 		case CS7:
4491da177e4SLinus Torvalds 			priv->cfg.databits = kl5kusb105a_dtb_7;
4501da177e4SLinus Torvalds 			break;
4511da177e4SLinus Torvalds 		case CS8:
4521da177e4SLinus Torvalds 			priv->cfg.databits = kl5kusb105a_dtb_8;
4531da177e4SLinus Torvalds 			break;
4541da177e4SLinus Torvalds 		default:
4551ad7604fSGreg Kroah-Hartman 			dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n");
4561da177e4SLinus Torvalds 			priv->cfg.databits = kl5kusb105a_dtb_8;
4571da177e4SLinus Torvalds 			break;
4581da177e4SLinus Torvalds 		}
4591da177e4SLinus Torvalds 	}
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	/*
4621da177e4SLinus Torvalds 	 * Update line control register (LCR)
4631da177e4SLinus Torvalds 	 */
4641da177e4SLinus Torvalds 	if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
4651da177e4SLinus Torvalds 	    || (cflag & CSTOPB) != (old_cflag & CSTOPB)) {
466a5b6f60cSAlan Cox 		/* Not currently supported */
467adc8d746SAlan Cox 		tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB);
4681da177e4SLinus Torvalds 	}
4691da177e4SLinus Torvalds 	/*
4701da177e4SLinus Torvalds 	 * Set flow control: well, I do not really now how to handle DTR/RTS.
4711da177e4SLinus Torvalds 	 * Just do what we have seen with SniffUSB on Win98.
4721da177e4SLinus Torvalds 	 */
4731da177e4SLinus Torvalds 	if ((iflag & IXOFF) != (old_iflag & IXOFF)
4741da177e4SLinus Torvalds 	    || (iflag & IXON) != (old_iflag & IXON)
4751da177e4SLinus Torvalds 	    ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
476a5b6f60cSAlan Cox 		/* Not currently supported */
477adc8d746SAlan Cox 		tty->termios.c_cflag &= ~CRTSCTS;
4781da177e4SLinus Torvalds 	}
479abf492e7SJohan Hovold 	memcpy(cfg, &priv->cfg, sizeof(*cfg));
4801da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 	/* now commit changes to device */
483abf492e7SJohan Hovold 	klsi_105_chg_port_settings(port, cfg);
484abf492e7SJohan Hovold err:
485abf492e7SJohan Hovold 	kfree(cfg);
486ff8c195fSJohan Hovold }
4871da177e4SLinus Torvalds 
klsi_105_tiocmget(struct tty_struct * tty)48860b33c13SAlan Cox static int klsi_105_tiocmget(struct tty_struct *tty)
4891da177e4SLinus Torvalds {
49095da310eSAlan Cox 	struct usb_serial_port *port = tty->driver_data;
4911da177e4SLinus Torvalds 	struct klsi_105_private *priv = usb_get_serial_port_data(port);
4921da177e4SLinus Torvalds 	unsigned long flags;
4931da177e4SLinus Torvalds 	int rc;
4941da177e4SLinus Torvalds 	unsigned long line_state;
4951da177e4SLinus Torvalds 
4961da177e4SLinus Torvalds 	rc = klsi_105_get_line_state(port, &line_state);
4971da177e4SLinus Torvalds 	if (rc < 0) {
498194343d9SGreg Kroah-Hartman 		dev_err(&port->dev,
499194343d9SGreg Kroah-Hartman 			"Reading line control failed (error = %d)\n", rc);
5001da177e4SLinus Torvalds 		/* better return value? EAGAIN? */
5011da177e4SLinus Torvalds 		return rc;
5021da177e4SLinus Torvalds 	}
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds 	spin_lock_irqsave(&priv->lock, flags);
5051da177e4SLinus Torvalds 	priv->line_state = line_state;
5061da177e4SLinus Torvalds 	spin_unlock_irqrestore(&priv->lock, flags);
5071ad7604fSGreg Kroah-Hartman 	dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
5081da177e4SLinus Torvalds 	return (int)line_state;
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds 
51168e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table);
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds MODULE_AUTHOR(DRIVER_AUTHOR);
5141da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
5151da177e4SLinus Torvalds MODULE_LICENSE("GPL");
516