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