18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * usb-serial driver for Quatech SSU-100
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * based on ftdi_sio.c and the original serqt_usb.c from Quatech
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/tty.h>
128c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
138c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/serial.h>
168c2ecf20Sopenharmony_ci#include <linux/usb.h>
178c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
188c2ecf20Sopenharmony_ci#include <linux/serial_reg.h>
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define QT_OPEN_CLOSE_CHANNEL       0xca
228c2ecf20Sopenharmony_ci#define QT_SET_GET_DEVICE           0xc2
238c2ecf20Sopenharmony_ci#define QT_SET_GET_REGISTER         0xc0
248c2ecf20Sopenharmony_ci#define QT_GET_SET_PREBUF_TRIG_LVL  0xcc
258c2ecf20Sopenharmony_ci#define QT_SET_ATF                  0xcd
268c2ecf20Sopenharmony_ci#define QT_GET_SET_UART             0xc1
278c2ecf20Sopenharmony_ci#define QT_TRANSFER_IN              0xc0
288c2ecf20Sopenharmony_ci#define QT_HW_FLOW_CONTROL_MASK     0xc5
298c2ecf20Sopenharmony_ci#define QT_SW_FLOW_CONTROL_MASK     0xc6
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define  SERIAL_MSR_MASK            0xf0
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define  SERIAL_CRTSCTS ((UART_MCR_RTS << 8) | UART_MSR_CTS)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define  SERIAL_EVEN_PARITY         (UART_LCR_PARITY | UART_LCR_EPAR)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define  MAX_BAUD_RATE              460800
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define ATC_DISABLED                0x00
408c2ecf20Sopenharmony_ci#define DUPMODE_BITS        0xc0
418c2ecf20Sopenharmony_ci#define RR_BITS             0x03
428c2ecf20Sopenharmony_ci#define LOOPMODE_BITS       0x41
438c2ecf20Sopenharmony_ci#define RS232_MODE          0x00
448c2ecf20Sopenharmony_ci#define RTSCTS_TO_CONNECTOR 0x40
458c2ecf20Sopenharmony_ci#define CLKS_X4             0x02
468c2ecf20Sopenharmony_ci#define FULLPWRBIT          0x00000080
478c2ecf20Sopenharmony_ci#define NEXT_BOARD_POWER_BIT        0x00000004
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver"
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define	USB_VENDOR_ID_QUATECH	0x061d	/* Quatech VID */
528c2ecf20Sopenharmony_ci#define QUATECH_SSU100	0xC020	/* SSU100 */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
558c2ecf20Sopenharmony_ci	{USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU100)},
568c2ecf20Sopenharmony_ci	{}			/* Terminating entry */
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct ssu100_port_private {
618c2ecf20Sopenharmony_ci	spinlock_t status_lock;
628c2ecf20Sopenharmony_ci	u8 shadowLSR;
638c2ecf20Sopenharmony_ci	u8 shadowMSR;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic inline int ssu100_control_msg(struct usb_device *dev,
678c2ecf20Sopenharmony_ci				     u8 request, u16 data, u16 index)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
708c2ecf20Sopenharmony_ci			       request, 0x40, data, index,
718c2ecf20Sopenharmony_ci			       NULL, 0, 300);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic inline int ssu100_setdevice(struct usb_device *dev, u8 *data)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	u16 x = ((u16)(data[1] << 8) | (u16)(data[0]));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return ssu100_control_msg(dev, QT_SET_GET_DEVICE, x, 0);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline int ssu100_getdevice(struct usb_device *dev, u8 *data)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	int ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
878c2ecf20Sopenharmony_ci			      QT_SET_GET_DEVICE, 0xc0, 0, 0,
888c2ecf20Sopenharmony_ci			      data, 3, 300);
898c2ecf20Sopenharmony_ci	if (ret < 3) {
908c2ecf20Sopenharmony_ci		if (ret >= 0)
918c2ecf20Sopenharmony_ci			ret = -EIO;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return ret;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic inline int ssu100_getregister(struct usb_device *dev,
988c2ecf20Sopenharmony_ci				     unsigned short uart,
998c2ecf20Sopenharmony_ci				     unsigned short reg,
1008c2ecf20Sopenharmony_ci				     u8 *data)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	int ret;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
1058c2ecf20Sopenharmony_ci			      QT_SET_GET_REGISTER, 0xc0, reg,
1068c2ecf20Sopenharmony_ci			      uart, data, sizeof(*data), 300);
1078c2ecf20Sopenharmony_ci	if (ret < (int)sizeof(*data)) {
1088c2ecf20Sopenharmony_ci		if (ret >= 0)
1098c2ecf20Sopenharmony_ci			ret = -EIO;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return ret;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic inline int ssu100_setregister(struct usb_device *dev,
1178c2ecf20Sopenharmony_ci				     unsigned short uart,
1188c2ecf20Sopenharmony_ci				     unsigned short reg,
1198c2ecf20Sopenharmony_ci				     u16 data)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u16 value = (data << 8) | reg;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
1248c2ecf20Sopenharmony_ci			       QT_SET_GET_REGISTER, 0x40, value, uart,
1258c2ecf20Sopenharmony_ci			       NULL, 0, 300);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci#define set_mctrl(dev, set)		update_mctrl((dev), (set), 0)
1308c2ecf20Sopenharmony_ci#define clear_mctrl(dev, clear)	update_mctrl((dev), 0, (clear))
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/* these do not deal with device that have more than 1 port */
1338c2ecf20Sopenharmony_cistatic inline int update_mctrl(struct usb_device *dev, unsigned int set,
1348c2ecf20Sopenharmony_ci			       unsigned int clear)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	unsigned urb_value;
1378c2ecf20Sopenharmony_ci	int result;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
1408c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s - DTR|RTS not being set|cleared\n", __func__);
1418c2ecf20Sopenharmony_ci		return 0;	/* no change */
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	clear &= ~set;	/* 'set' takes precedence over 'clear' */
1458c2ecf20Sopenharmony_ci	urb_value = 0;
1468c2ecf20Sopenharmony_ci	if (set & TIOCM_DTR)
1478c2ecf20Sopenharmony_ci		urb_value |= UART_MCR_DTR;
1488c2ecf20Sopenharmony_ci	if (set & TIOCM_RTS)
1498c2ecf20Sopenharmony_ci		urb_value |= UART_MCR_RTS;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	result = ssu100_setregister(dev, 0, UART_MCR, urb_value);
1528c2ecf20Sopenharmony_ci	if (result < 0)
1538c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s Error from MODEM_CTRL urb\n", __func__);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return result;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int ssu100_initdevice(struct usb_device *dev)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	u8 *data;
1618c2ecf20Sopenharmony_ci	int result = 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	data = kzalloc(3, GFP_KERNEL);
1648c2ecf20Sopenharmony_ci	if (!data)
1658c2ecf20Sopenharmony_ci		return -ENOMEM;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	result = ssu100_getdevice(dev, data);
1688c2ecf20Sopenharmony_ci	if (result < 0) {
1698c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
1708c2ecf20Sopenharmony_ci		goto out;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	data[1] &= ~FULLPWRBIT;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	result = ssu100_setdevice(dev, data);
1768c2ecf20Sopenharmony_ci	if (result < 0) {
1778c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
1788c2ecf20Sopenharmony_ci		goto out;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0);
1828c2ecf20Sopenharmony_ci	if (result < 0) {
1838c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s - set prebuffer level failed %i\n", __func__, result);
1848c2ecf20Sopenharmony_ci		goto out;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0);
1888c2ecf20Sopenharmony_ci	if (result < 0) {
1898c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s - set ATFprebuffer level failed %i\n", __func__, result);
1908c2ecf20Sopenharmony_ci		goto out;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	result = ssu100_getdevice(dev, data);
1948c2ecf20Sopenharmony_ci	if (result < 0) {
1958c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
1968c2ecf20Sopenharmony_ci		goto out;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	data[0] &= ~(RR_BITS | DUPMODE_BITS);
2008c2ecf20Sopenharmony_ci	data[0] |= CLKS_X4;
2018c2ecf20Sopenharmony_ci	data[1] &= ~(LOOPMODE_BITS);
2028c2ecf20Sopenharmony_ci	data[1] |= RS232_MODE;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	result = ssu100_setdevice(dev, data);
2058c2ecf20Sopenharmony_ci	if (result < 0) {
2068c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
2078c2ecf20Sopenharmony_ci		goto out;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciout:	kfree(data);
2118c2ecf20Sopenharmony_ci	return result;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void ssu100_set_termios(struct tty_struct *tty,
2178c2ecf20Sopenharmony_ci			       struct usb_serial_port *port,
2188c2ecf20Sopenharmony_ci			       struct ktermios *old_termios)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
2218c2ecf20Sopenharmony_ci	struct ktermios *termios = &tty->termios;
2228c2ecf20Sopenharmony_ci	u16 baud, divisor, remainder;
2238c2ecf20Sopenharmony_ci	unsigned int cflag = termios->c_cflag;
2248c2ecf20Sopenharmony_ci	u16 urb_value = 0; /* will hold the new flags */
2258c2ecf20Sopenharmony_ci	int result;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (cflag & PARENB) {
2288c2ecf20Sopenharmony_ci		if (cflag & PARODD)
2298c2ecf20Sopenharmony_ci			urb_value |= UART_LCR_PARITY;
2308c2ecf20Sopenharmony_ci		else
2318c2ecf20Sopenharmony_ci			urb_value |= SERIAL_EVEN_PARITY;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	switch (cflag & CSIZE) {
2358c2ecf20Sopenharmony_ci	case CS5:
2368c2ecf20Sopenharmony_ci		urb_value |= UART_LCR_WLEN5;
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	case CS6:
2398c2ecf20Sopenharmony_ci		urb_value |= UART_LCR_WLEN6;
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case CS7:
2428c2ecf20Sopenharmony_ci		urb_value |= UART_LCR_WLEN7;
2438c2ecf20Sopenharmony_ci		break;
2448c2ecf20Sopenharmony_ci	default:
2458c2ecf20Sopenharmony_ci	case CS8:
2468c2ecf20Sopenharmony_ci		urb_value |= UART_LCR_WLEN8;
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	baud = tty_get_baud_rate(tty);
2518c2ecf20Sopenharmony_ci	if (!baud)
2528c2ecf20Sopenharmony_ci		baud = 9600;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	divisor = MAX_BAUD_RATE / baud;
2588c2ecf20Sopenharmony_ci	remainder = MAX_BAUD_RATE % baud;
2598c2ecf20Sopenharmony_ci	if (((remainder * 2) >= baud) && (baud != 110))
2608c2ecf20Sopenharmony_ci		divisor++;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	urb_value = urb_value << 8;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value);
2658c2ecf20Sopenharmony_ci	if (result < 0)
2668c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (cflag & CRTSCTS)
2698c2ecf20Sopenharmony_ci		result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
2708c2ecf20Sopenharmony_ci					    SERIAL_CRTSCTS, 0);
2718c2ecf20Sopenharmony_ci	else
2728c2ecf20Sopenharmony_ci		result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
2738c2ecf20Sopenharmony_ci					    0, 0);
2748c2ecf20Sopenharmony_ci	if (result < 0)
2758c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - set HW flow control failed\n", __func__);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (I_IXOFF(tty) || I_IXON(tty)) {
2788c2ecf20Sopenharmony_ci		u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty)));
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
2818c2ecf20Sopenharmony_ci					    x, 0);
2828c2ecf20Sopenharmony_ci	} else
2838c2ecf20Sopenharmony_ci		result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
2848c2ecf20Sopenharmony_ci					    0, 0);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (result < 0)
2878c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - set SW flow control failed\n", __func__);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
2958c2ecf20Sopenharmony_ci	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
2968c2ecf20Sopenharmony_ci	u8 *data;
2978c2ecf20Sopenharmony_ci	int result;
2988c2ecf20Sopenharmony_ci	unsigned long flags;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	data = kzalloc(2, GFP_KERNEL);
3018c2ecf20Sopenharmony_ci	if (!data)
3028c2ecf20Sopenharmony_ci		return -ENOMEM;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
3058c2ecf20Sopenharmony_ci				 QT_OPEN_CLOSE_CHANNEL,
3068c2ecf20Sopenharmony_ci				 QT_TRANSFER_IN, 0x01,
3078c2ecf20Sopenharmony_ci				 0, data, 2, 300);
3088c2ecf20Sopenharmony_ci	if (result < 2) {
3098c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - open failed %i\n", __func__, result);
3108c2ecf20Sopenharmony_ci		if (result >= 0)
3118c2ecf20Sopenharmony_ci			result = -EIO;
3128c2ecf20Sopenharmony_ci		kfree(data);
3138c2ecf20Sopenharmony_ci		return result;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->status_lock, flags);
3178c2ecf20Sopenharmony_ci	priv->shadowLSR = data[0];
3188c2ecf20Sopenharmony_ci	priv->shadowMSR = data[1];
3198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->status_lock, flags);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	kfree(data);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/* set to 9600 */
3248c2ecf20Sopenharmony_ci	result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300);
3258c2ecf20Sopenharmony_ci	if (result < 0)
3268c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if (tty)
3298c2ecf20Sopenharmony_ci		ssu100_set_termios(tty, port, &tty->termios);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return usb_serial_generic_open(tty, port);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int get_serial_info(struct tty_struct *tty,
3358c2ecf20Sopenharmony_ci			   struct serial_struct *ss)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ss->line		= port->minor;
3408c2ecf20Sopenharmony_ci	ss->port		= 0;
3418c2ecf20Sopenharmony_ci	ss->irq			= 0;
3428c2ecf20Sopenharmony_ci	ss->xmit_fifo_size	= port->bulk_out_size;
3438c2ecf20Sopenharmony_ci	ss->baud_base		= 9600;
3448c2ecf20Sopenharmony_ci	ss->close_delay		= 5*HZ;
3458c2ecf20Sopenharmony_ci	ss->closing_wait	= 30*HZ;
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int ssu100_attach(struct usb_serial *serial)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	return ssu100_initdevice(serial->dev);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int ssu100_port_probe(struct usb_serial_port *port)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct ssu100_port_private *priv;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
3598c2ecf20Sopenharmony_ci	if (!priv)
3608c2ecf20Sopenharmony_ci		return -ENOMEM;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	spin_lock_init(&priv->status_lock);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, priv);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int ssu100_port_remove(struct usb_serial_port *port)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	struct ssu100_port_private *priv;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	priv = usb_get_serial_port_data(port);
3748c2ecf20Sopenharmony_ci	kfree(priv);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	return 0;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int ssu100_tiocmget(struct tty_struct *tty)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
3828c2ecf20Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
3838c2ecf20Sopenharmony_ci	u8 *d;
3848c2ecf20Sopenharmony_ci	int r;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	d = kzalloc(2, GFP_KERNEL);
3878c2ecf20Sopenharmony_ci	if (!d)
3888c2ecf20Sopenharmony_ci		return -ENOMEM;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	r = ssu100_getregister(dev, 0, UART_MCR, d);
3918c2ecf20Sopenharmony_ci	if (r < 0)
3928c2ecf20Sopenharmony_ci		goto mget_out;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	r = ssu100_getregister(dev, 0, UART_MSR, d+1);
3958c2ecf20Sopenharmony_ci	if (r < 0)
3968c2ecf20Sopenharmony_ci		goto mget_out;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	r = (d[0] & UART_MCR_DTR ? TIOCM_DTR : 0) |
3998c2ecf20Sopenharmony_ci		(d[0] & UART_MCR_RTS ? TIOCM_RTS : 0) |
4008c2ecf20Sopenharmony_ci		(d[1] & UART_MSR_CTS ? TIOCM_CTS : 0) |
4018c2ecf20Sopenharmony_ci		(d[1] & UART_MSR_DCD ? TIOCM_CAR : 0) |
4028c2ecf20Sopenharmony_ci		(d[1] & UART_MSR_RI ? TIOCM_RI : 0) |
4038c2ecf20Sopenharmony_ci		(d[1] & UART_MSR_DSR ? TIOCM_DSR : 0);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cimget_out:
4068c2ecf20Sopenharmony_ci	kfree(d);
4078c2ecf20Sopenharmony_ci	return r;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int ssu100_tiocmset(struct tty_struct *tty,
4118c2ecf20Sopenharmony_ci			   unsigned int set, unsigned int clear)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
4148c2ecf20Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return update_mctrl(dev, set, clear);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic void ssu100_dtr_rts(struct usb_serial_port *port, int on)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct usb_device *dev = port->serial->dev;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Disable flow control */
4248c2ecf20Sopenharmony_ci	if (!on) {
4258c2ecf20Sopenharmony_ci		if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
4268c2ecf20Sopenharmony_ci			dev_err(&port->dev, "error from flowcontrol urb\n");
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci	/* drop RTS and DTR */
4298c2ecf20Sopenharmony_ci	if (on)
4308c2ecf20Sopenharmony_ci		set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
4318c2ecf20Sopenharmony_ci	else
4328c2ecf20Sopenharmony_ci		clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
4388c2ecf20Sopenharmony_ci	unsigned long flags;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->status_lock, flags);
4418c2ecf20Sopenharmony_ci	priv->shadowMSR = msr;
4428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->status_lock, flags);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (msr & UART_MSR_ANY_DELTA) {
4458c2ecf20Sopenharmony_ci		/* update input line counters */
4468c2ecf20Sopenharmony_ci		if (msr & UART_MSR_DCTS)
4478c2ecf20Sopenharmony_ci			port->icount.cts++;
4488c2ecf20Sopenharmony_ci		if (msr & UART_MSR_DDSR)
4498c2ecf20Sopenharmony_ci			port->icount.dsr++;
4508c2ecf20Sopenharmony_ci		if (msr & UART_MSR_DDCD)
4518c2ecf20Sopenharmony_ci			port->icount.dcd++;
4528c2ecf20Sopenharmony_ci		if (msr & UART_MSR_TERI)
4538c2ecf20Sopenharmony_ci			port->icount.rng++;
4548c2ecf20Sopenharmony_ci		wake_up_interruptible(&port->port.delta_msr_wait);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr,
4598c2ecf20Sopenharmony_ci			      char *tty_flag)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct ssu100_port_private *priv = usb_get_serial_port_data(port);
4628c2ecf20Sopenharmony_ci	unsigned long flags;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->status_lock, flags);
4658c2ecf20Sopenharmony_ci	priv->shadowLSR = lsr;
4668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->status_lock, flags);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	*tty_flag = TTY_NORMAL;
4698c2ecf20Sopenharmony_ci	if (lsr & UART_LSR_BRK_ERROR_BITS) {
4708c2ecf20Sopenharmony_ci		/* we always want to update icount, but we only want to
4718c2ecf20Sopenharmony_ci		 * update tty_flag for one case */
4728c2ecf20Sopenharmony_ci		if (lsr & UART_LSR_BI) {
4738c2ecf20Sopenharmony_ci			port->icount.brk++;
4748c2ecf20Sopenharmony_ci			*tty_flag = TTY_BREAK;
4758c2ecf20Sopenharmony_ci			usb_serial_handle_break(port);
4768c2ecf20Sopenharmony_ci		}
4778c2ecf20Sopenharmony_ci		if (lsr & UART_LSR_PE) {
4788c2ecf20Sopenharmony_ci			port->icount.parity++;
4798c2ecf20Sopenharmony_ci			if (*tty_flag == TTY_NORMAL)
4808c2ecf20Sopenharmony_ci				*tty_flag = TTY_PARITY;
4818c2ecf20Sopenharmony_ci		}
4828c2ecf20Sopenharmony_ci		if (lsr & UART_LSR_FE) {
4838c2ecf20Sopenharmony_ci			port->icount.frame++;
4848c2ecf20Sopenharmony_ci			if (*tty_flag == TTY_NORMAL)
4858c2ecf20Sopenharmony_ci				*tty_flag = TTY_FRAME;
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci		if (lsr & UART_LSR_OE) {
4888c2ecf20Sopenharmony_ci			port->icount.overrun++;
4898c2ecf20Sopenharmony_ci			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic void ssu100_process_read_urb(struct urb *urb)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
4988c2ecf20Sopenharmony_ci	char *packet = urb->transfer_buffer;
4998c2ecf20Sopenharmony_ci	char flag = TTY_NORMAL;
5008c2ecf20Sopenharmony_ci	u32 len = urb->actual_length;
5018c2ecf20Sopenharmony_ci	int i;
5028c2ecf20Sopenharmony_ci	char *ch;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if ((len >= 4) &&
5058c2ecf20Sopenharmony_ci	    (packet[0] == 0x1b) && (packet[1] == 0x1b) &&
5068c2ecf20Sopenharmony_ci	    ((packet[2] == 0x00) || (packet[2] == 0x01))) {
5078c2ecf20Sopenharmony_ci		if (packet[2] == 0x00)
5088c2ecf20Sopenharmony_ci			ssu100_update_lsr(port, packet[3], &flag);
5098c2ecf20Sopenharmony_ci		if (packet[2] == 0x01)
5108c2ecf20Sopenharmony_ci			ssu100_update_msr(port, packet[3]);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		len -= 4;
5138c2ecf20Sopenharmony_ci		ch = packet + 4;
5148c2ecf20Sopenharmony_ci	} else
5158c2ecf20Sopenharmony_ci		ch = packet;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (!len)
5188c2ecf20Sopenharmony_ci		return;	/* status only */
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	if (port->sysrq) {
5218c2ecf20Sopenharmony_ci		for (i = 0; i < len; i++, ch++) {
5228c2ecf20Sopenharmony_ci			if (!usb_serial_handle_sysrq_char(port, *ch))
5238c2ecf20Sopenharmony_ci				tty_insert_flip_char(&port->port, *ch, flag);
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci	} else {
5268c2ecf20Sopenharmony_ci		tty_insert_flip_string_fixed_flag(&port->port, ch, flag, len);
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	tty_flip_buffer_push(&port->port);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic struct usb_serial_driver ssu100_device = {
5338c2ecf20Sopenharmony_ci	.driver = {
5348c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
5358c2ecf20Sopenharmony_ci		.name = "ssu100",
5368c2ecf20Sopenharmony_ci	},
5378c2ecf20Sopenharmony_ci	.description	     = DRIVER_DESC,
5388c2ecf20Sopenharmony_ci	.id_table	     = id_table,
5398c2ecf20Sopenharmony_ci	.num_ports	     = 1,
5408c2ecf20Sopenharmony_ci	.open		     = ssu100_open,
5418c2ecf20Sopenharmony_ci	.attach              = ssu100_attach,
5428c2ecf20Sopenharmony_ci	.port_probe          = ssu100_port_probe,
5438c2ecf20Sopenharmony_ci	.port_remove         = ssu100_port_remove,
5448c2ecf20Sopenharmony_ci	.dtr_rts             = ssu100_dtr_rts,
5458c2ecf20Sopenharmony_ci	.process_read_urb    = ssu100_process_read_urb,
5468c2ecf20Sopenharmony_ci	.tiocmget            = ssu100_tiocmget,
5478c2ecf20Sopenharmony_ci	.tiocmset            = ssu100_tiocmset,
5488c2ecf20Sopenharmony_ci	.tiocmiwait          = usb_serial_generic_tiocmiwait,
5498c2ecf20Sopenharmony_ci	.get_icount	     = usb_serial_generic_get_icount,
5508c2ecf20Sopenharmony_ci	.get_serial          = get_serial_info,
5518c2ecf20Sopenharmony_ci	.set_termios         = ssu100_set_termios,
5528c2ecf20Sopenharmony_ci};
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
5558c2ecf20Sopenharmony_ci	&ssu100_device, NULL
5568c2ecf20Sopenharmony_ci};
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
5618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
562