18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Ours Technology Inc. OTi-6858 USB to serial adapter driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyleft (C) 2007 Kees Lemmens (adapted for kernel 2.6.20) 68c2ecf20Sopenharmony_ci * Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail) 78c2ecf20Sopenharmony_ci * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) 88c2ecf20Sopenharmony_ci * Copyright (C) 2003 IBM Corp. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Many thanks to the authors of pl2303 driver: all functions in this file 118c2ecf20Sopenharmony_ci * are heavily based on pl2303 code, buffering code is a 1-to-1 copy. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Warning! You use this driver on your own risk! The only official 148c2ecf20Sopenharmony_ci * description of this device I have is datasheet from manufacturer, 158c2ecf20Sopenharmony_ci * and it doesn't contain almost any information needed to write a driver. 168c2ecf20Sopenharmony_ci * Almost all knowlegde used while writing this driver was gathered by: 178c2ecf20Sopenharmony_ci * - analyzing traffic between device and the M$ Windows 2000 driver, 188c2ecf20Sopenharmony_ci * - trying different bit combinations and checking pin states 198c2ecf20Sopenharmony_ci * with a voltmeter, 208c2ecf20Sopenharmony_ci * - receiving malformed frames and producing buffer overflows 218c2ecf20Sopenharmony_ci * to learn how errors are reported, 228c2ecf20Sopenharmony_ci * So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE 238c2ecf20Sopenharmony_ci * CONNECTED TO IT! 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this 268c2ecf20Sopenharmony_ci * driver 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * TODO: 298c2ecf20Sopenharmony_ci * - implement correct flushing for ioctls and oti6858_close() 308c2ecf20Sopenharmony_ci * - check how errors (rx overflow, parity error, framing error) are reported 318c2ecf20Sopenharmony_ci * - implement oti6858_break_ctl() 328c2ecf20Sopenharmony_ci * - implement more ioctls 338c2ecf20Sopenharmony_ci * - test/implement flow control 348c2ecf20Sopenharmony_ci * - allow setting custom baud rates 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/kernel.h> 388c2ecf20Sopenharmony_ci#include <linux/errno.h> 398c2ecf20Sopenharmony_ci#include <linux/slab.h> 408c2ecf20Sopenharmony_ci#include <linux/tty.h> 418c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 428c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 438c2ecf20Sopenharmony_ci#include <linux/serial.h> 448c2ecf20Sopenharmony_ci#include <linux/module.h> 458c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 468c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 478c2ecf20Sopenharmony_ci#include <linux/usb.h> 488c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 498c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 508c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 518c2ecf20Sopenharmony_ci#include "oti6858.h" 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define OTI6858_DESCRIPTION \ 548c2ecf20Sopenharmony_ci "Ours Technology Inc. OTi-6858 USB to serial adapter driver" 558c2ecf20Sopenharmony_ci#define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>" 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 588c2ecf20Sopenharmony_ci { USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) }, 598c2ecf20Sopenharmony_ci { } 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* requests */ 658c2ecf20Sopenharmony_ci#define OTI6858_REQ_GET_STATUS (USB_DIR_IN | USB_TYPE_VENDOR | 0x00) 668c2ecf20Sopenharmony_ci#define OTI6858_REQ_T_GET_STATUS 0x01 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define OTI6858_REQ_SET_LINE (USB_DIR_OUT | USB_TYPE_VENDOR | 0x00) 698c2ecf20Sopenharmony_ci#define OTI6858_REQ_T_SET_LINE 0x00 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define OTI6858_REQ_CHECK_TXBUFF (USB_DIR_IN | USB_TYPE_VENDOR | 0x01) 728c2ecf20Sopenharmony_ci#define OTI6858_REQ_T_CHECK_TXBUFF 0x00 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* format of the control packet */ 758c2ecf20Sopenharmony_cistruct oti6858_control_pkt { 768c2ecf20Sopenharmony_ci __le16 divisor; /* baud rate = 96000000 / (16 * divisor), LE */ 778c2ecf20Sopenharmony_ci#define OTI6858_MAX_BAUD_RATE 3000000 788c2ecf20Sopenharmony_ci u8 frame_fmt; 798c2ecf20Sopenharmony_ci#define FMT_STOP_BITS_MASK 0xc0 808c2ecf20Sopenharmony_ci#define FMT_STOP_BITS_1 0x00 818c2ecf20Sopenharmony_ci#define FMT_STOP_BITS_2 0x40 /* 1.5 stop bits if FMT_DATA_BITS_5 */ 828c2ecf20Sopenharmony_ci#define FMT_PARITY_MASK 0x38 838c2ecf20Sopenharmony_ci#define FMT_PARITY_NONE 0x00 848c2ecf20Sopenharmony_ci#define FMT_PARITY_ODD 0x08 858c2ecf20Sopenharmony_ci#define FMT_PARITY_EVEN 0x18 868c2ecf20Sopenharmony_ci#define FMT_PARITY_MARK 0x28 878c2ecf20Sopenharmony_ci#define FMT_PARITY_SPACE 0x38 888c2ecf20Sopenharmony_ci#define FMT_DATA_BITS_MASK 0x03 898c2ecf20Sopenharmony_ci#define FMT_DATA_BITS_5 0x00 908c2ecf20Sopenharmony_ci#define FMT_DATA_BITS_6 0x01 918c2ecf20Sopenharmony_ci#define FMT_DATA_BITS_7 0x02 928c2ecf20Sopenharmony_ci#define FMT_DATA_BITS_8 0x03 938c2ecf20Sopenharmony_ci u8 something; /* always equals 0x43 */ 948c2ecf20Sopenharmony_ci u8 control; /* settings of flow control lines */ 958c2ecf20Sopenharmony_ci#define CONTROL_MASK 0x0c 968c2ecf20Sopenharmony_ci#define CONTROL_DTR_HIGH 0x08 978c2ecf20Sopenharmony_ci#define CONTROL_RTS_HIGH 0x04 988c2ecf20Sopenharmony_ci u8 tx_status; 998c2ecf20Sopenharmony_ci#define TX_BUFFER_EMPTIED 0x09 1008c2ecf20Sopenharmony_ci u8 pin_state; 1018c2ecf20Sopenharmony_ci#define PIN_MASK 0x3f 1028c2ecf20Sopenharmony_ci#define PIN_MSR_MASK 0x1b 1038c2ecf20Sopenharmony_ci#define PIN_RTS 0x20 /* output pin */ 1048c2ecf20Sopenharmony_ci#define PIN_CTS 0x10 /* input pin, active low */ 1058c2ecf20Sopenharmony_ci#define PIN_DSR 0x08 /* input pin, active low */ 1068c2ecf20Sopenharmony_ci#define PIN_DTR 0x04 /* output pin */ 1078c2ecf20Sopenharmony_ci#define PIN_RI 0x02 /* input pin, active low */ 1088c2ecf20Sopenharmony_ci#define PIN_DCD 0x01 /* input pin, active low */ 1098c2ecf20Sopenharmony_ci u8 rx_bytes_avail; /* number of bytes in rx buffer */; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt) 1138c2ecf20Sopenharmony_ci#define OTI6858_CTRL_EQUALS_PENDING(a, priv) \ 1148c2ecf20Sopenharmony_ci (((a)->divisor == (priv)->pending_setup.divisor) \ 1158c2ecf20Sopenharmony_ci && ((a)->control == (priv)->pending_setup.control) \ 1168c2ecf20Sopenharmony_ci && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt)) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* function prototypes */ 1198c2ecf20Sopenharmony_cistatic int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port); 1208c2ecf20Sopenharmony_cistatic void oti6858_close(struct usb_serial_port *port); 1218c2ecf20Sopenharmony_cistatic void oti6858_set_termios(struct tty_struct *tty, 1228c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old); 1238c2ecf20Sopenharmony_cistatic void oti6858_init_termios(struct tty_struct *tty); 1248c2ecf20Sopenharmony_cistatic void oti6858_read_int_callback(struct urb *urb); 1258c2ecf20Sopenharmony_cistatic void oti6858_read_bulk_callback(struct urb *urb); 1268c2ecf20Sopenharmony_cistatic void oti6858_write_bulk_callback(struct urb *urb); 1278c2ecf20Sopenharmony_cistatic int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, 1288c2ecf20Sopenharmony_ci const unsigned char *buf, int count); 1298c2ecf20Sopenharmony_cistatic int oti6858_write_room(struct tty_struct *tty); 1308c2ecf20Sopenharmony_cistatic int oti6858_chars_in_buffer(struct tty_struct *tty); 1318c2ecf20Sopenharmony_cistatic int oti6858_tiocmget(struct tty_struct *tty); 1328c2ecf20Sopenharmony_cistatic int oti6858_tiocmset(struct tty_struct *tty, 1338c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear); 1348c2ecf20Sopenharmony_cistatic int oti6858_port_probe(struct usb_serial_port *port); 1358c2ecf20Sopenharmony_cistatic int oti6858_port_remove(struct usb_serial_port *port); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* device info */ 1388c2ecf20Sopenharmony_cistatic struct usb_serial_driver oti6858_device = { 1398c2ecf20Sopenharmony_ci .driver = { 1408c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1418c2ecf20Sopenharmony_ci .name = "oti6858", 1428c2ecf20Sopenharmony_ci }, 1438c2ecf20Sopenharmony_ci .id_table = id_table, 1448c2ecf20Sopenharmony_ci .num_ports = 1, 1458c2ecf20Sopenharmony_ci .num_bulk_in = 1, 1468c2ecf20Sopenharmony_ci .num_bulk_out = 1, 1478c2ecf20Sopenharmony_ci .num_interrupt_in = 1, 1488c2ecf20Sopenharmony_ci .open = oti6858_open, 1498c2ecf20Sopenharmony_ci .close = oti6858_close, 1508c2ecf20Sopenharmony_ci .write = oti6858_write, 1518c2ecf20Sopenharmony_ci .set_termios = oti6858_set_termios, 1528c2ecf20Sopenharmony_ci .init_termios = oti6858_init_termios, 1538c2ecf20Sopenharmony_ci .tiocmget = oti6858_tiocmget, 1548c2ecf20Sopenharmony_ci .tiocmset = oti6858_tiocmset, 1558c2ecf20Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 1568c2ecf20Sopenharmony_ci .read_bulk_callback = oti6858_read_bulk_callback, 1578c2ecf20Sopenharmony_ci .read_int_callback = oti6858_read_int_callback, 1588c2ecf20Sopenharmony_ci .write_bulk_callback = oti6858_write_bulk_callback, 1598c2ecf20Sopenharmony_ci .write_room = oti6858_write_room, 1608c2ecf20Sopenharmony_ci .chars_in_buffer = oti6858_chars_in_buffer, 1618c2ecf20Sopenharmony_ci .port_probe = oti6858_port_probe, 1628c2ecf20Sopenharmony_ci .port_remove = oti6858_port_remove, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 1668c2ecf20Sopenharmony_ci &oti6858_device, NULL 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistruct oti6858_private { 1708c2ecf20Sopenharmony_ci spinlock_t lock; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci struct oti6858_control_pkt status; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci struct { 1758c2ecf20Sopenharmony_ci u8 read_urb_in_use; 1768c2ecf20Sopenharmony_ci u8 write_urb_in_use; 1778c2ecf20Sopenharmony_ci } flags; 1788c2ecf20Sopenharmony_ci struct delayed_work delayed_write_work; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci struct { 1818c2ecf20Sopenharmony_ci __le16 divisor; 1828c2ecf20Sopenharmony_ci u8 frame_fmt; 1838c2ecf20Sopenharmony_ci u8 control; 1848c2ecf20Sopenharmony_ci } pending_setup; 1858c2ecf20Sopenharmony_ci u8 transient; 1868c2ecf20Sopenharmony_ci u8 setup_done; 1878c2ecf20Sopenharmony_ci struct delayed_work delayed_setup_work; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci struct usb_serial_port *port; /* USB port with which associated */ 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void setup_line(struct work_struct *work) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct oti6858_private *priv = container_of(work, 1958c2ecf20Sopenharmony_ci struct oti6858_private, delayed_setup_work.work); 1968c2ecf20Sopenharmony_ci struct usb_serial_port *port = priv->port; 1978c2ecf20Sopenharmony_ci struct oti6858_control_pkt *new_setup; 1988c2ecf20Sopenharmony_ci unsigned long flags; 1998c2ecf20Sopenharmony_ci int result; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL); 2028c2ecf20Sopenharmony_ci if (!new_setup) { 2038c2ecf20Sopenharmony_ci /* we will try again */ 2048c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->delayed_setup_work, 2058c2ecf20Sopenharmony_ci msecs_to_jiffies(2)); 2068c2ecf20Sopenharmony_ci return; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 2108c2ecf20Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 2118c2ecf20Sopenharmony_ci OTI6858_REQ_T_GET_STATUS, 2128c2ecf20Sopenharmony_ci OTI6858_REQ_GET_STATUS, 2138c2ecf20Sopenharmony_ci 0, 0, 2148c2ecf20Sopenharmony_ci new_setup, OTI6858_CTRL_PKT_SIZE, 2158c2ecf20Sopenharmony_ci 100); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (result != OTI6858_CTRL_PKT_SIZE) { 2188c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s(): error reading status\n", __func__); 2198c2ecf20Sopenharmony_ci kfree(new_setup); 2208c2ecf20Sopenharmony_ci /* we will try again */ 2218c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->delayed_setup_work, 2228c2ecf20Sopenharmony_ci msecs_to_jiffies(2)); 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2278c2ecf20Sopenharmony_ci if (!OTI6858_CTRL_EQUALS_PENDING(new_setup, priv)) { 2288c2ecf20Sopenharmony_ci new_setup->divisor = priv->pending_setup.divisor; 2298c2ecf20Sopenharmony_ci new_setup->control = priv->pending_setup.control; 2308c2ecf20Sopenharmony_ci new_setup->frame_fmt = priv->pending_setup.frame_fmt; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2338c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 2348c2ecf20Sopenharmony_ci usb_sndctrlpipe(port->serial->dev, 0), 2358c2ecf20Sopenharmony_ci OTI6858_REQ_T_SET_LINE, 2368c2ecf20Sopenharmony_ci OTI6858_REQ_SET_LINE, 2378c2ecf20Sopenharmony_ci 0, 0, 2388c2ecf20Sopenharmony_ci new_setup, OTI6858_CTRL_PKT_SIZE, 2398c2ecf20Sopenharmony_ci 100); 2408c2ecf20Sopenharmony_ci } else { 2418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2428c2ecf20Sopenharmony_ci result = 0; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci kfree(new_setup); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2478c2ecf20Sopenharmony_ci if (result != OTI6858_CTRL_PKT_SIZE) 2488c2ecf20Sopenharmony_ci priv->transient = 0; 2498c2ecf20Sopenharmony_ci priv->setup_done = 1; 2508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__); 2538c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 2548c2ecf20Sopenharmony_ci if (result != 0) { 2558c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n", 2568c2ecf20Sopenharmony_ci __func__, result); 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void send_data(struct work_struct *work) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct oti6858_private *priv = container_of(work, 2638c2ecf20Sopenharmony_ci struct oti6858_private, delayed_write_work.work); 2648c2ecf20Sopenharmony_ci struct usb_serial_port *port = priv->port; 2658c2ecf20Sopenharmony_ci int count = 0, result; 2668c2ecf20Sopenharmony_ci unsigned long flags; 2678c2ecf20Sopenharmony_ci u8 *allow; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 2708c2ecf20Sopenharmony_ci if (priv->flags.write_urb_in_use) { 2718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2728c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->delayed_write_work, 2738c2ecf20Sopenharmony_ci msecs_to_jiffies(2)); 2748c2ecf20Sopenharmony_ci return; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci priv->flags.write_urb_in_use = 1; 2778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 2808c2ecf20Sopenharmony_ci count = kfifo_len(&port->write_fifo); 2818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (count > port->bulk_out_size) 2848c2ecf20Sopenharmony_ci count = port->bulk_out_size; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (count != 0) { 2878c2ecf20Sopenharmony_ci allow = kmalloc(1, GFP_KERNEL); 2888c2ecf20Sopenharmony_ci if (!allow) 2898c2ecf20Sopenharmony_ci return; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci result = usb_control_msg(port->serial->dev, 2928c2ecf20Sopenharmony_ci usb_rcvctrlpipe(port->serial->dev, 0), 2938c2ecf20Sopenharmony_ci OTI6858_REQ_T_CHECK_TXBUFF, 2948c2ecf20Sopenharmony_ci OTI6858_REQ_CHECK_TXBUFF, 2958c2ecf20Sopenharmony_ci count, 0, allow, 1, 100); 2968c2ecf20Sopenharmony_ci if (result != 1 || *allow != 0) 2978c2ecf20Sopenharmony_ci count = 0; 2988c2ecf20Sopenharmony_ci kfree(allow); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (count == 0) { 3028c2ecf20Sopenharmony_ci priv->flags.write_urb_in_use = 0; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__); 3058c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); 3068c2ecf20Sopenharmony_ci if (result != 0) { 3078c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n", 3088c2ecf20Sopenharmony_ci __func__, result); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci return; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci count = kfifo_out_locked(&port->write_fifo, 3148c2ecf20Sopenharmony_ci port->write_urb->transfer_buffer, 3158c2ecf20Sopenharmony_ci count, &port->lock); 3168c2ecf20Sopenharmony_ci port->write_urb->transfer_buffer_length = count; 3178c2ecf20Sopenharmony_ci result = usb_submit_urb(port->write_urb, GFP_NOIO); 3188c2ecf20Sopenharmony_ci if (result != 0) { 3198c2ecf20Sopenharmony_ci dev_err_console(port, "%s(): usb_submit_urb() failed with error %d\n", 3208c2ecf20Sopenharmony_ci __func__, result); 3218c2ecf20Sopenharmony_ci priv->flags.write_urb_in_use = 0; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci usb_serial_port_softint(port); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int oti6858_port_probe(struct usb_serial_port *port) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct oti6858_private *priv; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 3328c2ecf20Sopenharmony_ci if (!priv) 3338c2ecf20Sopenharmony_ci return -ENOMEM; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 3368c2ecf20Sopenharmony_ci priv->port = port; 3378c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line); 3388c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&priv->delayed_write_work, send_data); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci usb_set_serial_port_data(port, priv); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci port->port.drain_delay = 256; /* FIXME: check the FIFO length */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int oti6858_port_remove(struct usb_serial_port *port) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct oti6858_private *priv; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci priv = usb_get_serial_port_data(port); 3528c2ecf20Sopenharmony_ci kfree(priv); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, 3588c2ecf20Sopenharmony_ci const unsigned char *buf, int count) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci if (!count) 3618c2ecf20Sopenharmony_ci return count; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return count; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int oti6858_write_room(struct tty_struct *tty) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 3718c2ecf20Sopenharmony_ci int room = 0; 3728c2ecf20Sopenharmony_ci unsigned long flags; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 3758c2ecf20Sopenharmony_ci room = kfifo_avail(&port->write_fifo); 3768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return room; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int oti6858_chars_in_buffer(struct tty_struct *tty) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 3848c2ecf20Sopenharmony_ci int chars = 0; 3858c2ecf20Sopenharmony_ci unsigned long flags; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 3888c2ecf20Sopenharmony_ci chars = kfifo_len(&port->write_fifo); 3898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return chars; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic void oti6858_init_termios(struct tty_struct *tty) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci tty_encode_baud_rate(tty, 38400, 38400); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void oti6858_set_termios(struct tty_struct *tty, 4008c2ecf20Sopenharmony_ci struct usb_serial_port *port, struct ktermios *old_termios) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 4038c2ecf20Sopenharmony_ci unsigned long flags; 4048c2ecf20Sopenharmony_ci unsigned int cflag; 4058c2ecf20Sopenharmony_ci u8 frame_fmt, control; 4068c2ecf20Sopenharmony_ci __le16 divisor; 4078c2ecf20Sopenharmony_ci int br; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci cflag = tty->termios.c_cflag; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 4128c2ecf20Sopenharmony_ci divisor = priv->pending_setup.divisor; 4138c2ecf20Sopenharmony_ci frame_fmt = priv->pending_setup.frame_fmt; 4148c2ecf20Sopenharmony_ci control = priv->pending_setup.control; 4158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci frame_fmt &= ~FMT_DATA_BITS_MASK; 4188c2ecf20Sopenharmony_ci switch (cflag & CSIZE) { 4198c2ecf20Sopenharmony_ci case CS5: 4208c2ecf20Sopenharmony_ci frame_fmt |= FMT_DATA_BITS_5; 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case CS6: 4238c2ecf20Sopenharmony_ci frame_fmt |= FMT_DATA_BITS_6; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case CS7: 4268c2ecf20Sopenharmony_ci frame_fmt |= FMT_DATA_BITS_7; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci default: 4298c2ecf20Sopenharmony_ci case CS8: 4308c2ecf20Sopenharmony_ci frame_fmt |= FMT_DATA_BITS_8; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* manufacturer claims that this device can work with baud rates 4358c2ecf20Sopenharmony_ci * up to 3 Mbps; I've tested it only on 115200 bps, so I can't 4368c2ecf20Sopenharmony_ci * guarantee that any other baud rate will work (especially 4378c2ecf20Sopenharmony_ci * the higher ones) 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci br = tty_get_baud_rate(tty); 4408c2ecf20Sopenharmony_ci if (br == 0) { 4418c2ecf20Sopenharmony_ci divisor = 0; 4428c2ecf20Sopenharmony_ci } else { 4438c2ecf20Sopenharmony_ci int real_br; 4448c2ecf20Sopenharmony_ci int new_divisor; 4458c2ecf20Sopenharmony_ci br = min(br, OTI6858_MAX_BAUD_RATE); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci new_divisor = (96000000 + 8 * br) / (16 * br); 4488c2ecf20Sopenharmony_ci real_br = 96000000 / (16 * new_divisor); 4498c2ecf20Sopenharmony_ci divisor = cpu_to_le16(new_divisor); 4508c2ecf20Sopenharmony_ci tty_encode_baud_rate(tty, real_br, real_br); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci frame_fmt &= ~FMT_STOP_BITS_MASK; 4548c2ecf20Sopenharmony_ci if ((cflag & CSTOPB) != 0) 4558c2ecf20Sopenharmony_ci frame_fmt |= FMT_STOP_BITS_2; 4568c2ecf20Sopenharmony_ci else 4578c2ecf20Sopenharmony_ci frame_fmt |= FMT_STOP_BITS_1; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci frame_fmt &= ~FMT_PARITY_MASK; 4608c2ecf20Sopenharmony_ci if ((cflag & PARENB) != 0) { 4618c2ecf20Sopenharmony_ci if ((cflag & PARODD) != 0) 4628c2ecf20Sopenharmony_ci frame_fmt |= FMT_PARITY_ODD; 4638c2ecf20Sopenharmony_ci else 4648c2ecf20Sopenharmony_ci frame_fmt |= FMT_PARITY_EVEN; 4658c2ecf20Sopenharmony_ci } else { 4668c2ecf20Sopenharmony_ci frame_fmt |= FMT_PARITY_NONE; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci control &= ~CONTROL_MASK; 4708c2ecf20Sopenharmony_ci if ((cflag & CRTSCTS) != 0) 4718c2ecf20Sopenharmony_ci control |= (CONTROL_DTR_HIGH | CONTROL_RTS_HIGH); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* change control lines if we are switching to or from B0 */ 4748c2ecf20Sopenharmony_ci /* FIXME: 4758c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 4768c2ecf20Sopenharmony_ci control = priv->line_control; 4778c2ecf20Sopenharmony_ci if ((cflag & CBAUD) == B0) 4788c2ecf20Sopenharmony_ci priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); 4798c2ecf20Sopenharmony_ci else 4808c2ecf20Sopenharmony_ci priv->line_control |= (CONTROL_DTR | CONTROL_RTS); 4818c2ecf20Sopenharmony_ci if (control != priv->line_control) { 4828c2ecf20Sopenharmony_ci control = priv->line_control; 4838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 4848c2ecf20Sopenharmony_ci set_control_lines(serial->dev, control); 4858c2ecf20Sopenharmony_ci } else { 4868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 4918c2ecf20Sopenharmony_ci if (divisor != priv->pending_setup.divisor 4928c2ecf20Sopenharmony_ci || control != priv->pending_setup.control 4938c2ecf20Sopenharmony_ci || frame_fmt != priv->pending_setup.frame_fmt) { 4948c2ecf20Sopenharmony_ci priv->pending_setup.divisor = divisor; 4958c2ecf20Sopenharmony_ci priv->pending_setup.control = control; 4968c2ecf20Sopenharmony_ci priv->pending_setup.frame_fmt = frame_fmt; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 5048c2ecf20Sopenharmony_ci struct usb_serial *serial = port->serial; 5058c2ecf20Sopenharmony_ci struct oti6858_control_pkt *buf; 5068c2ecf20Sopenharmony_ci unsigned long flags; 5078c2ecf20Sopenharmony_ci int result; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, port->write_urb->pipe); 5108c2ecf20Sopenharmony_ci usb_clear_halt(serial->dev, port->read_urb->pipe); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL); 5138c2ecf20Sopenharmony_ci if (!buf) 5148c2ecf20Sopenharmony_ci return -ENOMEM; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), 5178c2ecf20Sopenharmony_ci OTI6858_REQ_T_GET_STATUS, 5188c2ecf20Sopenharmony_ci OTI6858_REQ_GET_STATUS, 5198c2ecf20Sopenharmony_ci 0, 0, 5208c2ecf20Sopenharmony_ci buf, OTI6858_CTRL_PKT_SIZE, 5218c2ecf20Sopenharmony_ci 100); 5228c2ecf20Sopenharmony_ci if (result != OTI6858_CTRL_PKT_SIZE) { 5238c2ecf20Sopenharmony_ci /* assume default (after power-on reset) values */ 5248c2ecf20Sopenharmony_ci buf->divisor = cpu_to_le16(0x009c); /* 38400 bps */ 5258c2ecf20Sopenharmony_ci buf->frame_fmt = 0x03; /* 8N1 */ 5268c2ecf20Sopenharmony_ci buf->something = 0x43; 5278c2ecf20Sopenharmony_ci buf->control = 0x4c; /* DTR, RTS */ 5288c2ecf20Sopenharmony_ci buf->tx_status = 0x00; 5298c2ecf20Sopenharmony_ci buf->pin_state = 0x5b; /* RTS, CTS, DSR, DTR, RI, DCD */ 5308c2ecf20Sopenharmony_ci buf->rx_bytes_avail = 0x00; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 5348c2ecf20Sopenharmony_ci memcpy(&priv->status, buf, OTI6858_CTRL_PKT_SIZE); 5358c2ecf20Sopenharmony_ci priv->pending_setup.divisor = buf->divisor; 5368c2ecf20Sopenharmony_ci priv->pending_setup.frame_fmt = buf->frame_fmt; 5378c2ecf20Sopenharmony_ci priv->pending_setup.control = buf->control; 5388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 5398c2ecf20Sopenharmony_ci kfree(buf); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__); 5428c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 5438c2ecf20Sopenharmony_ci if (result != 0) { 5448c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n", 5458c2ecf20Sopenharmony_ci __func__, result); 5468c2ecf20Sopenharmony_ci oti6858_close(port); 5478c2ecf20Sopenharmony_ci return result; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* setup termios */ 5518c2ecf20Sopenharmony_ci if (tty) 5528c2ecf20Sopenharmony_ci oti6858_set_termios(tty, port, NULL); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic void oti6858_close(struct usb_serial_port *port) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 5608c2ecf20Sopenharmony_ci unsigned long flags; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 5638c2ecf20Sopenharmony_ci /* clear out any remaining data in the buffer */ 5648c2ecf20Sopenharmony_ci kfifo_reset_out(&port->write_fifo); 5658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): after buf_clear()\n", __func__); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* cancel scheduled setup */ 5708c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->delayed_setup_work); 5718c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->delayed_write_work); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* shutdown our urbs */ 5748c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): shutting down urbs\n", __func__); 5758c2ecf20Sopenharmony_ci usb_kill_urb(port->write_urb); 5768c2ecf20Sopenharmony_ci usb_kill_urb(port->read_urb); 5778c2ecf20Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int oti6858_tiocmset(struct tty_struct *tty, 5818c2ecf20Sopenharmony_ci unsigned int set, unsigned int clear) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 5848c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 5858c2ecf20Sopenharmony_ci unsigned long flags; 5868c2ecf20Sopenharmony_ci u8 control; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(set = 0x%08x, clear = 0x%08x)\n", 5898c2ecf20Sopenharmony_ci __func__, set, clear); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* FIXME: check if this is correct (active high/low) */ 5928c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 5938c2ecf20Sopenharmony_ci control = priv->pending_setup.control; 5948c2ecf20Sopenharmony_ci if ((set & TIOCM_RTS) != 0) 5958c2ecf20Sopenharmony_ci control |= CONTROL_RTS_HIGH; 5968c2ecf20Sopenharmony_ci if ((set & TIOCM_DTR) != 0) 5978c2ecf20Sopenharmony_ci control |= CONTROL_DTR_HIGH; 5988c2ecf20Sopenharmony_ci if ((clear & TIOCM_RTS) != 0) 5998c2ecf20Sopenharmony_ci control &= ~CONTROL_RTS_HIGH; 6008c2ecf20Sopenharmony_ci if ((clear & TIOCM_DTR) != 0) 6018c2ecf20Sopenharmony_ci control &= ~CONTROL_DTR_HIGH; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (control != priv->pending_setup.control) 6048c2ecf20Sopenharmony_ci priv->pending_setup.control = control; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int oti6858_tiocmget(struct tty_struct *tty) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 6138c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 6148c2ecf20Sopenharmony_ci unsigned long flags; 6158c2ecf20Sopenharmony_ci unsigned pin_state; 6168c2ecf20Sopenharmony_ci unsigned result = 0; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 6198c2ecf20Sopenharmony_ci pin_state = priv->status.pin_state & PIN_MASK; 6208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* FIXME: check if this is correct (active high/low) */ 6238c2ecf20Sopenharmony_ci if ((pin_state & PIN_RTS) != 0) 6248c2ecf20Sopenharmony_ci result |= TIOCM_RTS; 6258c2ecf20Sopenharmony_ci if ((pin_state & PIN_CTS) != 0) 6268c2ecf20Sopenharmony_ci result |= TIOCM_CTS; 6278c2ecf20Sopenharmony_ci if ((pin_state & PIN_DSR) != 0) 6288c2ecf20Sopenharmony_ci result |= TIOCM_DSR; 6298c2ecf20Sopenharmony_ci if ((pin_state & PIN_DTR) != 0) 6308c2ecf20Sopenharmony_ci result |= TIOCM_DTR; 6318c2ecf20Sopenharmony_ci if ((pin_state & PIN_RI) != 0) 6328c2ecf20Sopenharmony_ci result |= TIOCM_RI; 6338c2ecf20Sopenharmony_ci if ((pin_state & PIN_DCD) != 0) 6348c2ecf20Sopenharmony_ci result |= TIOCM_CD; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s() = 0x%08x\n", __func__, result); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return result; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic void oti6858_read_int_callback(struct urb *urb) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 6448c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 6458c2ecf20Sopenharmony_ci int transient = 0, can_recv = 0, resubmit = 1; 6468c2ecf20Sopenharmony_ci int status = urb->status; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci switch (status) { 6498c2ecf20Sopenharmony_ci case 0: 6508c2ecf20Sopenharmony_ci /* success */ 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci case -ECONNRESET: 6538c2ecf20Sopenharmony_ci case -ENOENT: 6548c2ecf20Sopenharmony_ci case -ESHUTDOWN: 6558c2ecf20Sopenharmony_ci /* this urb is terminated, clean up */ 6568c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n", 6578c2ecf20Sopenharmony_ci __func__, status); 6588c2ecf20Sopenharmony_ci return; 6598c2ecf20Sopenharmony_ci default: 6608c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s(): nonzero urb status received: %d\n", 6618c2ecf20Sopenharmony_ci __func__, status); 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (status == 0 && urb->actual_length == OTI6858_CTRL_PKT_SIZE) { 6668c2ecf20Sopenharmony_ci struct oti6858_control_pkt *xs = urb->transfer_buffer; 6678c2ecf20Sopenharmony_ci unsigned long flags; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (!priv->transient) { 6728c2ecf20Sopenharmony_ci if (!OTI6858_CTRL_EQUALS_PENDING(xs, priv)) { 6738c2ecf20Sopenharmony_ci if (xs->rx_bytes_avail == 0) { 6748c2ecf20Sopenharmony_ci priv->transient = 4; 6758c2ecf20Sopenharmony_ci priv->setup_done = 0; 6768c2ecf20Sopenharmony_ci resubmit = 0; 6778c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__); 6788c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->delayed_setup_work, 0); 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci } else { 6828c2ecf20Sopenharmony_ci if (OTI6858_CTRL_EQUALS_PENDING(xs, priv)) { 6838c2ecf20Sopenharmony_ci priv->transient = 0; 6848c2ecf20Sopenharmony_ci } else if (!priv->setup_done) { 6858c2ecf20Sopenharmony_ci resubmit = 0; 6868c2ecf20Sopenharmony_ci } else if (--priv->transient == 0) { 6878c2ecf20Sopenharmony_ci if (xs->rx_bytes_avail == 0) { 6888c2ecf20Sopenharmony_ci priv->transient = 4; 6898c2ecf20Sopenharmony_ci priv->setup_done = 0; 6908c2ecf20Sopenharmony_ci resubmit = 0; 6918c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__); 6928c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->delayed_setup_work, 0); 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (!priv->transient) { 6988c2ecf20Sopenharmony_ci u8 delta = xs->pin_state ^ priv->status.pin_state; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (delta & PIN_MSR_MASK) { 7018c2ecf20Sopenharmony_ci if (delta & PIN_CTS) 7028c2ecf20Sopenharmony_ci port->icount.cts++; 7038c2ecf20Sopenharmony_ci if (delta & PIN_DSR) 7048c2ecf20Sopenharmony_ci port->icount.dsr++; 7058c2ecf20Sopenharmony_ci if (delta & PIN_RI) 7068c2ecf20Sopenharmony_ci port->icount.rng++; 7078c2ecf20Sopenharmony_ci if (delta & PIN_DCD) 7088c2ecf20Sopenharmony_ci port->icount.dcd++; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci wake_up_interruptible(&port->port.delta_msr_wait); 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE); 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (!priv->transient && xs->rx_bytes_avail != 0) { 7178c2ecf20Sopenharmony_ci can_recv = xs->rx_bytes_avail; 7188c2ecf20Sopenharmony_ci priv->flags.read_urb_in_use = 1; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci transient = priv->transient; 7228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (can_recv) { 7268c2ecf20Sopenharmony_ci int result; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci result = usb_submit_urb(port->read_urb, GFP_ATOMIC); 7298c2ecf20Sopenharmony_ci if (result != 0) { 7308c2ecf20Sopenharmony_ci priv->flags.read_urb_in_use = 0; 7318c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s(): usb_submit_urb() failed," 7328c2ecf20Sopenharmony_ci " error %d\n", __func__, result); 7338c2ecf20Sopenharmony_ci } else { 7348c2ecf20Sopenharmony_ci resubmit = 0; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } else if (!transient) { 7378c2ecf20Sopenharmony_ci unsigned long flags; 7388c2ecf20Sopenharmony_ci int count; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 7418c2ecf20Sopenharmony_ci count = kfifo_len(&port->write_fifo); 7428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 7458c2ecf20Sopenharmony_ci if (priv->flags.write_urb_in_use == 0 && count != 0) { 7468c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->delayed_write_work, 0); 7478c2ecf20Sopenharmony_ci resubmit = 0; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (resubmit) { 7538c2ecf20Sopenharmony_ci int result; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/* dev_dbg(&urb->dev->dev, "%s(): submitting interrupt urb\n", __func__); */ 7568c2ecf20Sopenharmony_ci result = usb_submit_urb(urb, GFP_ATOMIC); 7578c2ecf20Sopenharmony_ci if (result != 0) { 7588c2ecf20Sopenharmony_ci dev_err(&urb->dev->dev, 7598c2ecf20Sopenharmony_ci "%s(): usb_submit_urb() failed with" 7608c2ecf20Sopenharmony_ci " error %d\n", __func__, result); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic void oti6858_read_bulk_callback(struct urb *urb) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 7688c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 7698c2ecf20Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 7708c2ecf20Sopenharmony_ci unsigned long flags; 7718c2ecf20Sopenharmony_ci int status = urb->status; 7728c2ecf20Sopenharmony_ci int result; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 7758c2ecf20Sopenharmony_ci priv->flags.read_urb_in_use = 0; 7768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (status != 0) { 7798c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s(): unable to handle the error, exiting\n", __func__); 7808c2ecf20Sopenharmony_ci return; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (urb->actual_length > 0) { 7848c2ecf20Sopenharmony_ci tty_insert_flip_string(&port->port, data, urb->actual_length); 7858c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* schedule the interrupt urb */ 7898c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); 7908c2ecf20Sopenharmony_ci if (result != 0 && result != -EPERM) { 7918c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s(): usb_submit_urb() failed," 7928c2ecf20Sopenharmony_ci " error %d\n", __func__, result); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic void oti6858_write_bulk_callback(struct urb *urb) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 7998c2ecf20Sopenharmony_ci struct oti6858_private *priv = usb_get_serial_port_data(port); 8008c2ecf20Sopenharmony_ci int status = urb->status; 8018c2ecf20Sopenharmony_ci int result; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci switch (status) { 8048c2ecf20Sopenharmony_ci case 0: 8058c2ecf20Sopenharmony_ci /* success */ 8068c2ecf20Sopenharmony_ci break; 8078c2ecf20Sopenharmony_ci case -ECONNRESET: 8088c2ecf20Sopenharmony_ci case -ENOENT: 8098c2ecf20Sopenharmony_ci case -ESHUTDOWN: 8108c2ecf20Sopenharmony_ci /* this urb is terminated, clean up */ 8118c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n", __func__, status); 8128c2ecf20Sopenharmony_ci priv->flags.write_urb_in_use = 0; 8138c2ecf20Sopenharmony_ci return; 8148c2ecf20Sopenharmony_ci default: 8158c2ecf20Sopenharmony_ci /* error in the urb, so we have to resubmit it */ 8168c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s(): nonzero write bulk status received: %d\n", __func__, status); 8178c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s(): overflow in write\n", __func__); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci port->write_urb->transfer_buffer_length = 1; 8208c2ecf20Sopenharmony_ci result = usb_submit_urb(port->write_urb, GFP_ATOMIC); 8218c2ecf20Sopenharmony_ci if (result) { 8228c2ecf20Sopenharmony_ci dev_err_console(port, "%s(): usb_submit_urb() failed," 8238c2ecf20Sopenharmony_ci " error %d\n", __func__, result); 8248c2ecf20Sopenharmony_ci } else { 8258c2ecf20Sopenharmony_ci return; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci priv->flags.write_urb_in_use = 0; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* schedule the interrupt urb if we are still open */ 8328c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__); 8338c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); 8348c2ecf20Sopenharmony_ci if (result != 0) { 8358c2ecf20Sopenharmony_ci dev_err(&port->dev, "%s(): failed submitting int urb," 8368c2ecf20Sopenharmony_ci " error %d\n", __func__, result); 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(OTI6858_DESCRIPTION); 8438c2ecf20Sopenharmony_ciMODULE_AUTHOR(OTI6858_AUTHOR); 8448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 845