blob: 5c4d2fbd4e11892e7c40479b322c3f56e74e7294 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * KOBIL USB Smart Card Terminal Driver
3 *
Alan Coxdee0a7c2008-07-22 11:14:10 +01004 * Copyright (C) 2002 KOBIL Systems GmbH
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author: Thomas Wahrenbruch
6 *
7 * Contact: linuxusb@kobil.de
8 *
9 * This program is largely derived from work by the linux-usb group
10 * and associated source files. Please see the usb/serial files for
11 * individual credits and copyrights.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
19 * patience.
20 *
21 * Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus
22 * (Adapter K), B1 Professional and KAAN Professional (Adapter B)
Linus Torvalds1da177e2005-04-16 15:20:36 -070023 */
24
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/kernel.h>
27#include <linux/errno.h>
28#include <linux/init.h>
29#include <linux/slab.h>
30#include <linux/tty.h>
31#include <linux/tty_driver.h>
32#include <linux/tty_flip.h>
33#include <linux/module.h>
34#include <linux/spinlock.h>
Alan Coxdee0a7c2008-07-22 11:14:10 +010035#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/usb.h>
Greg Kroah-Hartmana9698882006-07-11 21:22:58 -070037#include <linux/usb/serial.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <linux/ioctl.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "kobil_sct.h"
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041/* Version Information */
42#define DRIVER_VERSION "21/05/2004"
43#define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com"
44#define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)"
45
46#define KOBIL_VENDOR_ID 0x0D46
47#define KOBIL_ADAPTER_B_PRODUCT_ID 0x2011
48#define KOBIL_ADAPTER_K_PRODUCT_ID 0x2012
49#define KOBIL_USBTWIN_PRODUCT_ID 0x0078
50#define KOBIL_KAAN_SIM_PRODUCT_ID 0x0081
51
52#define KOBIL_TIMEOUT 500
53#define KOBIL_BUF_LENGTH 300
54
55
56/* Function prototypes */
Alan Coxdee0a7c2008-07-22 11:14:10 +010057static int kobil_startup(struct usb_serial *serial);
Alan Sternf9c99bb2009-06-02 11:53:55 -040058static void kobil_release(struct usb_serial *serial);
Alan Coxa509a7e2009-09-19 13:13:26 -070059static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
Alan Cox335f8512009-06-11 12:26:29 +010060static void kobil_close(struct usb_serial_port *port);
Alan Coxdee0a7c2008-07-22 11:14:10 +010061static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 const unsigned char *buf, int count);
Alan Cox95da3102008-07-22 11:09:07 +010063static int kobil_write_room(struct tty_struct *tty);
Alan Cox00a0d0d2011-02-14 16:27:06 +000064static int kobil_ioctl(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 unsigned int cmd, unsigned long arg);
Alan Cox60b33c12011-02-14 16:26:14 +000066static int kobil_tiocmget(struct tty_struct *tty);
Alan Cox20b9d172011-02-14 16:26:50 +000067static int kobil_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 unsigned int set, unsigned int clear);
Alan Coxdee0a7c2008-07-22 11:14:10 +010069static void kobil_read_int_callback(struct urb *urb);
70static void kobil_write_callback(struct urb *purb);
71static void kobil_set_termios(struct tty_struct *tty,
Alan Cox95da3102008-07-22 11:09:07 +010072 struct usb_serial_port *port, struct ktermios *old);
Alan Coxfe1ae7f2009-09-19 13:13:33 -070073static void kobil_init_termios(struct tty_struct *tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
Németh Márton7d40d7e2010-01-10 15:34:24 +010075static const struct usb_device_id id_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) },
77 { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_K_PRODUCT_ID) },
78 { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) },
79 { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_KAAN_SIM_PRODUCT_ID) },
80 { } /* Terminating entry */
81};
Alan Coxdee0a7c2008-07-22 11:14:10 +010082MODULE_DEVICE_TABLE(usb, id_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -070084static struct usb_serial_driver kobil_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -070085 .driver = {
86 .owner = THIS_MODULE,
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -070087 .name = "kobil",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -070088 },
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -070089 .description = "KOBIL USB smart card terminal",
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 .id_table = id_table,
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 .num_ports = 1,
92 .attach = kobil_startup,
Alan Sternf9c99bb2009-06-02 11:53:55 -040093 .release = kobil_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 .ioctl = kobil_ioctl,
Alan Cox94d0f7e2007-08-22 23:09:16 +010095 .set_termios = kobil_set_termios,
Alan Coxfe1ae7f2009-09-19 13:13:33 -070096 .init_termios = kobil_init_termios,
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 .tiocmget = kobil_tiocmget,
98 .tiocmset = kobil_tiocmset,
99 .open = kobil_open,
100 .close = kobil_close,
101 .write = kobil_write,
102 .write_room = kobil_write_room,
103 .read_int_callback = kobil_read_int_callback,
104};
105
Alan Stern4d2a7af2012-02-23 14:57:09 -0500106static struct usb_serial_driver * const serial_drivers[] = {
107 &kobil_device, NULL
108};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
110struct kobil_private {
111 int write_int_endpoint_address;
112 int read_int_endpoint_address;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100113 unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */
114 int filled; /* index of the last char in buf */
115 int cur_pos; /* index of the next char to send in buf */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 __u16 device_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117};
118
119
Alan Coxdee0a7c2008-07-22 11:14:10 +0100120static int kobil_startup(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121{
122 int i;
123 struct kobil_private *priv;
124 struct usb_device *pdev;
125 struct usb_host_config *actconfig;
126 struct usb_interface *interface;
127 struct usb_host_interface *altsetting;
128 struct usb_host_endpoint *endpoint;
129
130 priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100131 if (!priv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
134 priv->filled = 0;
135 priv->cur_pos = 0;
136 priv->device_type = le16_to_cpu(serial->dev->descriptor.idProduct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
Alan Coxdee0a7c2008-07-22 11:14:10 +0100138 switch (priv->device_type) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 case KOBIL_ADAPTER_B_PRODUCT_ID:
Greg Kroah-Hartman6c27ad82012-09-18 17:03:31 +0100140 dev_dbg(&serial->dev->dev, "KOBIL B1 PRO / KAAN PRO detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 break;
142 case KOBIL_ADAPTER_K_PRODUCT_ID:
Greg Kroah-Hartman6c27ad82012-09-18 17:03:31 +0100143 dev_dbg(&serial->dev->dev, "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 break;
145 case KOBIL_USBTWIN_PRODUCT_ID:
Greg Kroah-Hartman6c27ad82012-09-18 17:03:31 +0100146 dev_dbg(&serial->dev->dev, "KOBIL USBTWIN detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 break;
148 case KOBIL_KAAN_SIM_PRODUCT_ID:
Greg Kroah-Hartman6c27ad82012-09-18 17:03:31 +0100149 dev_dbg(&serial->dev->dev, "KOBIL KAAN SIM detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 break;
151 }
152 usb_set_serial_port_data(serial->port[0], priv);
153
Alan Coxdee0a7c2008-07-22 11:14:10 +0100154 /* search for the necessary endpoints */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 pdev = serial->dev;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100156 actconfig = pdev->actconfig;
157 interface = actconfig->interface[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 altsetting = interface->cur_altsetting;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100159 endpoint = altsetting->endpoint;
160
161 for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 endpoint = &altsetting->endpoint[i];
Luiz Fernando N. Capitulino4f1f1dd2006-10-26 13:02:53 -0300163 if (usb_endpoint_is_int_out(&endpoint->desc)) {
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700164 dev_dbg(&serial->dev->dev,
165 "%s Found interrupt out endpoint. Address: %d\n",
Alan Coxdee0a7c2008-07-22 11:14:10 +0100166 __func__, endpoint->desc.bEndpointAddress);
167 priv->write_int_endpoint_address =
168 endpoint->desc.bEndpointAddress;
169 }
Luiz Fernando N. Capitulino4f1f1dd2006-10-26 13:02:53 -0300170 if (usb_endpoint_is_int_in(&endpoint->desc)) {
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700171 dev_dbg(&serial->dev->dev,
172 "%s Found interrupt in endpoint. Address: %d\n",
Alan Coxdee0a7c2008-07-22 11:14:10 +0100173 __func__, endpoint->desc.bEndpointAddress);
174 priv->read_int_endpoint_address =
175 endpoint->desc.bEndpointAddress;
176 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 }
178 return 0;
179}
180
181
Alan Sternf9c99bb2009-06-02 11:53:55 -0400182static void kobil_release(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183{
184 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Alan Sternf9c99bb2009-06-02 11:53:55 -0400186 for (i = 0; i < serial->num_ports; ++i)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 kfree(usb_get_serial_port_data(serial->port[i]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188}
189
Alan Coxfe1ae7f2009-09-19 13:13:33 -0700190static void kobil_init_termios(struct tty_struct *tty)
191{
192 /* Default to echo off and other sane device settings */
Alan Coxadc8d742012-07-14 15:31:47 +0100193 tty->termios.c_lflag = 0;
Alan Cox6a6c8b32012-07-14 15:32:50 +0100194 tty->termios.c_iflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
195 tty->termios.c_iflag |= IGNBRK | IGNPAR | IXOFF;
Alan Coxfe1ae7f2009-09-19 13:13:33 -0700196 /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
Alan Coxadc8d742012-07-14 15:31:47 +0100197 tty->termios.c_oflag &= ~ONLCR;
Alan Coxfe1ae7f2009-09-19 13:13:33 -0700198}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
Alan Coxa509a7e2009-09-19 13:13:26 -0700200static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201{
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700202 struct device *dev = &port->dev;
Alan Cox94d0f7e2007-08-22 23:09:16 +0100203 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 struct kobil_private *priv;
205 unsigned char *transfer_buffer;
206 int transfer_buffer_length = 8;
207 int write_urb_transfer_buffer_length = 8;
208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 priv = usb_get_serial_port_data(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
Alan Coxdee0a7c2008-07-22 11:14:10 +0100211 /* allocate memory for transfer buffer */
212 transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
213 if (!transfer_buffer)
214 return -ENOMEM;
215
216 /* allocate write_urb */
217 if (!port->write_urb) {
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700218 dev_dbg(dev, "%s - Allocating port->write_urb\n", __func__);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100219 port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 if (!port->write_urb) {
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700221 dev_dbg(dev, "%s - usb_alloc_urb failed\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 kfree(transfer_buffer);
223 return -ENOMEM;
224 }
225 }
226
Alan Coxdee0a7c2008-07-22 11:14:10 +0100227 /* allocate memory for write_urb transfer buffer */
228 port->write_urb->transfer_buffer =
229 kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL);
230 if (!port->write_urb->transfer_buffer) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 kfree(transfer_buffer);
232 usb_free_urb(port->write_urb);
233 port->write_urb = NULL;
234 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 }
Alan Coxdee0a7c2008-07-22 11:14:10 +0100236
237 /* get hardware version */
238 result = usb_control_msg(port->serial->dev,
239 usb_rcvctrlpipe(port->serial->dev, 0),
240 SUSBCRequest_GetMisc,
241 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
242 SUSBCR_MSC_GetHWVersion,
243 0,
244 transfer_buffer,
245 transfer_buffer_length,
246 KOBIL_TIMEOUT
247 );
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700248 dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
249 dev_dbg(dev, "Harware version: %i.%i.%i\n", transfer_buffer[0],
250 transfer_buffer[1], transfer_buffer[2]);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100251
252 /* get firmware version */
253 result = usb_control_msg(port->serial->dev,
254 usb_rcvctrlpipe(port->serial->dev, 0),
255 SUSBCRequest_GetMisc,
256 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
257 SUSBCR_MSC_GetFWVersion,
258 0,
259 transfer_buffer,
260 transfer_buffer_length,
261 KOBIL_TIMEOUT
262 );
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700263 dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
264 dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
265 transfer_buffer[1], transfer_buffer[2]);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100266
267 if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
268 priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
269 /* Setting Baudrate, Parity and Stopbits */
270 result = usb_control_msg(port->serial->dev,
271 usb_rcvctrlpipe(port->serial->dev, 0),
272 SUSBCRequest_SetBaudRateParityAndStopBits,
273 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
274 SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
275 SUSBCR_SPASB_1StopBit,
276 0,
277 transfer_buffer,
278 0,
279 KOBIL_TIMEOUT
280 );
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700281 dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100282
283 /* reset all queues */
284 result = usb_control_msg(port->serial->dev,
285 usb_rcvctrlpipe(port->serial->dev, 0),
286 SUSBCRequest_Misc,
287 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
288 SUSBCR_MSC_ResetAllQueues,
289 0,
290 transfer_buffer,
291 0,
292 KOBIL_TIMEOUT
293 );
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700294 dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100295 }
296 if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
297 priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
Alan Coxdee0a7c2008-07-22 11:14:10 +0100299 /* start reading (Adapter B 'cause PNP string) */
300 result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700301 dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 }
303
304 kfree(transfer_buffer);
305 return 0;
306}
307
308
Alan Cox335f8512009-06-11 12:26:29 +0100309static void kobil_close(struct usb_serial_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310{
Alan Cox335f8512009-06-11 12:26:29 +0100311 /* FIXME: Add rts/dtr methods */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 if (port->write_urb) {
Johan Hovoldc0f631d2010-05-15 17:53:43 +0200313 usb_poison_urb(port->write_urb);
314 kfree(port->write_urb->transfer_buffer);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100315 usb_free_urb(port->write_urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 port->write_urb = NULL;
317 }
Mariusz Kozlowski5505c222006-11-08 15:36:38 +0100318 usb_kill_urb(port->interrupt_in_urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319}
320
321
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700322static void kobil_read_int_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 int result;
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700325 struct usb_serial_port *port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 struct tty_struct *tty;
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700327 unsigned char *data = urb->transfer_buffer;
328 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700330 if (status) {
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700331 dev_dbg(&port->dev, "%s - Read int status not zero: %d\n", __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 return;
333 }
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700334
Alan Cox4a90f092008-10-13 10:39:46 +0100335 tty = tty_port_tty_get(&port->port);
Jiri Slaby6960f402011-02-28 10:34:06 +0100336 if (tty && urb->actual_length) {
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700337
Alan Coxdee0a7c2008-07-22 11:14:10 +0100338 /* BEGIN DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 /*
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700340 char *dbg_data;
341
Alan Coxdee0a7c2008-07-22 11:14:10 +0100342 dbg_data = kzalloc((3 * purb->actual_length + 10)
343 * sizeof(char), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 if (! dbg_data) {
Alan Coxdee0a7c2008-07-22 11:14:10 +0100345 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 }
Alan Coxdee0a7c2008-07-22 11:14:10 +0100347 for (i = 0; i < purb->actual_length; i++) {
348 sprintf(dbg_data +3*i, "%02X ", data[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 }
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700350 dev_dbg(&port->dev, " <-- %s\n", dbg_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 kfree(dbg_data);
352 */
Alan Coxdee0a7c2008-07-22 11:14:10 +0100353 /* END DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700355 tty_insert_flip_string(tty, data, urb->actual_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 tty_flip_buffer_push(tty);
357 }
Alan Cox4a90f092008-10-13 10:39:46 +0100358 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Greg Kroah-Hartman6fcdcf02007-06-15 15:44:13 -0700360 result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700361 dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362}
363
364
Alan Coxdee0a7c2008-07-22 11:14:10 +0100365static void kobil_write_callback(struct urb *purb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366{
367}
368
369
Alan Coxdee0a7c2008-07-22 11:14:10 +0100370static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 const unsigned char *buf, int count)
372{
373 int length = 0;
374 int result = 0;
375 int todo = 0;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100376 struct kobil_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378 if (count == 0) {
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700379 dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 return 0;
381 }
382
383 priv = usb_get_serial_port_data(port);
384
385 if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700386 dev_dbg(&port->dev, "%s - Error: write request bigger than buffer size\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 return -ENOMEM;
388 }
389
Alan Coxdee0a7c2008-07-22 11:14:10 +0100390 /* Copy data to buffer */
391 memcpy(priv->buf + priv->filled, buf, count);
Greg Kroah-Hartman59d33f22012-09-18 09:58:57 +0100392 usb_serial_debug_data(&port->dev, __func__, count, priv->buf + priv->filled);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 priv->filled = priv->filled + count;
394
Alan Coxdee0a7c2008-07-22 11:14:10 +0100395 /* only send complete block. TWIN, KAAN SIM and adapter K
396 use the same protocol. */
397 if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) ||
398 ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) {
399 /* stop reading (except TWIN and KAAN SIM) */
400 if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID)
401 || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 usb_kill_urb(port->interrupt_in_urb);
403
404 todo = priv->filled - priv->cur_pos;
405
Alan Coxdee0a7c2008-07-22 11:14:10 +0100406 while (todo > 0) {
407 /* max 8 byte in one urb (endpoint size) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 length = (todo < 8) ? todo : 8;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100409 /* copy data to transfer buffer */
410 memcpy(port->write_urb->transfer_buffer,
411 priv->buf + priv->cur_pos, length);
412 usb_fill_int_urb(port->write_urb,
413 port->serial->dev,
414 usb_sndintpipe(port->serial->dev,
415 priv->write_int_endpoint_address),
416 port->write_urb->transfer_buffer,
417 length,
418 kobil_write_callback,
419 port,
420 8
421 );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
423 priv->cur_pos = priv->cur_pos + length;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100424 result = usb_submit_urb(port->write_urb, GFP_NOIO);
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700425 dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 todo = priv->filled - priv->cur_pos;
427
Alan Coxdee0a7c2008-07-22 11:14:10 +0100428 if (todo > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 msleep(24);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100430 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 priv->filled = 0;
433 priv->cur_pos = 0;
434
Alan Coxdee0a7c2008-07-22 11:14:10 +0100435 /* start reading (except TWIN and KAAN SIM) */
436 if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
437 priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
Alan Coxdee0a7c2008-07-22 11:14:10 +0100438 result = usb_submit_urb(port->interrupt_in_urb,
439 GFP_NOIO);
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700440 dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 }
442 }
443 return count;
444}
445
446
Alan Coxdee0a7c2008-07-22 11:14:10 +0100447static int kobil_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
Alan Cox95da3102008-07-22 11:09:07 +0100449 /* FIXME */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 return 8;
451}
452
453
Alan Cox60b33c12011-02-14 16:26:14 +0000454static int kobil_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455{
Alan Cox95da3102008-07-22 11:09:07 +0100456 struct usb_serial_port *port = tty->driver_data;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100457 struct kobil_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 int result;
459 unsigned char *transfer_buffer;
460 int transfer_buffer_length = 8;
461
462 priv = usb_get_serial_port_data(port);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100463 if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
464 || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
465 /* This device doesn't support ioctl calls */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 return -EINVAL;
467 }
468
Alan Coxdee0a7c2008-07-22 11:14:10 +0100469 /* allocate memory for transfer buffer */
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100470 transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100471 if (!transfer_buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
Alan Coxdee0a7c2008-07-22 11:14:10 +0100474 result = usb_control_msg(port->serial->dev,
475 usb_rcvctrlpipe(port->serial->dev, 0),
476 SUSBCRequest_GetStatusLineState,
477 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
478 0,
479 0,
480 transfer_buffer,
481 transfer_buffer_length,
482 KOBIL_TIMEOUT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700484 dev_dbg(&port->dev, "%s - Send get_status_line_state URB returns: %i. Statusline: %02x\n",
485 __func__, result, transfer_buffer[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
Alan Coxa40d8542008-02-20 21:40:34 +0000487 result = 0;
488 if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
489 result = TIOCM_DSR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 kfree(transfer_buffer);
Alan Coxa40d8542008-02-20 21:40:34 +0000491 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492}
493
Alan Cox20b9d172011-02-14 16:26:50 +0000494static int kobil_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 unsigned int set, unsigned int clear)
496{
Alan Cox95da3102008-07-22 11:09:07 +0100497 struct usb_serial_port *port = tty->driver_data;
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700498 struct device *dev = &port->dev;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100499 struct kobil_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 int result;
501 int dtr = 0;
502 int rts = 0;
503 unsigned char *transfer_buffer;
504 int transfer_buffer_length = 8;
505
Alan Coxa40d8542008-02-20 21:40:34 +0000506 /* FIXME: locking ? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 priv = usb_get_serial_port_data(port);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100508 if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
509 || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
510 /* This device doesn't support ioctl calls */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 return -EINVAL;
512 }
513
Alan Coxdee0a7c2008-07-22 11:14:10 +0100514 /* allocate memory for transfer buffer */
Eric Sesterhenn80b6ca42006-02-27 21:29:43 +0100515 transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100516 if (!transfer_buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
519 if (set & TIOCM_RTS)
520 rts = 1;
521 if (set & TIOCM_DTR)
522 dtr = 1;
523 if (clear & TIOCM_RTS)
524 rts = 0;
525 if (clear & TIOCM_DTR)
526 dtr = 0;
527
528 if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
529 if (dtr != 0)
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700530 dev_dbg(dev, "%s - Setting DTR\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 else
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700532 dev_dbg(dev, "%s - Clearing DTR\n", __func__);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100533 result = usb_control_msg(port->serial->dev,
534 usb_rcvctrlpipe(port->serial->dev, 0),
535 SUSBCRequest_SetStatusLinesOrQueues,
536 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
537 ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
538 0,
539 transfer_buffer,
540 0,
541 KOBIL_TIMEOUT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 } else {
543 if (rts != 0)
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700544 dev_dbg(dev, "%s - Setting RTS\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 else
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700546 dev_dbg(dev, "%s - Clearing RTS\n", __func__);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100547 result = usb_control_msg(port->serial->dev,
548 usb_rcvctrlpipe(port->serial->dev, 0),
549 SUSBCRequest_SetStatusLinesOrQueues,
550 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
551 ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
552 0,
553 transfer_buffer,
554 0,
555 KOBIL_TIMEOUT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 }
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700557 dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 kfree(transfer_buffer);
559 return (result < 0) ? result : 0;
560}
561
Alan Cox95da3102008-07-22 11:09:07 +0100562static void kobil_set_termios(struct tty_struct *tty,
563 struct usb_serial_port *port, struct ktermios *old)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Alan Coxdee0a7c2008-07-22 11:14:10 +0100565 struct kobil_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 int result;
567 unsigned short urb_val = 0;
Alan Coxadc8d742012-07-14 15:31:47 +0100568 int c_cflag = tty->termios.c_cflag;
Alan Cox94d0f7e2007-08-22 23:09:16 +0100569 speed_t speed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
571 priv = usb_get_serial_port_data(port);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100572 if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
Alan Coxb31f6582008-07-22 11:14:22 +0100573 priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
Alan Coxdee0a7c2008-07-22 11:14:10 +0100574 /* This device doesn't support ioctl calls */
Alan Cox6a6c8b32012-07-14 15:32:50 +0100575 tty_termios_copy_hw(&tty->termios, old);
Alan Cox94d0f7e2007-08-22 23:09:16 +0100576 return;
Alan Coxb31f6582008-07-22 11:14:22 +0100577 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
Alan Coxdee0a7c2008-07-22 11:14:10 +0100579 speed = tty_get_baud_rate(tty);
580 switch (speed) {
581 case 1200:
582 urb_val = SUSBCR_SBR_1200;
583 break;
584 default:
585 speed = 9600;
586 case 9600:
587 urb_val = SUSBCR_SBR_9600;
588 break;
Alan Cox94d0f7e2007-08-22 23:09:16 +0100589 }
Alan Coxdee0a7c2008-07-22 11:14:10 +0100590 urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits :
591 SUSBCR_SPASB_1StopBit;
Alan Cox94d0f7e2007-08-22 23:09:16 +0100592 if (c_cflag & PARENB) {
Johan Hovold96679f62009-12-28 23:01:58 +0100593 if (c_cflag & PARODD)
Alan Cox94d0f7e2007-08-22 23:09:16 +0100594 urb_val |= SUSBCR_SPASB_OddParity;
Johan Hovold96679f62009-12-28 23:01:58 +0100595 else
Alan Cox94d0f7e2007-08-22 23:09:16 +0100596 urb_val |= SUSBCR_SPASB_EvenParity;
Johan Hovold96679f62009-12-28 23:01:58 +0100597 } else
Alan Cox94d0f7e2007-08-22 23:09:16 +0100598 urb_val |= SUSBCR_SPASB_NoParity;
Alan Coxadc8d742012-07-14 15:31:47 +0100599 tty->termios.c_cflag &= ~CMSPAR;
Alan Cox95da3102008-07-22 11:09:07 +0100600 tty_encode_baud_rate(tty, speed, speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
Alan Coxdee0a7c2008-07-22 11:14:10 +0100602 result = usb_control_msg(port->serial->dev,
603 usb_rcvctrlpipe(port->serial->dev, 0),
604 SUSBCRequest_SetBaudRateParityAndStopBits,
605 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
606 urb_val,
607 0,
Johan Hovold96679f62009-12-28 23:01:58 +0100608 NULL,
Alan Coxdee0a7c2008-07-22 11:14:10 +0100609 0,
610 KOBIL_TIMEOUT
Alan Cox94d0f7e2007-08-22 23:09:16 +0100611 );
Alan Cox94d0f7e2007-08-22 23:09:16 +0100612}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Alan Cox00a0d0d2011-02-14 16:27:06 +0000614static int kobil_ioctl(struct tty_struct *tty,
Alan Coxdee0a7c2008-07-22 11:14:10 +0100615 unsigned int cmd, unsigned long arg)
Alan Cox94d0f7e2007-08-22 23:09:16 +0100616{
Alan Cox95da3102008-07-22 11:09:07 +0100617 struct usb_serial_port *port = tty->driver_data;
Alan Coxdee0a7c2008-07-22 11:14:10 +0100618 struct kobil_private *priv = usb_get_serial_port_data(port);
Alan Cox94d0f7e2007-08-22 23:09:16 +0100619 unsigned char *transfer_buffer;
620 int transfer_buffer_length = 8;
621 int result;
622
Alan Coxdee0a7c2008-07-22 11:14:10 +0100623 if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
624 priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
625 /* This device doesn't support ioctl calls */
Alan Coxb31f6582008-07-22 11:14:22 +0100626 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Alan Cox94d0f7e2007-08-22 23:09:16 +0100628 switch (cmd) {
Alan Cox95da3102008-07-22 11:09:07 +0100629 case TCFLSH:
Robert P. J. Day5cbded52006-12-13 00:35:56 -0800630 transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL);
Alan Coxdee0a7c2008-07-22 11:14:10 +0100631 if (!transfer_buffer)
632 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
Alan Coxdee0a7c2008-07-22 11:14:10 +0100634 result = usb_control_msg(port->serial->dev,
635 usb_rcvctrlpipe(port->serial->dev, 0),
636 SUSBCRequest_Misc,
637 USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
638 SUSBCR_MSC_ResetAllQueues,
639 0,
640 NULL, /* transfer_buffer, */
641 0,
642 KOBIL_TIMEOUT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 );
Alan Coxdee0a7c2008-07-22 11:14:10 +0100644
Greg Kroah-Hartmanb12f7a12012-09-14 12:06:55 -0700645 dev_dbg(&port->dev,
646 "%s - Send reset_all_queues (FLUSH) URB returns: %i", __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 kfree(transfer_buffer);
Alan Cox95da3102008-07-22 11:09:07 +0100648 return (result < 0) ? -EIO: 0;
Alan Cox94d0f7e2007-08-22 23:09:16 +0100649 default:
650 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652}
653
Greg Kroah-Hartman68e24112012-05-08 15:46:14 -0700654module_usb_serial_driver(serial_drivers, id_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Alan Coxdee0a7c2008-07-22 11:14:10 +0100656MODULE_AUTHOR(DRIVER_AUTHOR);
657MODULE_DESCRIPTION(DRIVER_DESC);
658MODULE_LICENSE("GPL");