18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * KLSI KL5KUSB105 chip RS232 converter driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * All information about the device was acquired using SniffUSB ans snoopUSB 98c2ecf20Sopenharmony_ci * on Windows98. 108c2ecf20Sopenharmony_ci * It was written out of frustration with the PalmConnect USB Serial adapter 118c2ecf20Sopenharmony_ci * sold by Palm Inc. 128c2ecf20Sopenharmony_ci * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided 138c2ecf20Sopenharmony_ci * information that was not already available. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * It seems that KLSI bought some silicon-design information from ScanLogic, 168c2ecf20Sopenharmony_ci * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI. 178c2ecf20Sopenharmony_ci * KLSI has firmware available for their devices; it is probable that the 188c2ecf20Sopenharmony_ci * firmware differs from that used by KLSI in their products. If you have an 198c2ecf20Sopenharmony_ci * original KLSI device and can provide some information on it, I would be 208c2ecf20Sopenharmony_ci * most interested in adding support for it here. If you have any information 218c2ecf20Sopenharmony_ci * on the protocol used (or find errors in my reverse-engineered stuff), please 228c2ecf20Sopenharmony_ci * let me know. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * The code was only tested with a PalmConnect USB adapter; if you 258c2ecf20Sopenharmony_ci * are adventurous, try it with any KLSI-based device and let me know how it 268c2ecf20Sopenharmony_ci * breaks so that I can fix it! 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* TODO: 308c2ecf20Sopenharmony_ci * check modem line signals 318c2ecf20Sopenharmony_ci * implement handshaking or decide that we do not support it 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/errno.h> 368c2ecf20Sopenharmony_ci#include <linux/slab.h> 378c2ecf20Sopenharmony_ci#include <linux/tty.h> 388c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 398c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 428c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 438c2ecf20Sopenharmony_ci#include <linux/usb.h> 448c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 458c2ecf20Sopenharmony_ci#include "kl5kusb105.h" 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>, Johan Hovold <jhovold@gmail.com>" 488c2ecf20Sopenharmony_ci#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Function prototypes 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic int klsi_105_port_probe(struct usb_serial_port *port); 558c2ecf20Sopenharmony_cistatic int klsi_105_port_remove(struct usb_serial_port *port); 568c2ecf20Sopenharmony_cistatic int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port); 578c2ecf20Sopenharmony_cistatic void klsi_105_close(struct usb_serial_port *port); 588c2ecf20Sopenharmony_cistatic void klsi_105_set_termios(struct tty_struct *tty, 598c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old); 608c2ecf20Sopenharmony_cistatic int klsi_105_tiocmget(struct tty_struct *tty); 618c2ecf20Sopenharmony_cistatic void klsi_105_process_read_urb(struct urb *urb); 628c2ecf20Sopenharmony_cistatic int klsi_105_prepare_write_buffer(struct usb_serial_port *port, 638c2ecf20Sopenharmony_ci void *dest, size_t size); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * All of the device info needed for the KLSI converters. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 698c2ecf20Sopenharmony_ci { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) }, 708c2ecf20Sopenharmony_ci { } /* Terminating entry */ 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic struct usb_serial_driver kl5kusb105d_device = { 768c2ecf20Sopenharmony_ci .driver = { 778c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 788c2ecf20Sopenharmony_ci .name = "kl5kusb105d", 798c2ecf20Sopenharmony_ci }, 808c2ecf20Sopenharmony_ci .description = "KL5KUSB105D / PalmConnect", 818c2ecf20Sopenharmony_ci .id_table = id_table, 828c2ecf20Sopenharmony_ci .num_ports = 1, 838c2ecf20Sopenharmony_ci .bulk_out_size = 64, 848c2ecf20Sopenharmony_ci .open = klsi_105_open, 858c2ecf20Sopenharmony_ci .close = klsi_105_close, 868c2ecf20Sopenharmony_ci .set_termios = klsi_105_set_termios, 878c2ecf20Sopenharmony_ci .tiocmget = klsi_105_tiocmget, 888c2ecf20Sopenharmony_ci .port_probe = klsi_105_port_probe, 898c2ecf20Sopenharmony_ci .port_remove = klsi_105_port_remove, 908c2ecf20Sopenharmony_ci .throttle = usb_serial_generic_throttle, 918c2ecf20Sopenharmony_ci .unthrottle = usb_serial_generic_unthrottle, 928c2ecf20Sopenharmony_ci .process_read_urb = klsi_105_process_read_urb, 938c2ecf20Sopenharmony_ci .prepare_write_buffer = klsi_105_prepare_write_buffer, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 978c2ecf20Sopenharmony_ci &kl5kusb105d_device, NULL 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct klsi_105_port_settings { 1018c2ecf20Sopenharmony_ci u8 pktlen; /* always 5, it seems */ 1028c2ecf20Sopenharmony_ci u8 baudrate; 1038c2ecf20Sopenharmony_ci u8 databits; 1048c2ecf20Sopenharmony_ci u8 unknown1; 1058c2ecf20Sopenharmony_ci u8 unknown2; 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistruct klsi_105_private { 1098c2ecf20Sopenharmony_ci struct klsi_105_port_settings cfg; 1108c2ecf20Sopenharmony_ci unsigned long line_state; /* modem line settings */ 1118c2ecf20Sopenharmony_ci spinlock_t lock; 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * Handle vendor specific USB requests 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define KLSI_TIMEOUT 5000 /* default urb timeout */ 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int klsi_105_chg_port_settings(struct usb_serial_port *port, 1238c2ecf20Sopenharmony_ci struct klsi_105_port_settings *settings) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int rc; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci rc = usb_control_msg(port->serial->dev, 1288c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 1298c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_SET_DATA, 1308c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE, 1318c2ecf20Sopenharmony_ci 0, /* value */ 1328c2ecf20Sopenharmony_ci 0, /* index */ 1338c2ecf20Sopenharmony_ci settings, 1348c2ecf20Sopenharmony_ci sizeof(struct klsi_105_port_settings), 1358c2ecf20Sopenharmony_ci KLSI_TIMEOUT); 1368c2ecf20Sopenharmony_ci if (rc < 0) 1378c2ecf20Sopenharmony_ci dev_err(&port->dev, 1388c2ecf20Sopenharmony_ci "Change port settings failed (error = %d)\n", rc); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci dev_dbg(&port->dev, 1418c2ecf20Sopenharmony_ci "pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n", 1428c2ecf20Sopenharmony_ci settings->pktlen, settings->baudrate, settings->databits, 1438c2ecf20Sopenharmony_ci settings->unknown1, settings->unknown2); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return rc; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* translate a 16-bit status value from the device to linux's TIO bits */ 1498c2ecf20Sopenharmony_cistatic unsigned long klsi_105_status2linestate(const __u16 status) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci unsigned long res = 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci res = ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0) 1548c2ecf20Sopenharmony_ci | ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0) 1558c2ecf20Sopenharmony_ci ; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return res; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * Read line control via vendor command and return result through 1628c2ecf20Sopenharmony_ci * *line_state_p 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci/* It seems that the status buffer has always only 2 bytes length */ 1658c2ecf20Sopenharmony_ci#define KLSI_STATUSBUF_LEN 2 1668c2ecf20Sopenharmony_cistatic int klsi_105_get_line_state(struct usb_serial_port *port, 1678c2ecf20Sopenharmony_ci unsigned long *line_state_p) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int rc; 1708c2ecf20Sopenharmony_ci u8 *status_buf; 1718c2ecf20Sopenharmony_ci __u16 status; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!status_buf) 1758c2ecf20Sopenharmony_ci return -ENOMEM; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci status_buf[0] = 0xff; 1788c2ecf20Sopenharmony_ci status_buf[1] = 0xff; 1798c2ecf20Sopenharmony_ci rc = usb_control_msg(port->serial->dev, 1808c2ecf20Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 1818c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_POLL, 1828c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_IN, 1838c2ecf20Sopenharmony_ci 0, /* value */ 1848c2ecf20Sopenharmony_ci 0, /* index */ 1858c2ecf20Sopenharmony_ci status_buf, KLSI_STATUSBUF_LEN, 1868c2ecf20Sopenharmony_ci 10000 1878c2ecf20Sopenharmony_ci ); 1888c2ecf20Sopenharmony_ci if (rc != KLSI_STATUSBUF_LEN) { 1898c2ecf20Sopenharmony_ci dev_err(&port->dev, "reading line status failed: %d\n", rc); 1908c2ecf20Sopenharmony_ci if (rc >= 0) 1918c2ecf20Sopenharmony_ci rc = -EIO; 1928c2ecf20Sopenharmony_ci } else { 1938c2ecf20Sopenharmony_ci status = get_unaligned_le16(status_buf); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "read status %02x %02x\n", 1968c2ecf20Sopenharmony_ci status_buf[0], status_buf[1]); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci *line_state_p = klsi_105_status2linestate(status); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci kfree(status_buf); 2028c2ecf20Sopenharmony_ci return rc; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * Driver's tty interface functions 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int klsi_105_port_probe(struct usb_serial_port *port) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct klsi_105_private *priv; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci priv = kmalloc(sizeof(*priv), GFP_KERNEL); 2158c2ecf20Sopenharmony_ci if (!priv) 2168c2ecf20Sopenharmony_ci return -ENOMEM; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* set initial values for control structures */ 2198c2ecf20Sopenharmony_ci priv->cfg.pktlen = 5; 2208c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b9600; 2218c2ecf20Sopenharmony_ci priv->cfg.databits = kl5kusb105a_dtb_8; 2228c2ecf20Sopenharmony_ci priv->cfg.unknown1 = 0; 2238c2ecf20Sopenharmony_ci priv->cfg.unknown2 = 1; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci priv->line_state = 0; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci usb_set_serial_port_data(port, priv); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int klsi_105_port_remove(struct usb_serial_port *port) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct klsi_105_private *priv; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 2398c2ecf20Sopenharmony_ci kfree(priv); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct klsi_105_private *priv = usb_get_serial_port_data(port); 2478c2ecf20Sopenharmony_ci int retval = 0; 2488c2ecf20Sopenharmony_ci int rc; 2498c2ecf20Sopenharmony_ci unsigned long line_state; 2508c2ecf20Sopenharmony_ci struct klsi_105_port_settings *cfg; 2518c2ecf20Sopenharmony_ci unsigned long flags; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Do a defined restart: 2548c2ecf20Sopenharmony_ci * Set up sane default baud rate and send the 'READ_ON' 2558c2ecf20Sopenharmony_ci * vendor command. 2568c2ecf20Sopenharmony_ci * FIXME: set modem line control (how?) 2578c2ecf20Sopenharmony_ci * Then read the modem line control and store values in 2588c2ecf20Sopenharmony_ci * priv->line_state. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); 2618c2ecf20Sopenharmony_ci if (!cfg) 2628c2ecf20Sopenharmony_ci return -ENOMEM; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci cfg->pktlen = 5; 2658c2ecf20Sopenharmony_ci cfg->baudrate = kl5kusb105a_sio_b9600; 2668c2ecf20Sopenharmony_ci cfg->databits = kl5kusb105a_dtb_8; 2678c2ecf20Sopenharmony_ci cfg->unknown1 = 0; 2688c2ecf20Sopenharmony_ci cfg->unknown2 = 1; 2698c2ecf20Sopenharmony_ci klsi_105_chg_port_settings(port, cfg); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2728c2ecf20Sopenharmony_ci priv->cfg.pktlen = cfg->pktlen; 2738c2ecf20Sopenharmony_ci priv->cfg.baudrate = cfg->baudrate; 2748c2ecf20Sopenharmony_ci priv->cfg.databits = cfg->databits; 2758c2ecf20Sopenharmony_ci priv->cfg.unknown1 = cfg->unknown1; 2768c2ecf20Sopenharmony_ci priv->cfg.unknown2 = cfg->unknown2; 2778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci kfree(cfg); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* READ_ON and urb submission */ 2828c2ecf20Sopenharmony_ci rc = usb_serial_generic_open(tty, port); 2838c2ecf20Sopenharmony_ci if (rc) 2848c2ecf20Sopenharmony_ci return rc; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci rc = usb_control_msg(port->serial->dev, 2878c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 2888c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_CONFIGURE, 2898c2ecf20Sopenharmony_ci USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE, 2908c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_CONFIGURE_READ_ON, 2918c2ecf20Sopenharmony_ci 0, /* index */ 2928c2ecf20Sopenharmony_ci NULL, 2938c2ecf20Sopenharmony_ci 0, 2948c2ecf20Sopenharmony_ci KLSI_TIMEOUT); 2958c2ecf20Sopenharmony_ci if (rc < 0) { 2968c2ecf20Sopenharmony_ci dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc); 2978c2ecf20Sopenharmony_ci retval = rc; 2988c2ecf20Sopenharmony_ci goto err_generic_close; 2998c2ecf20Sopenharmony_ci } else 3008c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - enabled reading\n", __func__); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci rc = klsi_105_get_line_state(port, &line_state); 3038c2ecf20Sopenharmony_ci if (rc < 0) { 3048c2ecf20Sopenharmony_ci retval = rc; 3058c2ecf20Sopenharmony_ci goto err_disable_read; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 3098c2ecf20Sopenharmony_ci priv->line_state = line_state; 3108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 3118c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, 3128c2ecf20Sopenharmony_ci line_state); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cierr_disable_read: 3178c2ecf20Sopenharmony_ci usb_control_msg(port->serial->dev, 3188c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 3198c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_CONFIGURE, 3208c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT, 3218c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_CONFIGURE_READ_OFF, 3228c2ecf20Sopenharmony_ci 0, /* index */ 3238c2ecf20Sopenharmony_ci NULL, 0, 3248c2ecf20Sopenharmony_ci KLSI_TIMEOUT); 3258c2ecf20Sopenharmony_cierr_generic_close: 3268c2ecf20Sopenharmony_ci usb_serial_generic_close(port); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return retval; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void klsi_105_close(struct usb_serial_port *port) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int rc; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* send READ_OFF */ 3368c2ecf20Sopenharmony_ci rc = usb_control_msg(port->serial->dev, 3378c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 3388c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_CONFIGURE, 3398c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT, 3408c2ecf20Sopenharmony_ci KL5KUSB105A_SIO_CONFIGURE_READ_OFF, 3418c2ecf20Sopenharmony_ci 0, /* index */ 3428c2ecf20Sopenharmony_ci NULL, 0, 3438c2ecf20Sopenharmony_ci KLSI_TIMEOUT); 3448c2ecf20Sopenharmony_ci if (rc < 0) 3458c2ecf20Sopenharmony_ci dev_err(&port->dev, "failed to disable read: %d\n", rc); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* shutdown our bulk reads and writes */ 3488c2ecf20Sopenharmony_ci usb_serial_generic_close(port); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* We need to write a complete 64-byte data block and encode the 3528c2ecf20Sopenharmony_ci * number actually sent in the first double-byte, LSB-order. That 3538c2ecf20Sopenharmony_ci * leaves at most 62 bytes of payload. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci#define KLSI_HDR_LEN 2 3568c2ecf20Sopenharmony_cistatic int klsi_105_prepare_write_buffer(struct usb_serial_port *port, 3578c2ecf20Sopenharmony_ci void *dest, size_t size) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci unsigned char *buf = dest; 3608c2ecf20Sopenharmony_ci int count; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci count = kfifo_out_locked(&port->write_fifo, buf + KLSI_HDR_LEN, size, 3638c2ecf20Sopenharmony_ci &port->lock); 3648c2ecf20Sopenharmony_ci put_unaligned_le16(count, buf); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return count + KLSI_HDR_LEN; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* The data received is preceded by a length double-byte in LSB-first order. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_cistatic void klsi_105_process_read_urb(struct urb *urb) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 3748c2ecf20Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 3758c2ecf20Sopenharmony_ci unsigned len; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* empty urbs seem to happen, we ignore them */ 3788c2ecf20Sopenharmony_ci if (!urb->actual_length) 3798c2ecf20Sopenharmony_ci return; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (urb->actual_length <= KLSI_HDR_LEN) { 3828c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - malformed packet\n", __func__); 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci len = get_unaligned_le16(data); 3878c2ecf20Sopenharmony_ci if (len > urb->actual_length - KLSI_HDR_LEN) { 3888c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__); 3898c2ecf20Sopenharmony_ci len = urb->actual_length - KLSI_HDR_LEN; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci tty_insert_flip_string(&port->port, data + KLSI_HDR_LEN, len); 3938c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void klsi_105_set_termios(struct tty_struct *tty, 3978c2ecf20Sopenharmony_ci struct usb_serial_port *port, 3988c2ecf20Sopenharmony_ci struct ktermios *old_termios) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct klsi_105_private *priv = usb_get_serial_port_data(port); 4018c2ecf20Sopenharmony_ci struct device *dev = &port->dev; 4028c2ecf20Sopenharmony_ci unsigned int iflag = tty->termios.c_iflag; 4038c2ecf20Sopenharmony_ci unsigned int old_iflag = old_termios->c_iflag; 4048c2ecf20Sopenharmony_ci unsigned int cflag = tty->termios.c_cflag; 4058c2ecf20Sopenharmony_ci unsigned int old_cflag = old_termios->c_cflag; 4068c2ecf20Sopenharmony_ci struct klsi_105_port_settings *cfg; 4078c2ecf20Sopenharmony_ci unsigned long flags; 4088c2ecf20Sopenharmony_ci speed_t baud; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); 4118c2ecf20Sopenharmony_ci if (!cfg) 4128c2ecf20Sopenharmony_ci return; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* lock while we are modifying the settings */ 4158c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* 4188c2ecf20Sopenharmony_ci * Update baud rate 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci baud = tty_get_baud_rate(tty); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci switch (baud) { 4238c2ecf20Sopenharmony_ci case 0: /* handled below */ 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case 1200: 4268c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b1200; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case 2400: 4298c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b2400; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case 4800: 4328c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b4800; 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci case 9600: 4358c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b9600; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci case 19200: 4388c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b19200; 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci case 38400: 4418c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b38400; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci case 57600: 4448c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b57600; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case 115200: 4478c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b115200; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci default: 4508c2ecf20Sopenharmony_ci dev_dbg(dev, "unsupported baudrate, using 9600\n"); 4518c2ecf20Sopenharmony_ci priv->cfg.baudrate = kl5kusb105a_sio_b9600; 4528c2ecf20Sopenharmony_ci baud = 9600; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * FIXME: implement B0 handling 4588c2ecf20Sopenharmony_ci * 4598c2ecf20Sopenharmony_ci * Maybe this should be simulated by sending read disable and read 4608c2ecf20Sopenharmony_ci * enable messages? 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if ((cflag & CSIZE) != (old_cflag & CSIZE)) { 4668c2ecf20Sopenharmony_ci /* set the number of data bits */ 4678c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 4688c2ecf20Sopenharmony_ci case CS5: 4698c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__); 4708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 4718c2ecf20Sopenharmony_ci goto err; 4728c2ecf20Sopenharmony_ci case CS6: 4738c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__); 4748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 4758c2ecf20Sopenharmony_ci goto err; 4768c2ecf20Sopenharmony_ci case CS7: 4778c2ecf20Sopenharmony_ci priv->cfg.databits = kl5kusb105a_dtb_7; 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci case CS8: 4808c2ecf20Sopenharmony_ci priv->cfg.databits = kl5kusb105a_dtb_8; 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci default: 4838c2ecf20Sopenharmony_ci dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n"); 4848c2ecf20Sopenharmony_ci priv->cfg.databits = kl5kusb105a_dtb_8; 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * Update line control register (LCR) 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD)) 4938c2ecf20Sopenharmony_ci || (cflag & CSTOPB) != (old_cflag & CSTOPB)) { 4948c2ecf20Sopenharmony_ci /* Not currently supported */ 4958c2ecf20Sopenharmony_ci tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * Set flow control: well, I do not really now how to handle DTR/RTS. 4998c2ecf20Sopenharmony_ci * Just do what we have seen with SniffUSB on Win98. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci if ((iflag & IXOFF) != (old_iflag & IXOFF) 5028c2ecf20Sopenharmony_ci || (iflag & IXON) != (old_iflag & IXON) 5038c2ecf20Sopenharmony_ci || (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { 5048c2ecf20Sopenharmony_ci /* Not currently supported */ 5058c2ecf20Sopenharmony_ci tty->termios.c_cflag &= ~CRTSCTS; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci memcpy(cfg, &priv->cfg, sizeof(*cfg)); 5088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* now commit changes to device */ 5118c2ecf20Sopenharmony_ci klsi_105_chg_port_settings(port, cfg); 5128c2ecf20Sopenharmony_cierr: 5138c2ecf20Sopenharmony_ci kfree(cfg); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int klsi_105_tiocmget(struct tty_struct *tty) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 5198c2ecf20Sopenharmony_ci struct klsi_105_private *priv = usb_get_serial_port_data(port); 5208c2ecf20Sopenharmony_ci unsigned long flags; 5218c2ecf20Sopenharmony_ci int rc; 5228c2ecf20Sopenharmony_ci unsigned long line_state; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci rc = klsi_105_get_line_state(port, &line_state); 5258c2ecf20Sopenharmony_ci if (rc < 0) { 5268c2ecf20Sopenharmony_ci dev_err(&port->dev, 5278c2ecf20Sopenharmony_ci "Reading line control failed (error = %d)\n", rc); 5288c2ecf20Sopenharmony_ci /* better return value? EAGAIN? */ 5298c2ecf20Sopenharmony_ci return rc; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 5338c2ecf20Sopenharmony_ci priv->line_state = line_state; 5348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 5358c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state); 5368c2ecf20Sopenharmony_ci return (int)line_state; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 5428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 5438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 544