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