162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Belkin USB Serial Adapter Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000 William Greathouse (wgreathouse@smva.com) 662306a36Sopenharmony_ci * Copyright (C) 2000-2001 Greg Kroah-Hartman (greg@kroah.com) 762306a36Sopenharmony_ci * Copyright (C) 2010 Johan Hovold (jhovold@gmail.com) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is largely derived from work by the linux-usb group 1062306a36Sopenharmony_ci * and associated source files. Please see the usb/serial files for 1162306a36Sopenharmony_ci * individual credits and copyrights. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * See Documentation/usb/usb-serial.rst for more information on using this 1462306a36Sopenharmony_ci * driver 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * TODO: 1762306a36Sopenharmony_ci * -- Add true modem control line query capability. Currently we track the 1862306a36Sopenharmony_ci * states reported by the interrupt and the states we request. 1962306a36Sopenharmony_ci * -- Add support for flush commands 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/errno.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/tty.h> 2662306a36Sopenharmony_ci#include <linux/tty_driver.h> 2762306a36Sopenharmony_ci#include <linux/tty_flip.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/spinlock.h> 3062306a36Sopenharmony_ci#include <linux/uaccess.h> 3162306a36Sopenharmony_ci#include <linux/usb.h> 3262306a36Sopenharmony_ci#include <linux/usb/serial.h> 3362306a36Sopenharmony_ci#include "belkin_sa.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRIVER_AUTHOR "William Greathouse <wgreathouse@smva.com>" 3662306a36Sopenharmony_ci#define DRIVER_DESC "USB Belkin Serial converter driver" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* function prototypes for a Belkin USB Serial Adapter F5U103 */ 3962306a36Sopenharmony_cistatic int belkin_sa_port_probe(struct usb_serial_port *port); 4062306a36Sopenharmony_cistatic void belkin_sa_port_remove(struct usb_serial_port *port); 4162306a36Sopenharmony_cistatic int belkin_sa_open(struct tty_struct *tty, 4262306a36Sopenharmony_ci struct usb_serial_port *port); 4362306a36Sopenharmony_cistatic void belkin_sa_close(struct usb_serial_port *port); 4462306a36Sopenharmony_cistatic void belkin_sa_read_int_callback(struct urb *urb); 4562306a36Sopenharmony_cistatic void belkin_sa_process_read_urb(struct urb *urb); 4662306a36Sopenharmony_cistatic void belkin_sa_set_termios(struct tty_struct *tty, 4762306a36Sopenharmony_ci struct usb_serial_port *port, 4862306a36Sopenharmony_ci const struct ktermios *old_termios); 4962306a36Sopenharmony_cistatic int belkin_sa_break_ctl(struct tty_struct *tty, int break_state); 5062306a36Sopenharmony_cistatic int belkin_sa_tiocmget(struct tty_struct *tty); 5162306a36Sopenharmony_cistatic int belkin_sa_tiocmset(struct tty_struct *tty, 5262306a36Sopenharmony_ci unsigned int set, unsigned int clear); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const struct usb_device_id id_table[] = { 5662306a36Sopenharmony_ci { USB_DEVICE(BELKIN_SA_VID, BELKIN_SA_PID) }, 5762306a36Sopenharmony_ci { USB_DEVICE(BELKIN_OLD_VID, BELKIN_OLD_PID) }, 5862306a36Sopenharmony_ci { USB_DEVICE(PERACOM_VID, PERACOM_PID) }, 5962306a36Sopenharmony_ci { USB_DEVICE(GOHUBS_VID, GOHUBS_PID) }, 6062306a36Sopenharmony_ci { USB_DEVICE(GOHUBS_VID, HANDYLINK_PID) }, 6162306a36Sopenharmony_ci { USB_DEVICE(BELKIN_DOCKSTATION_VID, BELKIN_DOCKSTATION_PID) }, 6262306a36Sopenharmony_ci { } /* Terminating entry */ 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* All of the device info needed for the serial converters */ 6762306a36Sopenharmony_cistatic struct usb_serial_driver belkin_device = { 6862306a36Sopenharmony_ci .driver = { 6962306a36Sopenharmony_ci .owner = THIS_MODULE, 7062306a36Sopenharmony_ci .name = "belkin", 7162306a36Sopenharmony_ci }, 7262306a36Sopenharmony_ci .description = "Belkin / Peracom / GoHubs USB Serial Adapter", 7362306a36Sopenharmony_ci .id_table = id_table, 7462306a36Sopenharmony_ci .num_ports = 1, 7562306a36Sopenharmony_ci .open = belkin_sa_open, 7662306a36Sopenharmony_ci .close = belkin_sa_close, 7762306a36Sopenharmony_ci .read_int_callback = belkin_sa_read_int_callback, 7862306a36Sopenharmony_ci .process_read_urb = belkin_sa_process_read_urb, 7962306a36Sopenharmony_ci .set_termios = belkin_sa_set_termios, 8062306a36Sopenharmony_ci .break_ctl = belkin_sa_break_ctl, 8162306a36Sopenharmony_ci .tiocmget = belkin_sa_tiocmget, 8262306a36Sopenharmony_ci .tiocmset = belkin_sa_tiocmset, 8362306a36Sopenharmony_ci .port_probe = belkin_sa_port_probe, 8462306a36Sopenharmony_ci .port_remove = belkin_sa_port_remove, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 8862306a36Sopenharmony_ci &belkin_device, NULL 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct belkin_sa_private { 9262306a36Sopenharmony_ci spinlock_t lock; 9362306a36Sopenharmony_ci unsigned long control_state; 9462306a36Sopenharmony_ci unsigned char last_lsr; 9562306a36Sopenharmony_ci unsigned char last_msr; 9662306a36Sopenharmony_ci int bad_flow_control; 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * *************************************************************************** 10262306a36Sopenharmony_ci * Belkin USB Serial Adapter F5U103 specific driver functions 10362306a36Sopenharmony_ci * *************************************************************************** 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define WDR_TIMEOUT 5000 /* default urb timeout */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* assumes that struct usb_serial *serial is available */ 10962306a36Sopenharmony_ci#define BSA_USB_CMD(c, v) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), \ 11062306a36Sopenharmony_ci (c), BELKIN_SA_SET_REQUEST_TYPE, \ 11162306a36Sopenharmony_ci (v), 0, NULL, 0, WDR_TIMEOUT) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int belkin_sa_port_probe(struct usb_serial_port *port) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct usb_device *dev = port->serial->dev; 11662306a36Sopenharmony_ci struct belkin_sa_private *priv; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL); 11962306a36Sopenharmony_ci if (!priv) 12062306a36Sopenharmony_ci return -ENOMEM; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spin_lock_init(&priv->lock); 12362306a36Sopenharmony_ci priv->control_state = 0; 12462306a36Sopenharmony_ci priv->last_lsr = 0; 12562306a36Sopenharmony_ci priv->last_msr = 0; 12662306a36Sopenharmony_ci /* see comments at top of file */ 12762306a36Sopenharmony_ci priv->bad_flow_control = 12862306a36Sopenharmony_ci (le16_to_cpu(dev->descriptor.bcdDevice) <= 0x0206) ? 1 : 0; 12962306a36Sopenharmony_ci dev_info(&dev->dev, "bcdDevice: %04x, bfc: %d\n", 13062306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.bcdDevice), 13162306a36Sopenharmony_ci priv->bad_flow_control); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci usb_set_serial_port_data(port, priv); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void belkin_sa_port_remove(struct usb_serial_port *port) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct belkin_sa_private *priv; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 14362306a36Sopenharmony_ci kfree(priv); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int belkin_sa_open(struct tty_struct *tty, 14762306a36Sopenharmony_ci struct usb_serial_port *port) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int retval; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 15262306a36Sopenharmony_ci if (retval) { 15362306a36Sopenharmony_ci dev_err(&port->dev, "usb_submit_urb(read int) failed\n"); 15462306a36Sopenharmony_ci return retval; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci retval = usb_serial_generic_open(tty, port); 15862306a36Sopenharmony_ci if (retval) 15962306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return retval; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void belkin_sa_close(struct usb_serial_port *port) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci usb_serial_generic_close(port); 16762306a36Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void belkin_sa_read_int_callback(struct urb *urb) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 17362306a36Sopenharmony_ci struct belkin_sa_private *priv; 17462306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 17562306a36Sopenharmony_ci int retval; 17662306a36Sopenharmony_ci int status = urb->status; 17762306a36Sopenharmony_ci unsigned long flags; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci switch (status) { 18062306a36Sopenharmony_ci case 0: 18162306a36Sopenharmony_ci /* success */ 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case -ECONNRESET: 18462306a36Sopenharmony_ci case -ENOENT: 18562306a36Sopenharmony_ci case -ESHUTDOWN: 18662306a36Sopenharmony_ci /* this urb is terminated, clean up */ 18762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n", 18862306a36Sopenharmony_ci __func__, status); 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci default: 19162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n", 19262306a36Sopenharmony_ci __func__, status); 19362306a36Sopenharmony_ci goto exit; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Handle known interrupt data */ 19962306a36Sopenharmony_ci /* ignore data[0] and data[1] */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci priv = usb_get_serial_port_data(port); 20262306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 20362306a36Sopenharmony_ci priv->last_msr = data[BELKIN_SA_MSR_INDEX]; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Record Control Line states */ 20662306a36Sopenharmony_ci if (priv->last_msr & BELKIN_SA_MSR_DSR) 20762306a36Sopenharmony_ci priv->control_state |= TIOCM_DSR; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci priv->control_state &= ~TIOCM_DSR; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (priv->last_msr & BELKIN_SA_MSR_CTS) 21262306a36Sopenharmony_ci priv->control_state |= TIOCM_CTS; 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci priv->control_state &= ~TIOCM_CTS; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (priv->last_msr & BELKIN_SA_MSR_RI) 21762306a36Sopenharmony_ci priv->control_state |= TIOCM_RI; 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci priv->control_state &= ~TIOCM_RI; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (priv->last_msr & BELKIN_SA_MSR_CD) 22262306a36Sopenharmony_ci priv->control_state |= TIOCM_CD; 22362306a36Sopenharmony_ci else 22462306a36Sopenharmony_ci priv->control_state &= ~TIOCM_CD; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci priv->last_lsr = data[BELKIN_SA_LSR_INDEX]; 22762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 22862306a36Sopenharmony_ciexit: 22962306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 23062306a36Sopenharmony_ci if (retval) 23162306a36Sopenharmony_ci dev_err(&port->dev, "%s - usb_submit_urb failed with " 23262306a36Sopenharmony_ci "result %d\n", __func__, retval); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void belkin_sa_process_read_urb(struct urb *urb) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 23862306a36Sopenharmony_ci struct belkin_sa_private *priv = usb_get_serial_port_data(port); 23962306a36Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 24062306a36Sopenharmony_ci unsigned long flags; 24162306a36Sopenharmony_ci unsigned char status; 24262306a36Sopenharmony_ci char tty_flag; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Update line status */ 24562306a36Sopenharmony_ci tty_flag = TTY_NORMAL; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 24862306a36Sopenharmony_ci status = priv->last_lsr; 24962306a36Sopenharmony_ci priv->last_lsr &= ~BELKIN_SA_LSR_ERR; 25062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!urb->actual_length) 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (status & BELKIN_SA_LSR_ERR) { 25662306a36Sopenharmony_ci /* Break takes precedence over parity, which takes precedence 25762306a36Sopenharmony_ci * over framing errors. */ 25862306a36Sopenharmony_ci if (status & BELKIN_SA_LSR_BI) 25962306a36Sopenharmony_ci tty_flag = TTY_BREAK; 26062306a36Sopenharmony_ci else if (status & BELKIN_SA_LSR_PE) 26162306a36Sopenharmony_ci tty_flag = TTY_PARITY; 26262306a36Sopenharmony_ci else if (status & BELKIN_SA_LSR_FE) 26362306a36Sopenharmony_ci tty_flag = TTY_FRAME; 26462306a36Sopenharmony_ci dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Overrun is special, not associated with a char. */ 26762306a36Sopenharmony_ci if (status & BELKIN_SA_LSR_OE) 26862306a36Sopenharmony_ci tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci tty_insert_flip_string_fixed_flag(&port->port, data, tty_flag, 27262306a36Sopenharmony_ci urb->actual_length); 27362306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void belkin_sa_set_termios(struct tty_struct *tty, 27762306a36Sopenharmony_ci struct usb_serial_port *port, 27862306a36Sopenharmony_ci const struct ktermios *old_termios) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 28162306a36Sopenharmony_ci struct belkin_sa_private *priv = usb_get_serial_port_data(port); 28262306a36Sopenharmony_ci unsigned int iflag; 28362306a36Sopenharmony_ci unsigned int cflag; 28462306a36Sopenharmony_ci unsigned int old_iflag = 0; 28562306a36Sopenharmony_ci unsigned int old_cflag = 0; 28662306a36Sopenharmony_ci __u16 urb_value = 0; /* Will hold the new flags */ 28762306a36Sopenharmony_ci unsigned long flags; 28862306a36Sopenharmony_ci unsigned long control_state; 28962306a36Sopenharmony_ci int bad_flow_control; 29062306a36Sopenharmony_ci speed_t baud; 29162306a36Sopenharmony_ci struct ktermios *termios = &tty->termios; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci iflag = termios->c_iflag; 29462306a36Sopenharmony_ci cflag = termios->c_cflag; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci termios->c_cflag &= ~CMSPAR; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* get a local copy of the current port settings */ 29962306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 30062306a36Sopenharmony_ci control_state = priv->control_state; 30162306a36Sopenharmony_ci bad_flow_control = priv->bad_flow_control; 30262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci old_iflag = old_termios->c_iflag; 30562306a36Sopenharmony_ci old_cflag = old_termios->c_cflag; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Set the baud rate */ 30862306a36Sopenharmony_ci if ((cflag & CBAUD) != (old_cflag & CBAUD)) { 30962306a36Sopenharmony_ci /* reassert DTR and (maybe) RTS on transition from B0 */ 31062306a36Sopenharmony_ci if ((old_cflag & CBAUD) == B0) { 31162306a36Sopenharmony_ci control_state |= (TIOCM_DTR|TIOCM_RTS); 31262306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0) 31362306a36Sopenharmony_ci dev_err(&port->dev, "Set DTR error\n"); 31462306a36Sopenharmony_ci /* don't set RTS if using hardware flow control */ 31562306a36Sopenharmony_ci if (!(old_cflag & CRTSCTS)) 31662306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST 31762306a36Sopenharmony_ci , 1) < 0) 31862306a36Sopenharmony_ci dev_err(&port->dev, "Set RTS error\n"); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci baud = tty_get_baud_rate(tty); 32362306a36Sopenharmony_ci if (baud) { 32462306a36Sopenharmony_ci urb_value = BELKIN_SA_BAUD(baud); 32562306a36Sopenharmony_ci /* Clip to maximum speed */ 32662306a36Sopenharmony_ci if (urb_value == 0) 32762306a36Sopenharmony_ci urb_value = 1; 32862306a36Sopenharmony_ci /* Turn it back into a resulting real baud rate */ 32962306a36Sopenharmony_ci baud = BELKIN_SA_BAUD(urb_value); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Report the actual baud rate back to the caller */ 33262306a36Sopenharmony_ci tty_encode_baud_rate(tty, baud, baud); 33362306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) 33462306a36Sopenharmony_ci dev_err(&port->dev, "Set baudrate error\n"); 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci /* Disable flow control */ 33762306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, 33862306a36Sopenharmony_ci BELKIN_SA_FLOW_NONE) < 0) 33962306a36Sopenharmony_ci dev_err(&port->dev, "Disable flowcontrol error\n"); 34062306a36Sopenharmony_ci /* Drop RTS and DTR */ 34162306a36Sopenharmony_ci control_state &= ~(TIOCM_DTR | TIOCM_RTS); 34262306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0) 34362306a36Sopenharmony_ci dev_err(&port->dev, "DTR LOW error\n"); 34462306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0) 34562306a36Sopenharmony_ci dev_err(&port->dev, "RTS LOW error\n"); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* set the parity */ 34962306a36Sopenharmony_ci if ((cflag ^ old_cflag) & (PARENB | PARODD)) { 35062306a36Sopenharmony_ci if (cflag & PARENB) 35162306a36Sopenharmony_ci urb_value = (cflag & PARODD) ? BELKIN_SA_PARITY_ODD 35262306a36Sopenharmony_ci : BELKIN_SA_PARITY_EVEN; 35362306a36Sopenharmony_ci else 35462306a36Sopenharmony_ci urb_value = BELKIN_SA_PARITY_NONE; 35562306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_PARITY_REQUEST, urb_value) < 0) 35662306a36Sopenharmony_ci dev_err(&port->dev, "Set parity error\n"); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* set the number of data bits */ 36062306a36Sopenharmony_ci if ((cflag & CSIZE) != (old_cflag & CSIZE)) { 36162306a36Sopenharmony_ci urb_value = BELKIN_SA_DATA_BITS(tty_get_char_size(cflag)); 36262306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_DATA_BITS_REQUEST, urb_value) < 0) 36362306a36Sopenharmony_ci dev_err(&port->dev, "Set data bits error\n"); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* set the number of stop bits */ 36762306a36Sopenharmony_ci if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { 36862306a36Sopenharmony_ci urb_value = (cflag & CSTOPB) ? BELKIN_SA_STOP_BITS(2) 36962306a36Sopenharmony_ci : BELKIN_SA_STOP_BITS(1); 37062306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST, 37162306a36Sopenharmony_ci urb_value) < 0) 37262306a36Sopenharmony_ci dev_err(&port->dev, "Set stop bits error\n"); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Set flow control */ 37662306a36Sopenharmony_ci if (((iflag ^ old_iflag) & (IXOFF | IXON)) || 37762306a36Sopenharmony_ci ((cflag ^ old_cflag) & CRTSCTS)) { 37862306a36Sopenharmony_ci urb_value = 0; 37962306a36Sopenharmony_ci if ((iflag & IXOFF) || (iflag & IXON)) 38062306a36Sopenharmony_ci urb_value |= (BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON); 38162306a36Sopenharmony_ci else 38262306a36Sopenharmony_ci urb_value &= ~(BELKIN_SA_FLOW_OXON | BELKIN_SA_FLOW_IXON); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (cflag & CRTSCTS) 38562306a36Sopenharmony_ci urb_value |= (BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS); 38662306a36Sopenharmony_ci else 38762306a36Sopenharmony_ci urb_value &= ~(BELKIN_SA_FLOW_OCTS | BELKIN_SA_FLOW_IRTS); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (bad_flow_control) 39062306a36Sopenharmony_ci urb_value &= ~(BELKIN_SA_FLOW_IRTS); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, urb_value) < 0) 39362306a36Sopenharmony_ci dev_err(&port->dev, "Set flow control error\n"); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* save off the modified port settings */ 39762306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 39862306a36Sopenharmony_ci priv->control_state = control_state; 39962306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int belkin_sa_break_ctl(struct tty_struct *tty, int break_state) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 40562306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 40662306a36Sopenharmony_ci int ret; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ret = BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0); 40962306a36Sopenharmony_ci if (ret < 0) { 41062306a36Sopenharmony_ci dev_err(&port->dev, "Set break_ctl %d\n", break_state); 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int belkin_sa_tiocmget(struct tty_struct *tty) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 42062306a36Sopenharmony_ci struct belkin_sa_private *priv = usb_get_serial_port_data(port); 42162306a36Sopenharmony_ci unsigned long control_state; 42262306a36Sopenharmony_ci unsigned long flags; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 42562306a36Sopenharmony_ci control_state = priv->control_state; 42662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return control_state; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int belkin_sa_tiocmset(struct tty_struct *tty, 43262306a36Sopenharmony_ci unsigned int set, unsigned int clear) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 43562306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 43662306a36Sopenharmony_ci struct belkin_sa_private *priv = usb_get_serial_port_data(port); 43762306a36Sopenharmony_ci unsigned long control_state; 43862306a36Sopenharmony_ci unsigned long flags; 43962306a36Sopenharmony_ci int retval; 44062306a36Sopenharmony_ci int rts = 0; 44162306a36Sopenharmony_ci int dtr = 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 44462306a36Sopenharmony_ci control_state = priv->control_state; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (set & TIOCM_RTS) { 44762306a36Sopenharmony_ci control_state |= TIOCM_RTS; 44862306a36Sopenharmony_ci rts = 1; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci if (set & TIOCM_DTR) { 45162306a36Sopenharmony_ci control_state |= TIOCM_DTR; 45262306a36Sopenharmony_ci dtr = 1; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci if (clear & TIOCM_RTS) { 45562306a36Sopenharmony_ci control_state &= ~TIOCM_RTS; 45662306a36Sopenharmony_ci rts = 0; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci if (clear & TIOCM_DTR) { 45962306a36Sopenharmony_ci control_state &= ~TIOCM_DTR; 46062306a36Sopenharmony_ci dtr = 0; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci priv->control_state = control_state; 46462306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts); 46762306a36Sopenharmony_ci if (retval < 0) { 46862306a36Sopenharmony_ci dev_err(&port->dev, "Set RTS error %d\n", retval); 46962306a36Sopenharmony_ci goto exit; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr); 47362306a36Sopenharmony_ci if (retval < 0) { 47462306a36Sopenharmony_ci dev_err(&port->dev, "Set DTR error %d\n", retval); 47562306a36Sopenharmony_ci goto exit; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ciexit: 47862306a36Sopenharmony_ci return retval; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 48462306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 48562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 486