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