162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * KLSI KL5KUSB105 chip RS232 converter driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *   Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
662306a36Sopenharmony_ci *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * All information about the device was acquired using SniffUSB ans snoopUSB
962306a36Sopenharmony_ci * on Windows98.
1062306a36Sopenharmony_ci * It was written out of frustration with the PalmConnect USB Serial adapter
1162306a36Sopenharmony_ci * sold by Palm Inc.
1262306a36Sopenharmony_ci * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
1362306a36Sopenharmony_ci * information that was not already available.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * It seems that KLSI bought some silicon-design information from ScanLogic,
1662306a36Sopenharmony_ci * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
1762306a36Sopenharmony_ci * KLSI has firmware available for their devices; it is probable that the
1862306a36Sopenharmony_ci * firmware differs from that used by KLSI in their products. If you have an
1962306a36Sopenharmony_ci * original KLSI device and can provide some information on it, I would be
2062306a36Sopenharmony_ci * most interested in adding support for it here. If you have any information
2162306a36Sopenharmony_ci * on the protocol used (or find errors in my reverse-engineered stuff), please
2262306a36Sopenharmony_ci * let me know.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * The code was only tested with a PalmConnect USB adapter; if you
2562306a36Sopenharmony_ci * are adventurous, try it with any KLSI-based device and let me know how it
2662306a36Sopenharmony_ci * breaks so that I can fix it!
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* TODO:
3062306a36Sopenharmony_ci *	check modem line signals
3162306a36Sopenharmony_ci *	implement handshaking or decide that we do not support it
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/kernel.h>
3562306a36Sopenharmony_ci#include <linux/errno.h>
3662306a36Sopenharmony_ci#include <linux/slab.h>
3762306a36Sopenharmony_ci#include <linux/tty.h>
3862306a36Sopenharmony_ci#include <linux/tty_driver.h>
3962306a36Sopenharmony_ci#include <linux/tty_flip.h>
4062306a36Sopenharmony_ci#include <linux/module.h>
4162306a36Sopenharmony_ci#include <linux/uaccess.h>
4262306a36Sopenharmony_ci#include <asm/unaligned.h>
4362306a36Sopenharmony_ci#include <linux/usb.h>
4462306a36Sopenharmony_ci#include <linux/usb/serial.h>
4562306a36Sopenharmony_ci#include "kl5kusb105.h"
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>"
4862306a36Sopenharmony_ci#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Function prototypes
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic int klsi_105_port_probe(struct usb_serial_port *port);
5562306a36Sopenharmony_cistatic void klsi_105_port_remove(struct usb_serial_port *port);
5662306a36Sopenharmony_cistatic int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port);
5762306a36Sopenharmony_cistatic void klsi_105_close(struct usb_serial_port *port);
5862306a36Sopenharmony_cistatic void klsi_105_set_termios(struct tty_struct *tty,
5962306a36Sopenharmony_ci				 struct usb_serial_port *port,
6062306a36Sopenharmony_ci				 const struct ktermios *old_termios);
6162306a36Sopenharmony_cistatic int  klsi_105_tiocmget(struct tty_struct *tty);
6262306a36Sopenharmony_cistatic void klsi_105_process_read_urb(struct urb *urb);
6362306a36Sopenharmony_cistatic int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
6462306a36Sopenharmony_ci						void *dest, size_t size);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * All of the device info needed for the KLSI converters.
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = {
7062306a36Sopenharmony_ci	{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
7162306a36Sopenharmony_ci	{ }		/* Terminating entry */
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct usb_serial_driver kl5kusb105d_device = {
7762306a36Sopenharmony_ci	.driver = {
7862306a36Sopenharmony_ci		.owner =	THIS_MODULE,
7962306a36Sopenharmony_ci		.name =		"kl5kusb105d",
8062306a36Sopenharmony_ci	},
8162306a36Sopenharmony_ci	.description =		"KL5KUSB105D / PalmConnect",
8262306a36Sopenharmony_ci	.id_table =		id_table,
8362306a36Sopenharmony_ci	.num_ports =		1,
8462306a36Sopenharmony_ci	.bulk_out_size =	64,
8562306a36Sopenharmony_ci	.open =			klsi_105_open,
8662306a36Sopenharmony_ci	.close =		klsi_105_close,
8762306a36Sopenharmony_ci	.set_termios =		klsi_105_set_termios,
8862306a36Sopenharmony_ci	.tiocmget =		klsi_105_tiocmget,
8962306a36Sopenharmony_ci	.port_probe =		klsi_105_port_probe,
9062306a36Sopenharmony_ci	.port_remove =		klsi_105_port_remove,
9162306a36Sopenharmony_ci	.throttle =		usb_serial_generic_throttle,
9262306a36Sopenharmony_ci	.unthrottle =		usb_serial_generic_unthrottle,
9362306a36Sopenharmony_ci	.process_read_urb =	klsi_105_process_read_urb,
9462306a36Sopenharmony_ci	.prepare_write_buffer =	klsi_105_prepare_write_buffer,
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
9862306a36Sopenharmony_ci	&kl5kusb105d_device, NULL
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct klsi_105_port_settings {
10262306a36Sopenharmony_ci	u8	pktlen;		/* always 5, it seems */
10362306a36Sopenharmony_ci	u8	baudrate;
10462306a36Sopenharmony_ci	u8	databits;
10562306a36Sopenharmony_ci	u8	unknown1;
10662306a36Sopenharmony_ci	u8	unknown2;
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistruct klsi_105_private {
11062306a36Sopenharmony_ci	struct klsi_105_port_settings	cfg;
11162306a36Sopenharmony_ci	unsigned long			line_state; /* modem line settings */
11262306a36Sopenharmony_ci	spinlock_t			lock;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/*
11762306a36Sopenharmony_ci * Handle vendor specific USB requests
11862306a36Sopenharmony_ci */
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define KLSI_TIMEOUT	 5000 /* default urb timeout */
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int klsi_105_chg_port_settings(struct usb_serial_port *port,
12462306a36Sopenharmony_ci				      struct klsi_105_port_settings *settings)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int rc;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	rc = usb_control_msg_send(port->serial->dev,
12962306a36Sopenharmony_ci				  0,
13062306a36Sopenharmony_ci				  KL5KUSB105A_SIO_SET_DATA,
13162306a36Sopenharmony_ci				  USB_TYPE_VENDOR | USB_DIR_OUT |
13262306a36Sopenharmony_ci				  USB_RECIP_INTERFACE,
13362306a36Sopenharmony_ci				  0, /* value */
13462306a36Sopenharmony_ci				  0, /* index */
13562306a36Sopenharmony_ci				  settings,
13662306a36Sopenharmony_ci				  sizeof(struct klsi_105_port_settings),
13762306a36Sopenharmony_ci				  KLSI_TIMEOUT,
13862306a36Sopenharmony_ci				  GFP_KERNEL);
13962306a36Sopenharmony_ci	if (rc)
14062306a36Sopenharmony_ci		dev_err(&port->dev,
14162306a36Sopenharmony_ci			"Change port settings failed (error = %d)\n", rc);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	dev_dbg(&port->dev,
14462306a36Sopenharmony_ci		"pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n",
14562306a36Sopenharmony_ci		settings->pktlen, settings->baudrate, settings->databits,
14662306a36Sopenharmony_ci		settings->unknown1, settings->unknown2);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return rc;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * Read line control via vendor command and return result through
15362306a36Sopenharmony_ci * the state pointer.
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic int klsi_105_get_line_state(struct usb_serial_port *port,
15662306a36Sopenharmony_ci				   unsigned long *state)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	u16 status;
15962306a36Sopenharmony_ci	int rc;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	rc = usb_control_msg_recv(port->serial->dev, 0,
16262306a36Sopenharmony_ci				  KL5KUSB105A_SIO_POLL,
16362306a36Sopenharmony_ci				  USB_TYPE_VENDOR | USB_DIR_IN,
16462306a36Sopenharmony_ci				  0, /* value */
16562306a36Sopenharmony_ci				  0, /* index */
16662306a36Sopenharmony_ci				  &status, sizeof(status),
16762306a36Sopenharmony_ci				  10000,
16862306a36Sopenharmony_ci				  GFP_KERNEL);
16962306a36Sopenharmony_ci	if (rc) {
17062306a36Sopenharmony_ci		dev_err(&port->dev, "reading line status failed: %d\n", rc);
17162306a36Sopenharmony_ci		return rc;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	le16_to_cpus(&status);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	dev_dbg(&port->dev, "read status %04x\n", status);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	*state = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0) |
17962306a36Sopenharmony_ci		 ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * Driver's tty interface functions
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int klsi_105_port_probe(struct usb_serial_port *port)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct klsi_105_private *priv;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
19462306a36Sopenharmony_ci	if (!priv)
19562306a36Sopenharmony_ci		return -ENOMEM;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* set initial values for control structures */
19862306a36Sopenharmony_ci	priv->cfg.pktlen    = 5;
19962306a36Sopenharmony_ci	priv->cfg.baudrate  = kl5kusb105a_sio_b9600;
20062306a36Sopenharmony_ci	priv->cfg.databits  = kl5kusb105a_dtb_8;
20162306a36Sopenharmony_ci	priv->cfg.unknown1  = 0;
20262306a36Sopenharmony_ci	priv->cfg.unknown2  = 1;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	priv->line_state    = 0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	spin_lock_init(&priv->lock);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	usb_set_serial_port_data(port, priv);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void klsi_105_port_remove(struct usb_serial_port *port)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct klsi_105_private *priv;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	priv = usb_get_serial_port_data(port);
21862306a36Sopenharmony_ci	kfree(priv);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int  klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct klsi_105_private *priv = usb_get_serial_port_data(port);
22462306a36Sopenharmony_ci	int retval = 0;
22562306a36Sopenharmony_ci	int rc;
22662306a36Sopenharmony_ci	unsigned long line_state;
22762306a36Sopenharmony_ci	struct klsi_105_port_settings cfg;
22862306a36Sopenharmony_ci	unsigned long flags;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* Do a defined restart:
23162306a36Sopenharmony_ci	 * Set up sane default baud rate and send the 'READ_ON'
23262306a36Sopenharmony_ci	 * vendor command.
23362306a36Sopenharmony_ci	 * FIXME: set modem line control (how?)
23462306a36Sopenharmony_ci	 * Then read the modem line control and store values in
23562306a36Sopenharmony_ci	 * priv->line_state.
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	cfg.pktlen   = 5;
23962306a36Sopenharmony_ci	cfg.baudrate = kl5kusb105a_sio_b9600;
24062306a36Sopenharmony_ci	cfg.databits = kl5kusb105a_dtb_8;
24162306a36Sopenharmony_ci	cfg.unknown1 = 0;
24262306a36Sopenharmony_ci	cfg.unknown2 = 1;
24362306a36Sopenharmony_ci	klsi_105_chg_port_settings(port, &cfg);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
24662306a36Sopenharmony_ci	priv->cfg.pktlen   = cfg.pktlen;
24762306a36Sopenharmony_ci	priv->cfg.baudrate = cfg.baudrate;
24862306a36Sopenharmony_ci	priv->cfg.databits = cfg.databits;
24962306a36Sopenharmony_ci	priv->cfg.unknown1 = cfg.unknown1;
25062306a36Sopenharmony_ci	priv->cfg.unknown2 = cfg.unknown2;
25162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* READ_ON and urb submission */
25462306a36Sopenharmony_ci	rc = usb_serial_generic_open(tty, port);
25562306a36Sopenharmony_ci	if (rc)
25662306a36Sopenharmony_ci		return rc;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	rc = usb_control_msg(port->serial->dev,
25962306a36Sopenharmony_ci			     usb_sndctrlpipe(port->serial->dev, 0),
26062306a36Sopenharmony_ci			     KL5KUSB105A_SIO_CONFIGURE,
26162306a36Sopenharmony_ci			     USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
26262306a36Sopenharmony_ci			     KL5KUSB105A_SIO_CONFIGURE_READ_ON,
26362306a36Sopenharmony_ci			     0, /* index */
26462306a36Sopenharmony_ci			     NULL,
26562306a36Sopenharmony_ci			     0,
26662306a36Sopenharmony_ci			     KLSI_TIMEOUT);
26762306a36Sopenharmony_ci	if (rc < 0) {
26862306a36Sopenharmony_ci		dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
26962306a36Sopenharmony_ci		retval = rc;
27062306a36Sopenharmony_ci		goto err_generic_close;
27162306a36Sopenharmony_ci	} else
27262306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	rc = klsi_105_get_line_state(port, &line_state);
27562306a36Sopenharmony_ci	if (rc < 0) {
27662306a36Sopenharmony_ci		retval = rc;
27762306a36Sopenharmony_ci		goto err_disable_read;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
28162306a36Sopenharmony_ci	priv->line_state = line_state;
28262306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
28362306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
28462306a36Sopenharmony_ci			line_state);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return 0;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cierr_disable_read:
28962306a36Sopenharmony_ci	usb_control_msg(port->serial->dev,
29062306a36Sopenharmony_ci			     usb_sndctrlpipe(port->serial->dev, 0),
29162306a36Sopenharmony_ci			     KL5KUSB105A_SIO_CONFIGURE,
29262306a36Sopenharmony_ci			     USB_TYPE_VENDOR | USB_DIR_OUT,
29362306a36Sopenharmony_ci			     KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
29462306a36Sopenharmony_ci			     0, /* index */
29562306a36Sopenharmony_ci			     NULL, 0,
29662306a36Sopenharmony_ci			     KLSI_TIMEOUT);
29762306a36Sopenharmony_cierr_generic_close:
29862306a36Sopenharmony_ci	usb_serial_generic_close(port);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return retval;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void klsi_105_close(struct usb_serial_port *port)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int rc;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* send READ_OFF */
30862306a36Sopenharmony_ci	rc = usb_control_msg(port->serial->dev,
30962306a36Sopenharmony_ci			     usb_sndctrlpipe(port->serial->dev, 0),
31062306a36Sopenharmony_ci			     KL5KUSB105A_SIO_CONFIGURE,
31162306a36Sopenharmony_ci			     USB_TYPE_VENDOR | USB_DIR_OUT,
31262306a36Sopenharmony_ci			     KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
31362306a36Sopenharmony_ci			     0, /* index */
31462306a36Sopenharmony_ci			     NULL, 0,
31562306a36Sopenharmony_ci			     KLSI_TIMEOUT);
31662306a36Sopenharmony_ci	if (rc < 0)
31762306a36Sopenharmony_ci		dev_err(&port->dev, "failed to disable read: %d\n", rc);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* shutdown our bulk reads and writes */
32062306a36Sopenharmony_ci	usb_serial_generic_close(port);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/* We need to write a complete 64-byte data block and encode the
32462306a36Sopenharmony_ci * number actually sent in the first double-byte, LSB-order. That
32562306a36Sopenharmony_ci * leaves at most 62 bytes of payload.
32662306a36Sopenharmony_ci */
32762306a36Sopenharmony_ci#define KLSI_HDR_LEN		2
32862306a36Sopenharmony_cistatic int klsi_105_prepare_write_buffer(struct usb_serial_port *port,
32962306a36Sopenharmony_ci						void *dest, size_t size)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	unsigned char *buf = dest;
33262306a36Sopenharmony_ci	int count;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size,
33562306a36Sopenharmony_ci								&port->lock);
33662306a36Sopenharmony_ci	put_unaligned_le16(count, buf);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return count + KLSI_HDR_LEN;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/* The data received is preceded by a length double-byte in LSB-first order.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic void klsi_105_process_read_urb(struct urb *urb)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct usb_serial_port *port = urb->context;
34662306a36Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
34762306a36Sopenharmony_ci	unsigned len;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* empty urbs seem to happen, we ignore them */
35062306a36Sopenharmony_ci	if (!urb->actual_length)
35162306a36Sopenharmony_ci		return;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (urb->actual_length <= KLSI_HDR_LEN) {
35462306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
35562306a36Sopenharmony_ci		return;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	len = get_unaligned_le16(data);
35962306a36Sopenharmony_ci	if (len > urb->actual_length - KLSI_HDR_LEN) {
36062306a36Sopenharmony_ci		dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__);
36162306a36Sopenharmony_ci		len = urb->actual_length - KLSI_HDR_LEN;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	tty_insert_flip_string(&port->port, data + KLSI_HDR_LEN, len);
36562306a36Sopenharmony_ci	tty_flip_buffer_push(&port->port);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic void klsi_105_set_termios(struct tty_struct *tty,
36962306a36Sopenharmony_ci				 struct usb_serial_port *port,
37062306a36Sopenharmony_ci				 const struct ktermios *old_termios)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct klsi_105_private *priv = usb_get_serial_port_data(port);
37362306a36Sopenharmony_ci	struct device *dev = &port->dev;
37462306a36Sopenharmony_ci	unsigned int iflag = tty->termios.c_iflag;
37562306a36Sopenharmony_ci	unsigned int old_iflag = old_termios->c_iflag;
37662306a36Sopenharmony_ci	unsigned int cflag = tty->termios.c_cflag;
37762306a36Sopenharmony_ci	unsigned int old_cflag = old_termios->c_cflag;
37862306a36Sopenharmony_ci	struct klsi_105_port_settings *cfg;
37962306a36Sopenharmony_ci	unsigned long flags;
38062306a36Sopenharmony_ci	speed_t baud;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
38362306a36Sopenharmony_ci	if (!cfg)
38462306a36Sopenharmony_ci		return;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* lock while we are modifying the settings */
38762306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * Update baud rate
39162306a36Sopenharmony_ci	 */
39262306a36Sopenharmony_ci	baud = tty_get_baud_rate(tty);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	switch (baud) {
39562306a36Sopenharmony_ci	case 0: /* handled below */
39662306a36Sopenharmony_ci		break;
39762306a36Sopenharmony_ci	case 1200:
39862306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b1200;
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	case 2400:
40162306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b2400;
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci	case 4800:
40462306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b4800;
40562306a36Sopenharmony_ci		break;
40662306a36Sopenharmony_ci	case 9600:
40762306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
40862306a36Sopenharmony_ci		break;
40962306a36Sopenharmony_ci	case 19200:
41062306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b19200;
41162306a36Sopenharmony_ci		break;
41262306a36Sopenharmony_ci	case 38400:
41362306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b38400;
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci	case 57600:
41662306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b57600;
41762306a36Sopenharmony_ci		break;
41862306a36Sopenharmony_ci	case 115200:
41962306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b115200;
42062306a36Sopenharmony_ci		break;
42162306a36Sopenharmony_ci	default:
42262306a36Sopenharmony_ci		dev_dbg(dev, "unsupported baudrate, using 9600\n");
42362306a36Sopenharmony_ci		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
42462306a36Sopenharmony_ci		baud = 9600;
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/*
42962306a36Sopenharmony_ci	 * FIXME: implement B0 handling
43062306a36Sopenharmony_ci	 *
43162306a36Sopenharmony_ci	 * Maybe this should be simulated by sending read disable and read
43262306a36Sopenharmony_ci	 * enable messages?
43362306a36Sopenharmony_ci	 */
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	tty_encode_baud_rate(tty, baud, baud);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
43862306a36Sopenharmony_ci		/* set the number of data bits */
43962306a36Sopenharmony_ci		switch (cflag & CSIZE) {
44062306a36Sopenharmony_ci		case CS5:
44162306a36Sopenharmony_ci			dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__);
44262306a36Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
44362306a36Sopenharmony_ci			goto err;
44462306a36Sopenharmony_ci		case CS6:
44562306a36Sopenharmony_ci			dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__);
44662306a36Sopenharmony_ci			spin_unlock_irqrestore(&priv->lock, flags);
44762306a36Sopenharmony_ci			goto err;
44862306a36Sopenharmony_ci		case CS7:
44962306a36Sopenharmony_ci			priv->cfg.databits = kl5kusb105a_dtb_7;
45062306a36Sopenharmony_ci			break;
45162306a36Sopenharmony_ci		case CS8:
45262306a36Sopenharmony_ci			priv->cfg.databits = kl5kusb105a_dtb_8;
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		default:
45562306a36Sopenharmony_ci			dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n");
45662306a36Sopenharmony_ci			priv->cfg.databits = kl5kusb105a_dtb_8;
45762306a36Sopenharmony_ci			break;
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/*
46262306a36Sopenharmony_ci	 * Update line control register (LCR)
46362306a36Sopenharmony_ci	 */
46462306a36Sopenharmony_ci	if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
46562306a36Sopenharmony_ci	    || (cflag & CSTOPB) != (old_cflag & CSTOPB)) {
46662306a36Sopenharmony_ci		/* Not currently supported */
46762306a36Sopenharmony_ci		tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB);
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci	/*
47062306a36Sopenharmony_ci	 * Set flow control: well, I do not really now how to handle DTR/RTS.
47162306a36Sopenharmony_ci	 * Just do what we have seen with SniffUSB on Win98.
47262306a36Sopenharmony_ci	 */
47362306a36Sopenharmony_ci	if ((iflag & IXOFF) != (old_iflag & IXOFF)
47462306a36Sopenharmony_ci	    || (iflag & IXON) != (old_iflag & IXON)
47562306a36Sopenharmony_ci	    ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
47662306a36Sopenharmony_ci		/* Not currently supported */
47762306a36Sopenharmony_ci		tty->termios.c_cflag &= ~CRTSCTS;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	memcpy(cfg, &priv->cfg, sizeof(*cfg));
48062306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* now commit changes to device */
48362306a36Sopenharmony_ci	klsi_105_chg_port_settings(port, cfg);
48462306a36Sopenharmony_cierr:
48562306a36Sopenharmony_ci	kfree(cfg);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int klsi_105_tiocmget(struct tty_struct *tty)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
49162306a36Sopenharmony_ci	struct klsi_105_private *priv = usb_get_serial_port_data(port);
49262306a36Sopenharmony_ci	unsigned long flags;
49362306a36Sopenharmony_ci	int rc;
49462306a36Sopenharmony_ci	unsigned long line_state;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	rc = klsi_105_get_line_state(port, &line_state);
49762306a36Sopenharmony_ci	if (rc < 0) {
49862306a36Sopenharmony_ci		dev_err(&port->dev,
49962306a36Sopenharmony_ci			"Reading line control failed (error = %d)\n", rc);
50062306a36Sopenharmony_ci		/* better return value? EAGAIN? */
50162306a36Sopenharmony_ci		return rc;
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	spin_lock_irqsave(&priv->lock, flags);
50562306a36Sopenharmony_ci	priv->line_state = line_state;
50662306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->lock, flags);
50762306a36Sopenharmony_ci	dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
50862306a36Sopenharmony_ci	return (int)line_state;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
51462306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
51562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
516