xref: /OK3568_Linux_fs/kernel/drivers/usb/serial/kobil_sct.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  KOBIL USB Smart Card Terminal Driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2002  KOBIL Systems GmbH
6*4882a593Smuzhiyun  *  Author: Thomas Wahrenbruch
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *  Contact: linuxusb@kobil.de
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  *  This program is largely derived from work by the linux-usb group
11*4882a593Smuzhiyun  *  and associated source files.  Please see the usb/serial files for
12*4882a593Smuzhiyun  *  individual credits and copyrights.
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  *  Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
15*4882a593Smuzhiyun  *  patience.
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus
18*4882a593Smuzhiyun  * (Adapter K), B1 Professional and KAAN Professional (Adapter B)
19*4882a593Smuzhiyun  */
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include <linux/kernel.h>
23*4882a593Smuzhiyun #include <linux/errno.h>
24*4882a593Smuzhiyun #include <linux/slab.h>
25*4882a593Smuzhiyun #include <linux/tty.h>
26*4882a593Smuzhiyun #include <linux/tty_driver.h>
27*4882a593Smuzhiyun #include <linux/tty_flip.h>
28*4882a593Smuzhiyun #include <linux/module.h>
29*4882a593Smuzhiyun #include <linux/spinlock.h>
30*4882a593Smuzhiyun #include <linux/uaccess.h>
31*4882a593Smuzhiyun #include <linux/usb.h>
32*4882a593Smuzhiyun #include <linux/usb/serial.h>
33*4882a593Smuzhiyun #include <linux/ioctl.h>
34*4882a593Smuzhiyun #include "kobil_sct.h"
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com"
37*4882a593Smuzhiyun #define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)"
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #define KOBIL_VENDOR_ID			0x0D46
40*4882a593Smuzhiyun #define KOBIL_ADAPTER_B_PRODUCT_ID	0x2011
41*4882a593Smuzhiyun #define KOBIL_ADAPTER_K_PRODUCT_ID	0x2012
42*4882a593Smuzhiyun #define KOBIL_USBTWIN_PRODUCT_ID	0x0078
43*4882a593Smuzhiyun #define KOBIL_KAAN_SIM_PRODUCT_ID       0x0081
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define KOBIL_TIMEOUT		500
46*4882a593Smuzhiyun #define KOBIL_BUF_LENGTH	300
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun /* Function prototypes */
50*4882a593Smuzhiyun static int kobil_port_probe(struct usb_serial_port *probe);
51*4882a593Smuzhiyun static int kobil_port_remove(struct usb_serial_port *probe);
52*4882a593Smuzhiyun static int  kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
53*4882a593Smuzhiyun static void kobil_close(struct usb_serial_port *port);
54*4882a593Smuzhiyun static int  kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
55*4882a593Smuzhiyun 			 const unsigned char *buf, int count);
56*4882a593Smuzhiyun static int  kobil_write_room(struct tty_struct *tty);
57*4882a593Smuzhiyun static int  kobil_ioctl(struct tty_struct *tty,
58*4882a593Smuzhiyun 			unsigned int cmd, unsigned long arg);
59*4882a593Smuzhiyun static int  kobil_tiocmget(struct tty_struct *tty);
60*4882a593Smuzhiyun static int  kobil_tiocmset(struct tty_struct *tty,
61*4882a593Smuzhiyun 			   unsigned int set, unsigned int clear);
62*4882a593Smuzhiyun static void kobil_read_int_callback(struct urb *urb);
63*4882a593Smuzhiyun static void kobil_write_int_callback(struct urb *urb);
64*4882a593Smuzhiyun static void kobil_set_termios(struct tty_struct *tty,
65*4882a593Smuzhiyun 			struct usb_serial_port *port, struct ktermios *old);
66*4882a593Smuzhiyun static void kobil_init_termios(struct tty_struct *tty);
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun static const struct usb_device_id id_table[] = {
69*4882a593Smuzhiyun 	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) },
70*4882a593Smuzhiyun 	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_K_PRODUCT_ID) },
71*4882a593Smuzhiyun 	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) },
72*4882a593Smuzhiyun 	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_KAAN_SIM_PRODUCT_ID) },
73*4882a593Smuzhiyun 	{ }			/* Terminating entry */
74*4882a593Smuzhiyun };
75*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, id_table);
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun static struct usb_serial_driver kobil_device = {
78*4882a593Smuzhiyun 	.driver = {
79*4882a593Smuzhiyun 		.owner =	THIS_MODULE,
80*4882a593Smuzhiyun 		.name =		"kobil",
81*4882a593Smuzhiyun 	},
82*4882a593Smuzhiyun 	.description =		"KOBIL USB smart card terminal",
83*4882a593Smuzhiyun 	.id_table =		id_table,
84*4882a593Smuzhiyun 	.num_ports =		1,
85*4882a593Smuzhiyun 	.num_interrupt_out =	1,
86*4882a593Smuzhiyun 	.port_probe =		kobil_port_probe,
87*4882a593Smuzhiyun 	.port_remove =		kobil_port_remove,
88*4882a593Smuzhiyun 	.ioctl =		kobil_ioctl,
89*4882a593Smuzhiyun 	.set_termios =		kobil_set_termios,
90*4882a593Smuzhiyun 	.init_termios =		kobil_init_termios,
91*4882a593Smuzhiyun 	.tiocmget =		kobil_tiocmget,
92*4882a593Smuzhiyun 	.tiocmset =		kobil_tiocmset,
93*4882a593Smuzhiyun 	.open =			kobil_open,
94*4882a593Smuzhiyun 	.close =		kobil_close,
95*4882a593Smuzhiyun 	.write =		kobil_write,
96*4882a593Smuzhiyun 	.write_room =		kobil_write_room,
97*4882a593Smuzhiyun 	.read_int_callback =	kobil_read_int_callback,
98*4882a593Smuzhiyun 	.write_int_callback =	kobil_write_int_callback,
99*4882a593Smuzhiyun };
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun static struct usb_serial_driver * const serial_drivers[] = {
102*4882a593Smuzhiyun 	&kobil_device, NULL
103*4882a593Smuzhiyun };
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun struct kobil_private {
106*4882a593Smuzhiyun 	unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */
107*4882a593Smuzhiyun 	int filled;  /* index of the last char in buf */
108*4882a593Smuzhiyun 	int cur_pos; /* index of the next char to send in buf */
109*4882a593Smuzhiyun 	__u16 device_type;
110*4882a593Smuzhiyun };
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 
kobil_port_probe(struct usb_serial_port * port)113*4882a593Smuzhiyun static int kobil_port_probe(struct usb_serial_port *port)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	struct usb_serial *serial = port->serial;
116*4882a593Smuzhiyun 	struct kobil_private *priv;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL);
119*4882a593Smuzhiyun 	if (!priv)
120*4882a593Smuzhiyun 		return -ENOMEM;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	priv->filled = 0;
123*4882a593Smuzhiyun 	priv->cur_pos = 0;
124*4882a593Smuzhiyun 	priv->device_type = le16_to_cpu(serial->dev->descriptor.idProduct);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	switch (priv->device_type) {
127*4882a593Smuzhiyun 	case KOBIL_ADAPTER_B_PRODUCT_ID:
128*4882a593Smuzhiyun 		dev_dbg(&serial->dev->dev, "KOBIL B1 PRO / KAAN PRO detected\n");
129*4882a593Smuzhiyun 		break;
130*4882a593Smuzhiyun 	case KOBIL_ADAPTER_K_PRODUCT_ID:
131*4882a593Smuzhiyun 		dev_dbg(&serial->dev->dev, "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
132*4882a593Smuzhiyun 		break;
133*4882a593Smuzhiyun 	case KOBIL_USBTWIN_PRODUCT_ID:
134*4882a593Smuzhiyun 		dev_dbg(&serial->dev->dev, "KOBIL USBTWIN detected\n");
135*4882a593Smuzhiyun 		break;
136*4882a593Smuzhiyun 	case KOBIL_KAAN_SIM_PRODUCT_ID:
137*4882a593Smuzhiyun 		dev_dbg(&serial->dev->dev, "KOBIL KAAN SIM detected\n");
138*4882a593Smuzhiyun 		break;
139*4882a593Smuzhiyun 	}
140*4882a593Smuzhiyun 	usb_set_serial_port_data(port, priv);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	return 0;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 
kobil_port_remove(struct usb_serial_port * port)146*4882a593Smuzhiyun static int kobil_port_remove(struct usb_serial_port *port)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	struct kobil_private *priv;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
151*4882a593Smuzhiyun 	kfree(priv);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	return 0;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
kobil_init_termios(struct tty_struct * tty)156*4882a593Smuzhiyun static void kobil_init_termios(struct tty_struct *tty)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	/* Default to echo off and other sane device settings */
159*4882a593Smuzhiyun 	tty->termios.c_lflag = 0;
160*4882a593Smuzhiyun 	tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
161*4882a593Smuzhiyun 	tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF;
162*4882a593Smuzhiyun 	/* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
163*4882a593Smuzhiyun 	tty->termios.c_oflag &= ~ONLCR;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
kobil_open(struct tty_struct * tty,struct usb_serial_port * port)166*4882a593Smuzhiyun static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	struct device *dev = &port->dev;
169*4882a593Smuzhiyun 	int result = 0;
170*4882a593Smuzhiyun 	struct kobil_private *priv;
171*4882a593Smuzhiyun 	unsigned char *transfer_buffer;
172*4882a593Smuzhiyun 	int transfer_buffer_length = 8;
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	/* allocate memory for transfer buffer */
177*4882a593Smuzhiyun 	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
178*4882a593Smuzhiyun 	if (!transfer_buffer)
179*4882a593Smuzhiyun 		return -ENOMEM;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	/* get hardware version */
182*4882a593Smuzhiyun 	result = usb_control_msg(port->serial->dev,
183*4882a593Smuzhiyun 			  usb_rcvctrlpipe(port->serial->dev, 0),
184*4882a593Smuzhiyun 			  SUSBCRequest_GetMisc,
185*4882a593Smuzhiyun 			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
186*4882a593Smuzhiyun 			  SUSBCR_MSC_GetHWVersion,
187*4882a593Smuzhiyun 			  0,
188*4882a593Smuzhiyun 			  transfer_buffer,
189*4882a593Smuzhiyun 			  transfer_buffer_length,
190*4882a593Smuzhiyun 			  KOBIL_TIMEOUT
191*4882a593Smuzhiyun 	);
192*4882a593Smuzhiyun 	dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
193*4882a593Smuzhiyun 	if (result >= 3) {
194*4882a593Smuzhiyun 		dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0],
195*4882a593Smuzhiyun 				transfer_buffer[1], transfer_buffer[2]);
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	/* get firmware version */
199*4882a593Smuzhiyun 	result = usb_control_msg(port->serial->dev,
200*4882a593Smuzhiyun 			  usb_rcvctrlpipe(port->serial->dev, 0),
201*4882a593Smuzhiyun 			  SUSBCRequest_GetMisc,
202*4882a593Smuzhiyun 			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
203*4882a593Smuzhiyun 			  SUSBCR_MSC_GetFWVersion,
204*4882a593Smuzhiyun 			  0,
205*4882a593Smuzhiyun 			  transfer_buffer,
206*4882a593Smuzhiyun 			  transfer_buffer_length,
207*4882a593Smuzhiyun 			  KOBIL_TIMEOUT
208*4882a593Smuzhiyun 	);
209*4882a593Smuzhiyun 	dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
210*4882a593Smuzhiyun 	if (result >= 3) {
211*4882a593Smuzhiyun 		dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
212*4882a593Smuzhiyun 				transfer_buffer[1], transfer_buffer[2]);
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
216*4882a593Smuzhiyun 			priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
217*4882a593Smuzhiyun 		/* Setting Baudrate, Parity and Stopbits */
218*4882a593Smuzhiyun 		result = usb_control_msg(port->serial->dev,
219*4882a593Smuzhiyun 			  usb_sndctrlpipe(port->serial->dev, 0),
220*4882a593Smuzhiyun 			  SUSBCRequest_SetBaudRateParityAndStopBits,
221*4882a593Smuzhiyun 			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
222*4882a593Smuzhiyun 			  SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
223*4882a593Smuzhiyun 							SUSBCR_SPASB_1StopBit,
224*4882a593Smuzhiyun 			  0,
225*4882a593Smuzhiyun 			  NULL,
226*4882a593Smuzhiyun 			  0,
227*4882a593Smuzhiyun 			  KOBIL_TIMEOUT
228*4882a593Smuzhiyun 		);
229*4882a593Smuzhiyun 		dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result);
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 		/* reset all queues */
232*4882a593Smuzhiyun 		result = usb_control_msg(port->serial->dev,
233*4882a593Smuzhiyun 			  usb_sndctrlpipe(port->serial->dev, 0),
234*4882a593Smuzhiyun 			  SUSBCRequest_Misc,
235*4882a593Smuzhiyun 			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
236*4882a593Smuzhiyun 			  SUSBCR_MSC_ResetAllQueues,
237*4882a593Smuzhiyun 			  0,
238*4882a593Smuzhiyun 			  NULL,
239*4882a593Smuzhiyun 			  0,
240*4882a593Smuzhiyun 			  KOBIL_TIMEOUT
241*4882a593Smuzhiyun 		);
242*4882a593Smuzhiyun 		dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result);
243*4882a593Smuzhiyun 	}
244*4882a593Smuzhiyun 	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
245*4882a593Smuzhiyun 	    priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
246*4882a593Smuzhiyun 	    priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
247*4882a593Smuzhiyun 		/* start reading (Adapter B 'cause PNP string) */
248*4882a593Smuzhiyun 		result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
249*4882a593Smuzhiyun 		dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result);
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	kfree(transfer_buffer);
253*4882a593Smuzhiyun 	return 0;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 
kobil_close(struct usb_serial_port * port)257*4882a593Smuzhiyun static void kobil_close(struct usb_serial_port *port)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun 	/* FIXME: Add rts/dtr methods */
260*4882a593Smuzhiyun 	usb_kill_urb(port->interrupt_out_urb);
261*4882a593Smuzhiyun 	usb_kill_urb(port->interrupt_in_urb);
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 
kobil_read_int_callback(struct urb * urb)265*4882a593Smuzhiyun static void kobil_read_int_callback(struct urb *urb)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	int result;
268*4882a593Smuzhiyun 	struct usb_serial_port *port = urb->context;
269*4882a593Smuzhiyun 	unsigned char *data = urb->transfer_buffer;
270*4882a593Smuzhiyun 	int status = urb->status;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	if (status) {
273*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - Read int status not zero: %d\n", __func__, status);
274*4882a593Smuzhiyun 		return;
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	if (urb->actual_length) {
278*4882a593Smuzhiyun 		usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
279*4882a593Smuzhiyun 									data);
280*4882a593Smuzhiyun 		tty_insert_flip_string(&port->port, data, urb->actual_length);
281*4882a593Smuzhiyun 		tty_flip_buffer_push(&port->port);
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
285*4882a593Smuzhiyun 	dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 
kobil_write_int_callback(struct urb * urb)289*4882a593Smuzhiyun static void kobil_write_int_callback(struct urb *urb)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 
kobil_write(struct tty_struct * tty,struct usb_serial_port * port,const unsigned char * buf,int count)294*4882a593Smuzhiyun static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
295*4882a593Smuzhiyun 			const unsigned char *buf, int count)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	int length = 0;
298*4882a593Smuzhiyun 	int result = 0;
299*4882a593Smuzhiyun 	int todo = 0;
300*4882a593Smuzhiyun 	struct kobil_private *priv;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	if (count == 0) {
303*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
304*4882a593Smuzhiyun 		return 0;
305*4882a593Smuzhiyun 	}
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
310*4882a593Smuzhiyun 		dev_dbg(&port->dev, "%s - Error: write request bigger than buffer size\n", __func__);
311*4882a593Smuzhiyun 		return -ENOMEM;
312*4882a593Smuzhiyun 	}
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	/* Copy data to buffer */
315*4882a593Smuzhiyun 	memcpy(priv->buf + priv->filled, buf, count);
316*4882a593Smuzhiyun 	usb_serial_debug_data(&port->dev, __func__, count, priv->buf + priv->filled);
317*4882a593Smuzhiyun 	priv->filled = priv->filled + count;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	/* only send complete block. TWIN, KAAN SIM and adapter K
320*4882a593Smuzhiyun 	   use the same protocol. */
321*4882a593Smuzhiyun 	if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) ||
322*4882a593Smuzhiyun 	     ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) {
323*4882a593Smuzhiyun 		/* stop reading (except TWIN and KAAN SIM) */
324*4882a593Smuzhiyun 		if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID)
325*4882a593Smuzhiyun 			|| (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID))
326*4882a593Smuzhiyun 			usb_kill_urb(port->interrupt_in_urb);
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 		todo = priv->filled - priv->cur_pos;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 		while (todo > 0) {
331*4882a593Smuzhiyun 			/* max 8 byte in one urb (endpoint size) */
332*4882a593Smuzhiyun 			length = min(todo, port->interrupt_out_size);
333*4882a593Smuzhiyun 			/* copy data to transfer buffer */
334*4882a593Smuzhiyun 			memcpy(port->interrupt_out_buffer,
335*4882a593Smuzhiyun 					priv->buf + priv->cur_pos, length);
336*4882a593Smuzhiyun 			port->interrupt_out_urb->transfer_buffer_length = length;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 			priv->cur_pos = priv->cur_pos + length;
339*4882a593Smuzhiyun 			result = usb_submit_urb(port->interrupt_out_urb,
340*4882a593Smuzhiyun 					GFP_ATOMIC);
341*4882a593Smuzhiyun 			dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
342*4882a593Smuzhiyun 			todo = priv->filled - priv->cur_pos;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 			if (todo > 0)
345*4882a593Smuzhiyun 				msleep(24);
346*4882a593Smuzhiyun 		}
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 		priv->filled = 0;
349*4882a593Smuzhiyun 		priv->cur_pos = 0;
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 		/* start reading (except TWIN and KAAN SIM) */
352*4882a593Smuzhiyun 		if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
353*4882a593Smuzhiyun 			priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
354*4882a593Smuzhiyun 			result = usb_submit_urb(port->interrupt_in_urb,
355*4882a593Smuzhiyun 					GFP_ATOMIC);
356*4882a593Smuzhiyun 			dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
357*4882a593Smuzhiyun 		}
358*4882a593Smuzhiyun 	}
359*4882a593Smuzhiyun 	return count;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 
kobil_write_room(struct tty_struct * tty)363*4882a593Smuzhiyun static int kobil_write_room(struct tty_struct *tty)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	/* FIXME */
366*4882a593Smuzhiyun 	return 8;
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 
kobil_tiocmget(struct tty_struct * tty)370*4882a593Smuzhiyun static int kobil_tiocmget(struct tty_struct *tty)
371*4882a593Smuzhiyun {
372*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
373*4882a593Smuzhiyun 	struct kobil_private *priv;
374*4882a593Smuzhiyun 	int result;
375*4882a593Smuzhiyun 	unsigned char *transfer_buffer;
376*4882a593Smuzhiyun 	int transfer_buffer_length = 8;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
379*4882a593Smuzhiyun 	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
380*4882a593Smuzhiyun 			|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
381*4882a593Smuzhiyun 		/* This device doesn't support ioctl calls */
382*4882a593Smuzhiyun 		return -EINVAL;
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	/* allocate memory for transfer buffer */
386*4882a593Smuzhiyun 	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
387*4882a593Smuzhiyun 	if (!transfer_buffer)
388*4882a593Smuzhiyun 		return -ENOMEM;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	result = usb_control_msg(port->serial->dev,
391*4882a593Smuzhiyun 			  usb_rcvctrlpipe(port->serial->dev, 0),
392*4882a593Smuzhiyun 			  SUSBCRequest_GetStatusLineState,
393*4882a593Smuzhiyun 			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
394*4882a593Smuzhiyun 			  0,
395*4882a593Smuzhiyun 			  0,
396*4882a593Smuzhiyun 			  transfer_buffer,
397*4882a593Smuzhiyun 			  transfer_buffer_length,
398*4882a593Smuzhiyun 			  KOBIL_TIMEOUT);
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n",
401*4882a593Smuzhiyun 			result);
402*4882a593Smuzhiyun 	if (result < 1) {
403*4882a593Smuzhiyun 		if (result >= 0)
404*4882a593Smuzhiyun 			result = -EIO;
405*4882a593Smuzhiyun 		goto out_free;
406*4882a593Smuzhiyun 	}
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	dev_dbg(&port->dev, "Statusline: %02x\n", transfer_buffer[0]);
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	result = 0;
411*4882a593Smuzhiyun 	if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
412*4882a593Smuzhiyun 		result = TIOCM_DSR;
413*4882a593Smuzhiyun out_free:
414*4882a593Smuzhiyun 	kfree(transfer_buffer);
415*4882a593Smuzhiyun 	return result;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun 
kobil_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)418*4882a593Smuzhiyun static int kobil_tiocmset(struct tty_struct *tty,
419*4882a593Smuzhiyun 			   unsigned int set, unsigned int clear)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
422*4882a593Smuzhiyun 	struct device *dev = &port->dev;
423*4882a593Smuzhiyun 	struct kobil_private *priv;
424*4882a593Smuzhiyun 	int result;
425*4882a593Smuzhiyun 	int dtr = 0;
426*4882a593Smuzhiyun 	int rts = 0;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	/* FIXME: locking ? */
429*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
430*4882a593Smuzhiyun 	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
431*4882a593Smuzhiyun 		|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
432*4882a593Smuzhiyun 		/* This device doesn't support ioctl calls */
433*4882a593Smuzhiyun 		return -EINVAL;
434*4882a593Smuzhiyun 	}
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	if (set & TIOCM_RTS)
437*4882a593Smuzhiyun 		rts = 1;
438*4882a593Smuzhiyun 	if (set & TIOCM_DTR)
439*4882a593Smuzhiyun 		dtr = 1;
440*4882a593Smuzhiyun 	if (clear & TIOCM_RTS)
441*4882a593Smuzhiyun 		rts = 0;
442*4882a593Smuzhiyun 	if (clear & TIOCM_DTR)
443*4882a593Smuzhiyun 		dtr = 0;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
446*4882a593Smuzhiyun 		if (dtr != 0)
447*4882a593Smuzhiyun 			dev_dbg(dev, "%s - Setting DTR\n", __func__);
448*4882a593Smuzhiyun 		else
449*4882a593Smuzhiyun 			dev_dbg(dev, "%s - Clearing DTR\n", __func__);
450*4882a593Smuzhiyun 		result = usb_control_msg(port->serial->dev,
451*4882a593Smuzhiyun 			  usb_sndctrlpipe(port->serial->dev, 0),
452*4882a593Smuzhiyun 			  SUSBCRequest_SetStatusLinesOrQueues,
453*4882a593Smuzhiyun 			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
454*4882a593Smuzhiyun 			  ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
455*4882a593Smuzhiyun 			  0,
456*4882a593Smuzhiyun 			  NULL,
457*4882a593Smuzhiyun 			  0,
458*4882a593Smuzhiyun 			  KOBIL_TIMEOUT);
459*4882a593Smuzhiyun 	} else {
460*4882a593Smuzhiyun 		if (rts != 0)
461*4882a593Smuzhiyun 			dev_dbg(dev, "%s - Setting RTS\n", __func__);
462*4882a593Smuzhiyun 		else
463*4882a593Smuzhiyun 			dev_dbg(dev, "%s - Clearing RTS\n", __func__);
464*4882a593Smuzhiyun 		result = usb_control_msg(port->serial->dev,
465*4882a593Smuzhiyun 			usb_sndctrlpipe(port->serial->dev, 0),
466*4882a593Smuzhiyun 			SUSBCRequest_SetStatusLinesOrQueues,
467*4882a593Smuzhiyun 			USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
468*4882a593Smuzhiyun 			((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
469*4882a593Smuzhiyun 			0,
470*4882a593Smuzhiyun 			NULL,
471*4882a593Smuzhiyun 			0,
472*4882a593Smuzhiyun 			KOBIL_TIMEOUT);
473*4882a593Smuzhiyun 	}
474*4882a593Smuzhiyun 	dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result);
475*4882a593Smuzhiyun 	return (result < 0) ? result : 0;
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun 
kobil_set_termios(struct tty_struct * tty,struct usb_serial_port * port,struct ktermios * old)478*4882a593Smuzhiyun static void kobil_set_termios(struct tty_struct *tty,
479*4882a593Smuzhiyun 			struct usb_serial_port *port, struct ktermios *old)
480*4882a593Smuzhiyun {
481*4882a593Smuzhiyun 	struct kobil_private *priv;
482*4882a593Smuzhiyun 	int result;
483*4882a593Smuzhiyun 	unsigned short urb_val = 0;
484*4882a593Smuzhiyun 	int c_cflag = tty->termios.c_cflag;
485*4882a593Smuzhiyun 	speed_t speed;
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	priv = usb_get_serial_port_data(port);
488*4882a593Smuzhiyun 	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
489*4882a593Smuzhiyun 			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
490*4882a593Smuzhiyun 		/* This device doesn't support ioctl calls */
491*4882a593Smuzhiyun 		tty_termios_copy_hw(&tty->termios, old);
492*4882a593Smuzhiyun 		return;
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	speed = tty_get_baud_rate(tty);
496*4882a593Smuzhiyun 	switch (speed) {
497*4882a593Smuzhiyun 	case 1200:
498*4882a593Smuzhiyun 		urb_val = SUSBCR_SBR_1200;
499*4882a593Smuzhiyun 		break;
500*4882a593Smuzhiyun 	default:
501*4882a593Smuzhiyun 		speed = 9600;
502*4882a593Smuzhiyun 		fallthrough;
503*4882a593Smuzhiyun 	case 9600:
504*4882a593Smuzhiyun 		urb_val = SUSBCR_SBR_9600;
505*4882a593Smuzhiyun 		break;
506*4882a593Smuzhiyun 	}
507*4882a593Smuzhiyun 	urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits :
508*4882a593Smuzhiyun 							SUSBCR_SPASB_1StopBit;
509*4882a593Smuzhiyun 	if (c_cflag & PARENB) {
510*4882a593Smuzhiyun 		if  (c_cflag & PARODD)
511*4882a593Smuzhiyun 			urb_val |= SUSBCR_SPASB_OddParity;
512*4882a593Smuzhiyun 		else
513*4882a593Smuzhiyun 			urb_val |= SUSBCR_SPASB_EvenParity;
514*4882a593Smuzhiyun 	} else
515*4882a593Smuzhiyun 		urb_val |= SUSBCR_SPASB_NoParity;
516*4882a593Smuzhiyun 	tty->termios.c_cflag &= ~CMSPAR;
517*4882a593Smuzhiyun 	tty_encode_baud_rate(tty, speed, speed);
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	result = usb_control_msg(port->serial->dev,
520*4882a593Smuzhiyun 		  usb_sndctrlpipe(port->serial->dev, 0),
521*4882a593Smuzhiyun 		  SUSBCRequest_SetBaudRateParityAndStopBits,
522*4882a593Smuzhiyun 		  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
523*4882a593Smuzhiyun 		  urb_val,
524*4882a593Smuzhiyun 		  0,
525*4882a593Smuzhiyun 		  NULL,
526*4882a593Smuzhiyun 		  0,
527*4882a593Smuzhiyun 		  KOBIL_TIMEOUT
528*4882a593Smuzhiyun 		);
529*4882a593Smuzhiyun 	if (result) {
530*4882a593Smuzhiyun 		dev_err(&port->dev, "failed to update line settings: %d\n",
531*4882a593Smuzhiyun 				result);
532*4882a593Smuzhiyun 	}
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun 
kobil_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)535*4882a593Smuzhiyun static int kobil_ioctl(struct tty_struct *tty,
536*4882a593Smuzhiyun 					unsigned int cmd, unsigned long arg)
537*4882a593Smuzhiyun {
538*4882a593Smuzhiyun 	struct usb_serial_port *port = tty->driver_data;
539*4882a593Smuzhiyun 	struct kobil_private *priv = usb_get_serial_port_data(port);
540*4882a593Smuzhiyun 	int result;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
543*4882a593Smuzhiyun 			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
544*4882a593Smuzhiyun 		/* This device doesn't support ioctl calls */
545*4882a593Smuzhiyun 		return -ENOIOCTLCMD;
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 	switch (cmd) {
548*4882a593Smuzhiyun 	case TCFLSH:
549*4882a593Smuzhiyun 		result = usb_control_msg(port->serial->dev,
550*4882a593Smuzhiyun 			  usb_sndctrlpipe(port->serial->dev, 0),
551*4882a593Smuzhiyun 			  SUSBCRequest_Misc,
552*4882a593Smuzhiyun 			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
553*4882a593Smuzhiyun 			  SUSBCR_MSC_ResetAllQueues,
554*4882a593Smuzhiyun 			  0,
555*4882a593Smuzhiyun 			  NULL,
556*4882a593Smuzhiyun 			  0,
557*4882a593Smuzhiyun 			  KOBIL_TIMEOUT
558*4882a593Smuzhiyun 			);
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 		dev_dbg(&port->dev,
561*4882a593Smuzhiyun 			"%s - Send reset_all_queues (FLUSH) URB returns: %i\n",
562*4882a593Smuzhiyun 			__func__, result);
563*4882a593Smuzhiyun 		return (result < 0) ? -EIO: 0;
564*4882a593Smuzhiyun 	default:
565*4882a593Smuzhiyun 		return -ENOIOCTLCMD;
566*4882a593Smuzhiyun 	}
567*4882a593Smuzhiyun }
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun module_usb_serial_driver(serial_drivers, id_table);
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun MODULE_AUTHOR(DRIVER_AUTHOR);
572*4882a593Smuzhiyun MODULE_DESCRIPTION(DRIVER_DESC);
573*4882a593Smuzhiyun MODULE_LICENSE("GPL");
574