kobil_sct.c 20.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3
/*
 *  KOBIL USB Smart Card Terminal Driver
 *
Alan Cox's avatar
Alan Cox committed
4
 *  Copyright (C) 2002  KOBIL Systems GmbH
Linus Torvalds's avatar
Linus Torvalds committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *  Author: Thomas Wahrenbruch
 *
 *  Contact: linuxusb@kobil.de
 *
 *  This program is largely derived from work by the linux-usb group
 *  and associated source files.  Please see the usb/serial files for
 *  individual credits and copyrights.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Thanks to Greg Kroah-Hartman (greg@kroah.com) for his help and
 *  patience.
 *
 * Supported readers: USB TWIN, KAAN Standard Plus and SecOVID Reader Plus
 * (Adapter K), B1 Professional and KAAN Professional (Adapter B)
Alan Cox's avatar
Alan Cox committed
23
 *
Linus Torvalds's avatar
Linus Torvalds committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
 * (21/05/2004) tw
 *      Fix bug with P'n'P readers
 *
 * (28/05/2003) tw
 *      Add support for KAAN SIM
 *
 * (12/09/2002) tw
 *      Adapted to 2.5.
 *
 * (11/08/2002) tw
 *      Initial version.
 */


#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
Alan Cox's avatar
Alan Cox committed
47
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
48
#include <linux/usb.h>
49
#include <linux/usb/serial.h>
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
#include <linux/ioctl.h>
#include "kobil_sct.h"

static int debug;

/* Version Information */
#define DRIVER_VERSION "21/05/2004"
#define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com"
#define DRIVER_DESC "KOBIL USB Smart Card Terminal Driver (experimental)"

#define KOBIL_VENDOR_ID			0x0D46
#define KOBIL_ADAPTER_B_PRODUCT_ID	0x2011
#define KOBIL_ADAPTER_K_PRODUCT_ID	0x2012
#define KOBIL_USBTWIN_PRODUCT_ID	0x0078
#define KOBIL_KAAN_SIM_PRODUCT_ID       0x0081

#define KOBIL_TIMEOUT		500
#define KOBIL_BUF_LENGTH	300


/* Function prototypes */
Alan Cox's avatar
Alan Cox committed
71 72 73
static int  kobil_startup(struct usb_serial *serial);
static void kobil_shutdown(struct usb_serial *serial);
static int  kobil_open(struct tty_struct *tty,
Alan Cox's avatar
Alan Cox committed
74
			struct usb_serial_port *port, struct file *filp);
Alan Cox's avatar
Alan Cox committed
75
static void kobil_close(struct tty_struct *tty, struct usb_serial_port *port,
Alan Cox's avatar
Alan Cox committed
76
			struct file *filp);
Alan Cox's avatar
Alan Cox committed
77
static int  kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
Linus Torvalds's avatar
Linus Torvalds committed
78
			 const unsigned char *buf, int count);
Alan Cox's avatar
Alan Cox committed
79 80
static int  kobil_write_room(struct tty_struct *tty);
static int  kobil_ioctl(struct tty_struct *tty, struct file *file,
Linus Torvalds's avatar
Linus Torvalds committed
81
			unsigned int cmd, unsigned long arg);
Alan Cox's avatar
Alan Cox committed
82 83
static int  kobil_tiocmget(struct tty_struct *tty, struct file *file);
static int  kobil_tiocmset(struct tty_struct *tty, struct file *file,
Linus Torvalds's avatar
Linus Torvalds committed
84
			   unsigned int set, unsigned int clear);
Alan Cox's avatar
Alan Cox committed
85 86 87
static void kobil_read_int_callback(struct urb *urb);
static void kobil_write_callback(struct urb *purb);
static void kobil_set_termios(struct tty_struct *tty,
Alan Cox's avatar
Alan Cox committed
88
			struct usb_serial_port *port, struct ktermios *old);
Linus Torvalds's avatar
Linus Torvalds committed
89 90 91 92 93 94 95 96 97 98 99


static struct usb_device_id id_table [] = {
	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) },
	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_K_PRODUCT_ID) },
	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) },
	{ USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_KAAN_SIM_PRODUCT_ID) },
	{ }			/* Terminating entry */
};


Alan Cox's avatar
Alan Cox committed
100
MODULE_DEVICE_TABLE(usb, id_table);
Linus Torvalds's avatar
Linus Torvalds committed
101 102 103 104 105 106

static struct usb_driver kobil_driver = {
	.name =		"kobil",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	id_table,
107
	.no_dynamic_id = 	1,
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110
};


111
static struct usb_serial_driver kobil_device = {
112 113
	.driver = {
		.owner =	THIS_MODULE,
114
		.name =		"kobil",
115
	},
116
	.description =		"KOBIL USB smart card terminal",
117
	.usb_driver = 		&kobil_driver,
Linus Torvalds's avatar
Linus Torvalds committed
118 119 120 121 122
	.id_table =		id_table,
	.num_ports =		1,
	.attach =		kobil_startup,
	.shutdown =		kobil_shutdown,
	.ioctl =		kobil_ioctl,
Alan Cox's avatar
Alan Cox committed
123
	.set_termios =		kobil_set_termios,
Linus Torvalds's avatar
Linus Torvalds committed
124 125 126 127 128 129 130 131 132 133 134 135 136
	.tiocmget =		kobil_tiocmget,
	.tiocmset =		kobil_tiocmset,
	.open =			kobil_open,
	.close =		kobil_close,
	.write =		kobil_write,
	.write_room =		kobil_write_room,
	.read_int_callback =	kobil_read_int_callback,
};


struct kobil_private {
	int write_int_endpoint_address;
	int read_int_endpoint_address;
Alan Cox's avatar
Alan Cox committed
137 138 139
	unsigned char buf[KOBIL_BUF_LENGTH]; /* buffer for the APDU to send */
	int filled;  /* index of the last char in buf */
	int cur_pos; /* index of the next char to send in buf */
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142 143
	__u16 device_type;
};


Alan Cox's avatar
Alan Cox committed
144
static int kobil_startup(struct usb_serial *serial)
Linus Torvalds's avatar
Linus Torvalds committed
145 146 147 148 149 150 151 152 153 154
{
	int i;
	struct kobil_private *priv;
	struct usb_device *pdev;
	struct usb_host_config *actconfig;
	struct usb_interface *interface;
	struct usb_host_interface *altsetting;
	struct usb_host_endpoint *endpoint;

	priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL);
Alan Cox's avatar
Alan Cox committed
155
	if (!priv)
Linus Torvalds's avatar
Linus Torvalds committed
156 157 158 159 160 161
		return -ENOMEM;

	priv->filled = 0;
	priv->cur_pos = 0;
	priv->device_type = le16_to_cpu(serial->dev->descriptor.idProduct);

Alan Cox's avatar
Alan Cox committed
162
	switch (priv->device_type) {
Linus Torvalds's avatar
Linus Torvalds committed
163 164 165 166
	case KOBIL_ADAPTER_B_PRODUCT_ID:
		printk(KERN_DEBUG "KOBIL B1 PRO / KAAN PRO detected\n");
		break;
	case KOBIL_ADAPTER_K_PRODUCT_ID:
Alan Cox's avatar
Alan Cox committed
167 168
		printk(KERN_DEBUG
		  "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
Linus Torvalds's avatar
Linus Torvalds committed
169 170 171 172 173 174 175 176 177 178
		break;
	case KOBIL_USBTWIN_PRODUCT_ID:
		printk(KERN_DEBUG "KOBIL USBTWIN detected\n");
		break;
	case KOBIL_KAAN_SIM_PRODUCT_ID:
		printk(KERN_DEBUG "KOBIL KAAN SIM detected\n");
		break;
	}
	usb_set_serial_port_data(serial->port[0], priv);

Alan Cox's avatar
Alan Cox committed
179
	/* search for the necessary endpoints */
Linus Torvalds's avatar
Linus Torvalds committed
180
	pdev = serial->dev;
Alan Cox's avatar
Alan Cox committed
181 182
	actconfig = pdev->actconfig;
	interface = actconfig->interface[0];
Linus Torvalds's avatar
Linus Torvalds committed
183
	altsetting = interface->cur_altsetting;
Alan Cox's avatar
Alan Cox committed
184 185 186
	endpoint = altsetting->endpoint;

	for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
187
		endpoint = &altsetting->endpoint[i];
188
		if (usb_endpoint_is_int_out(&endpoint->desc)) {
Alan Cox's avatar
Alan Cox committed
189 190 191 192 193
			dbg("%s Found interrupt out endpoint. Address: %d",
				__func__, endpoint->desc.bEndpointAddress);
			priv->write_int_endpoint_address =
				endpoint->desc.bEndpointAddress;
		}
194
		if (usb_endpoint_is_int_in(&endpoint->desc)) {
Alan Cox's avatar
Alan Cox committed
195 196 197 198 199
			dbg("%s Found interrupt in  endpoint. Address: %d",
				__func__, endpoint->desc.bEndpointAddress);
			priv->read_int_endpoint_address =
				endpoint->desc.bEndpointAddress;
		}
Linus Torvalds's avatar
Linus Torvalds committed
200 201 202 203 204
	}
	return 0;
}


Alan Cox's avatar
Alan Cox committed
205
static void kobil_shutdown(struct usb_serial *serial)
Linus Torvalds's avatar
Linus Torvalds committed
206 207
{
	int i;
208
	dbg("%s - port %d", __func__, serial->port[0]->number);
Linus Torvalds's avatar
Linus Torvalds committed
209

Alan Cox's avatar
Alan Cox committed
210 211 212
	for (i = 0; i < serial->num_ports; ++i) {
		while (serial->port[i]->port.count > 0)
			kobil_close(NULL, serial->port[i], NULL);
Linus Torvalds's avatar
Linus Torvalds committed
213 214 215 216 217 218
		kfree(usb_get_serial_port_data(serial->port[i]));
		usb_set_serial_port_data(serial->port[i], NULL);
	}
}


Alan Cox's avatar
Alan Cox committed
219 220
static int kobil_open(struct tty_struct *tty,
			struct usb_serial_port *port, struct file *filp)
Linus Torvalds's avatar
Linus Torvalds committed
221
{
Alan Cox's avatar
Alan Cox committed
222
	int result = 0;
Linus Torvalds's avatar
Linus Torvalds committed
223 224 225 226 227
	struct kobil_private *priv;
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;
	int write_urb_transfer_buffer_length = 8;

228
	dbg("%s - port %d", __func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
229 230
	priv = usb_get_serial_port_data(port);

Alan Cox's avatar
Alan Cox committed
231
	/* someone sets the dev to 0 if the close method has been called */
Linus Torvalds's avatar
Linus Torvalds committed
232 233 234 235 236 237 238
	port->interrupt_in_urb->dev = port->serial->dev;


	/* force low_latency on so that our tty_push actually forces
	 * the data through, otherwise it is scheduled, and with high
	 * data rates (like with OHCI) data can get lost.
	 */
Alan Cox's avatar
Alan Cox committed
239 240 241 242 243
	if (tty) {
		tty->low_latency = 1;

		/* Default to echo off and other sane device settings */
		tty->termios->c_lflag = 0;
Alan Cox's avatar
Alan Cox committed
244 245
		tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN |
								 XCASE);
Alan Cox's avatar
Alan Cox committed
246
		tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF;
Alan Cox's avatar
Alan Cox committed
247 248 249 250
		/* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
		tty->termios->c_oflag &= ~ONLCR;
	}
	/* allocate memory for transfer buffer */
251
	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
Alan Cox's avatar
Alan Cox committed
252
	if (!transfer_buffer)
Linus Torvalds's avatar
Linus Torvalds committed
253
		return -ENOMEM;
Alan Cox's avatar
Alan Cox committed
254 255 256 257 258 259

	/* allocate write_urb */
	if (!port->write_urb) {
		dbg("%s - port %d  Allocating port->write_urb",
						__func__, port->number);
		port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
260
		if (!port->write_urb) {
Alan Cox's avatar
Alan Cox committed
261 262
			dbg("%s - port %d usb_alloc_urb failed",
						__func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
263 264 265 266 267
			kfree(transfer_buffer);
			return -ENOMEM;
		}
	}

Alan Cox's avatar
Alan Cox committed
268 269 270 271
	/* allocate memory for write_urb transfer buffer */
	port->write_urb->transfer_buffer =
			kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL);
	if (!port->write_urb->transfer_buffer) {
Linus Torvalds's avatar
Linus Torvalds committed
272 273 274 275
		kfree(transfer_buffer);
		usb_free_urb(port->write_urb);
		port->write_urb = NULL;
		return -ENOMEM;
Alan Cox's avatar
Alan Cox committed
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
	}

	/* get hardware version */
	result = usb_control_msg(port->serial->dev,
			  usb_rcvctrlpipe(port->serial->dev, 0),
			  SUSBCRequest_GetMisc,
			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
			  SUSBCR_MSC_GetHWVersion,
			  0,
			  transfer_buffer,
			  transfer_buffer_length,
			  KOBIL_TIMEOUT
	);
	dbg("%s - port %d Send get_HW_version URB returns: %i",
		__func__, port->number, result);
	dbg("Harware version: %i.%i.%i",
		transfer_buffer[0], transfer_buffer[1], transfer_buffer[2]);

	/* get firmware version */
	result = usb_control_msg(port->serial->dev,
			  usb_rcvctrlpipe(port->serial->dev, 0),
			  SUSBCRequest_GetMisc,
			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
			  SUSBCR_MSC_GetFWVersion,
			  0,
			  transfer_buffer,
			  transfer_buffer_length,
			  KOBIL_TIMEOUT
	);
	dbg("%s - port %d Send get_FW_version URB returns: %i",
					__func__, port->number, result);
	dbg("Firmware version: %i.%i.%i",
		transfer_buffer[0], transfer_buffer[1], transfer_buffer[2]);

	if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
			priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
		/* Setting Baudrate, Parity and Stopbits */
		result = usb_control_msg(port->serial->dev,
			  usb_rcvctrlpipe(port->serial->dev, 0),
			  SUSBCRequest_SetBaudRateParityAndStopBits,
			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
			  SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity |
							SUSBCR_SPASB_1StopBit,
			  0,
			  transfer_buffer,
			  0,
			  KOBIL_TIMEOUT
Linus Torvalds's avatar
Linus Torvalds committed
323
		);
Alan Cox's avatar
Alan Cox committed
324 325 326 327 328 329 330 331 332 333 334 335 336
		dbg("%s - port %d Send set_baudrate URB returns: %i",
					__func__, port->number, result);

		/* reset all queues */
		result = usb_control_msg(port->serial->dev,
			  usb_rcvctrlpipe(port->serial->dev, 0),
			  SUSBCRequest_Misc,
			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
			  SUSBCR_MSC_ResetAllQueues,
			  0,
			  transfer_buffer,
			  0,
			  KOBIL_TIMEOUT
Linus Torvalds's avatar
Linus Torvalds committed
337
		);
Alan Cox's avatar
Alan Cox committed
338 339
		dbg("%s - port %d Send reset_all_queues URB returns: %i",
					__func__, port->number, result);
Linus Torvalds's avatar
Linus Torvalds committed
340
	}
Alan Cox's avatar
Alan Cox committed
341 342
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
	    priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
Linus Torvalds's avatar
Linus Torvalds committed
343
	    priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
Alan Cox's avatar
Alan Cox committed
344 345 346 347
		/* start reading (Adapter B 'cause PNP string) */
		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
		dbg("%s - port %d Send read URB returns: %i",
					__func__, port->number, result);
Linus Torvalds's avatar
Linus Torvalds committed
348 349 350 351 352 353 354
	}

	kfree(transfer_buffer);
	return 0;
}


Alan Cox's avatar
Alan Cox committed
355 356
static void kobil_close(struct tty_struct *tty,
			struct usb_serial_port *port, struct file *filp)
Linus Torvalds's avatar
Linus Torvalds committed
357
{
358
	dbg("%s - port %d", __func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
359 360 361

	if (port->write_urb) {
		usb_kill_urb(port->write_urb);
Alan Cox's avatar
Alan Cox committed
362
		usb_free_urb(port->write_urb);
Linus Torvalds's avatar
Linus Torvalds committed
363 364
		port->write_urb = NULL;
	}
365
	usb_kill_urb(port->interrupt_in_urb);
Linus Torvalds's avatar
Linus Torvalds committed
366 367 368
}


369
static void kobil_read_int_callback(struct urb *urb)
Linus Torvalds's avatar
Linus Torvalds committed
370 371
{
	int result;
372
	struct usb_serial_port *port = urb->context;
Linus Torvalds's avatar
Linus Torvalds committed
373
	struct tty_struct *tty;
374 375
	unsigned char *data = urb->transfer_buffer;
	int status = urb->status;
Alan Cox's avatar
Alan Cox committed
376
/*	char *dbg_data; */
Linus Torvalds's avatar
Linus Torvalds committed
377

378
	dbg("%s - port %d", __func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
379

380 381
	if (status) {
		dbg("%s - port %d Read int status not zero: %d",
382
		    __func__, port->number, status);
Linus Torvalds's avatar
Linus Torvalds committed
383 384
		return;
	}
385

Alan Cox's avatar
Alan Cox committed
386
	tty = port->port.tty;
387 388
	if (urb->actual_length) {

Alan Cox's avatar
Alan Cox committed
389
		/* BEGIN DEBUG */
Linus Torvalds's avatar
Linus Torvalds committed
390
		/*
Alan Cox's avatar
Alan Cox committed
391 392
		  dbg_data = kzalloc((3 *  purb->actual_length + 10)
						* sizeof(char), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
393
		  if (! dbg_data) {
Alan Cox's avatar
Alan Cox committed
394
			  return;
Linus Torvalds's avatar
Linus Torvalds committed
395
		  }
Alan Cox's avatar
Alan Cox committed
396 397
		  for (i = 0; i < purb->actual_length; i++) {
			  sprintf(dbg_data +3*i, "%02X ", data[i]);
Linus Torvalds's avatar
Linus Torvalds committed
398
		  }
Alan Cox's avatar
Alan Cox committed
399
		  dbg(" <-- %s", dbg_data);
Linus Torvalds's avatar
Linus Torvalds committed
400 401
		  kfree(dbg_data);
		*/
Alan Cox's avatar
Alan Cox committed
402
		/* END DEBUG */
Linus Torvalds's avatar
Linus Torvalds committed
403

404 405
		tty_buffer_request_room(tty, urb->actual_length);
		tty_insert_flip_string(tty, data, urb->actual_length);
Linus Torvalds's avatar
Linus Torvalds committed
406 407
		tty_flip_buffer_push(tty);
	}
Alan Cox's avatar
Alan Cox committed
408
	/* someone sets the dev to 0 if the close method has been called */
Linus Torvalds's avatar
Linus Torvalds committed
409 410
	port->interrupt_in_urb->dev = port->serial->dev;

411
	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
Alan Cox's avatar
Alan Cox committed
412 413
	dbg("%s - port %d Send read URB returns: %i",
			__func__, port->number, result);
Linus Torvalds's avatar
Linus Torvalds committed
414 415 416
}


Alan Cox's avatar
Alan Cox committed
417
static void kobil_write_callback(struct urb *purb)
Linus Torvalds's avatar
Linus Torvalds committed
418 419 420 421
{
}


Alan Cox's avatar
Alan Cox committed
422
static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
Linus Torvalds's avatar
Linus Torvalds committed
423 424 425 426 427
			const unsigned char *buf, int count)
{
	int length = 0;
	int result = 0;
	int todo = 0;
Alan Cox's avatar
Alan Cox committed
428
	struct kobil_private *priv;
Linus Torvalds's avatar
Linus Torvalds committed
429 430

	if (count == 0) {
Alan Cox's avatar
Alan Cox committed
431 432
		dbg("%s - port %d write request of 0 bytes",
						__func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
433 434 435 436 437 438
		return 0;
	}

	priv = usb_get_serial_port_data(port);

	if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
439
		dbg("%s - port %d Error: write request bigger than buffer size", __func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
440 441 442
		return -ENOMEM;
	}

Alan Cox's avatar
Alan Cox committed
443 444 445 446
	/* Copy data to buffer */
	memcpy(priv->buf + priv->filled, buf, count);
	usb_serial_debug_data(debug, &port->dev, __func__, count,
						priv->buf + priv->filled);
Linus Torvalds's avatar
Linus Torvalds committed
447 448
	priv->filled = priv->filled + count;

Alan Cox's avatar
Alan Cox committed
449 450 451 452 453 454 455
	/* only send complete block. TWIN, KAAN SIM and adapter K
	   use the same protocol. */
	if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) ||
	     ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) {
		/* stop reading (except TWIN and KAAN SIM) */
		if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID)
			|| (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID))
Linus Torvalds's avatar
Linus Torvalds committed
456 457 458 459
			usb_kill_urb(port->interrupt_in_urb);

		todo = priv->filled - priv->cur_pos;

Alan Cox's avatar
Alan Cox committed
460 461
		while (todo > 0) {
			/* max 8 byte in one urb (endpoint size) */
Linus Torvalds's avatar
Linus Torvalds committed
462
			length = (todo < 8) ? todo : 8;
Alan Cox's avatar
Alan Cox committed
463 464 465 466 467 468 469 470 471 472 473 474 475
			/* copy data to transfer buffer */
			memcpy(port->write_urb->transfer_buffer,
					priv->buf + priv->cur_pos, length);
			usb_fill_int_urb(port->write_urb,
				  port->serial->dev,
				  usb_sndintpipe(port->serial->dev,
					priv->write_int_endpoint_address),
				  port->write_urb->transfer_buffer,
				  length,
				  kobil_write_callback,
				  port,
				  8
			);
Linus Torvalds's avatar
Linus Torvalds committed
476 477

			priv->cur_pos = priv->cur_pos + length;
Alan Cox's avatar
Alan Cox committed
478 479 480
			result = usb_submit_urb(port->write_urb, GFP_NOIO);
			dbg("%s - port %d Send write URB returns: %i",
					__func__, port->number, result);
Linus Torvalds's avatar
Linus Torvalds committed
481 482
			todo = priv->filled - priv->cur_pos;

Alan Cox's avatar
Alan Cox committed
483
			if (todo > 0)
Linus Torvalds's avatar
Linus Torvalds committed
484
				msleep(24);
Alan Cox's avatar
Alan Cox committed
485
		}
Linus Torvalds's avatar
Linus Torvalds committed
486 487 488 489

		priv->filled = 0;
		priv->cur_pos = 0;

Alan Cox's avatar
Alan Cox committed
490 491
		/* someone sets the dev to 0 if the close method
		   has been called */
Linus Torvalds's avatar
Linus Torvalds committed
492
		port->interrupt_in_urb->dev = port->serial->dev;
Alan Cox's avatar
Alan Cox committed
493 494 495 496 497 498

		/* start reading (except TWIN and KAAN SIM) */
		if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
			priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
			/* someone sets the dev to 0 if the close method has
			   been called */
Linus Torvalds's avatar
Linus Torvalds committed
499
			port->interrupt_in_urb->dev = port->serial->dev;
Alan Cox's avatar
Alan Cox committed
500 501 502 503 504

			result = usb_submit_urb(port->interrupt_in_urb,
								GFP_NOIO);
			dbg("%s - port %d Send read URB returns: %i",
					__func__, port->number, result);
Linus Torvalds's avatar
Linus Torvalds committed
505 506 507 508 509 510
		}
	}
	return count;
}


Alan Cox's avatar
Alan Cox committed
511
static int kobil_write_room(struct tty_struct *tty)
Linus Torvalds's avatar
Linus Torvalds committed
512
{
Alan Cox's avatar
Alan Cox committed
513
	/* dbg("%s - port %d", __func__, port->number); */
Alan Cox's avatar
Alan Cox committed
514
	/* FIXME */
Linus Torvalds's avatar
Linus Torvalds committed
515 516 517 518
	return 8;
}


Alan Cox's avatar
Alan Cox committed
519
static int kobil_tiocmget(struct tty_struct *tty, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
520
{
Alan Cox's avatar
Alan Cox committed
521
	struct usb_serial_port *port = tty->driver_data;
Alan Cox's avatar
Alan Cox committed
522
	struct kobil_private *priv;
Linus Torvalds's avatar
Linus Torvalds committed
523 524 525 526 527
	int result;
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;

	priv = usb_get_serial_port_data(port);
Alan Cox's avatar
Alan Cox committed
528 529 530
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
			|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
		/* This device doesn't support ioctl calls */
Linus Torvalds's avatar
Linus Torvalds committed
531 532 533
		return -EINVAL;
	}

Alan Cox's avatar
Alan Cox committed
534
	/* allocate memory for transfer buffer */
535
	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
Alan Cox's avatar
Alan Cox committed
536
	if (!transfer_buffer)
Linus Torvalds's avatar
Linus Torvalds committed
537 538
		return -ENOMEM;

Alan Cox's avatar
Alan Cox committed
539 540 541 542 543 544 545 546 547 548 549
	result = usb_control_msg(port->serial->dev,
			  usb_rcvctrlpipe(port->serial->dev, 0),
			  SUSBCRequest_GetStatusLineState,
			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN,
			  0,
			  0,
			  transfer_buffer,
			  transfer_buffer_length,
			  KOBIL_TIMEOUT);

	dbg("%s - port %d Send get_status_line_state URB returns: %i. Statusline: %02x",
550
	    __func__, port->number, result, transfer_buffer[0]);
Linus Torvalds's avatar
Linus Torvalds committed
551

552 553 554
	result = 0;
	if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
		result = TIOCM_DSR;
Linus Torvalds's avatar
Linus Torvalds committed
555
	kfree(transfer_buffer);
556
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
557 558
}

Alan Cox's avatar
Alan Cox committed
559
static int kobil_tiocmset(struct tty_struct *tty, struct file *file,
Linus Torvalds's avatar
Linus Torvalds committed
560 561
			   unsigned int set, unsigned int clear)
{
Alan Cox's avatar
Alan Cox committed
562
	struct usb_serial_port *port = tty->driver_data;
Alan Cox's avatar
Alan Cox committed
563
	struct kobil_private *priv;
Linus Torvalds's avatar
Linus Torvalds committed
564 565 566 567 568 569
	int result;
	int dtr = 0;
	int rts = 0;
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;

570
	/* FIXME: locking ? */
Linus Torvalds's avatar
Linus Torvalds committed
571
	priv = usb_get_serial_port_data(port);
Alan Cox's avatar
Alan Cox committed
572 573 574
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
		|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
		/* This device doesn't support ioctl calls */
Linus Torvalds's avatar
Linus Torvalds committed
575 576 577
		return -EINVAL;
	}

Alan Cox's avatar
Alan Cox committed
578
	/* allocate memory for transfer buffer */
579
	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
Alan Cox's avatar
Alan Cox committed
580
	if (!transfer_buffer)
Linus Torvalds's avatar
Linus Torvalds committed
581 582 583 584 585 586 587 588 589 590 591 592 593
		return -ENOMEM;

	if (set & TIOCM_RTS)
		rts = 1;
	if (set & TIOCM_DTR)
		dtr = 1;
	if (clear & TIOCM_RTS)
		rts = 0;
	if (clear & TIOCM_DTR)
		dtr = 0;

	if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
		if (dtr != 0)
Alan Cox's avatar
Alan Cox committed
594 595
			dbg("%s - port %d Setting DTR",
						__func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
596
		else
Alan Cox's avatar
Alan Cox committed
597 598 599 600 601 602 603 604 605 606 607
			dbg("%s - port %d Clearing DTR",
						__func__, port->number);
		result = usb_control_msg(port->serial->dev,
			  usb_rcvctrlpipe(port->serial->dev, 0),
			  SUSBCRequest_SetStatusLinesOrQueues,
			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
			  ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR),
			  0,
			  transfer_buffer,
			  0,
			  KOBIL_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
608 609
	} else {
		if (rts != 0)
Alan Cox's avatar
Alan Cox committed
610 611
			dbg("%s - port %d Setting RTS",
						__func__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
612
		else
Alan Cox's avatar
Alan Cox committed
613 614 615 616 617 618 619 620 621 622 623
			dbg("%s - port %d Clearing RTS",
						__func__, port->number);
		result = usb_control_msg(port->serial->dev,
			usb_rcvctrlpipe(port->serial->dev, 0),
			SUSBCRequest_SetStatusLinesOrQueues,
			USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
			((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS),
			0,
			transfer_buffer,
			0,
			KOBIL_TIMEOUT);
Linus Torvalds's avatar
Linus Torvalds committed
624
	}
Alan Cox's avatar
Alan Cox committed
625 626
	dbg("%s - port %d Send set_status_line URB returns: %i",
					__func__, port->number, result);
Linus Torvalds's avatar
Linus Torvalds committed
627 628 629 630
	kfree(transfer_buffer);
	return (result < 0) ? result : 0;
}

Alan Cox's avatar
Alan Cox committed
631 632
static void kobil_set_termios(struct tty_struct *tty,
			struct usb_serial_port *port, struct ktermios *old)
Linus Torvalds's avatar
Linus Torvalds committed
633
{
Alan Cox's avatar
Alan Cox committed
634
	struct kobil_private *priv;
Linus Torvalds's avatar
Linus Torvalds committed
635 636
	int result;
	unsigned short urb_val = 0;
Alan Cox's avatar
Alan Cox committed
637
	int c_cflag = tty->termios->c_cflag;
Alan Cox's avatar
Alan Cox committed
638
	speed_t speed;
Alan Cox's avatar
Alan Cox committed
639
	void *settings;
Linus Torvalds's avatar
Linus Torvalds committed
640 641

	priv = usb_get_serial_port_data(port);
Alan Cox's avatar
Alan Cox committed
642
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
Alan Cox's avatar
Alan Cox committed
643
			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
Alan Cox's avatar
Alan Cox committed
644
		/* This device doesn't support ioctl calls */
Alan Cox's avatar
Alan Cox committed
645
		*tty->termios = *old;
Alan Cox's avatar
Alan Cox committed
646
		return;
Alan Cox's avatar
Alan Cox committed
647
	}
Linus Torvalds's avatar
Linus Torvalds committed
648

Alan Cox's avatar
Alan Cox committed
649 650 651 652 653 654 655 656 657 658
	speed = tty_get_baud_rate(tty);
	switch (speed) {
	case 1200:
		urb_val = SUSBCR_SBR_1200;
		break;
	default:
		speed = 9600;
	case 9600:
		urb_val = SUSBCR_SBR_9600;
		break;
Alan Cox's avatar
Alan Cox committed
659
	}
Alan Cox's avatar
Alan Cox committed
660 661
	urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits :
							SUSBCR_SPASB_1StopBit;
Alan Cox's avatar
Alan Cox committed
662 663

	settings = kzalloc(50, GFP_KERNEL);
Alan Cox's avatar
Alan Cox committed
664
	if (!settings)
Alan Cox's avatar
Alan Cox committed
665
		return;
Linus Torvalds's avatar
Linus Torvalds committed
666

Alan Cox's avatar
Alan Cox committed
667
	sprintf(settings, "%d ", speed);
Linus Torvalds's avatar
Linus Torvalds committed
668

Alan Cox's avatar
Alan Cox committed
669 670 671 672
	if (c_cflag & PARENB) {
		if  (c_cflag & PARODD) {
			urb_val |= SUSBCR_SPASB_OddParity;
			strcat(settings, "Odd Parity");
Linus Torvalds's avatar
Linus Torvalds committed
673
		} else {
Alan Cox's avatar
Alan Cox committed
674 675
			urb_val |= SUSBCR_SPASB_EvenParity;
			strcat(settings, "Even Parity");
Linus Torvalds's avatar
Linus Torvalds committed
676
		}
Alan Cox's avatar
Alan Cox committed
677 678 679 680
	} else {
		urb_val |= SUSBCR_SPASB_NoParity;
		strcat(settings, "No Parity");
	}
Alan Cox's avatar
Alan Cox committed
681 682
	tty->termios->c_cflag &= ~CMSPAR;
	tty_encode_baud_rate(tty, speed, speed);
Linus Torvalds's avatar
Linus Torvalds committed
683

Alan Cox's avatar
Alan Cox committed
684 685 686 687 688 689 690 691 692
	result = usb_control_msg(port->serial->dev,
		  usb_rcvctrlpipe(port->serial->dev, 0),
		  SUSBCRequest_SetBaudRateParityAndStopBits,
		  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
		  urb_val,
		  0,
		  settings,
		  0,
		  KOBIL_TIMEOUT
Alan Cox's avatar
Alan Cox committed
693 694 695
		);
	kfree(settings);
}
Linus Torvalds's avatar
Linus Torvalds committed
696

Alan Cox's avatar
Alan Cox committed
697 698
static int kobil_ioctl(struct tty_struct *tty, struct file *file,
					unsigned int cmd, unsigned long arg)
Alan Cox's avatar
Alan Cox committed
699
{
Alan Cox's avatar
Alan Cox committed
700
	struct usb_serial_port *port = tty->driver_data;
Alan Cox's avatar
Alan Cox committed
701
	struct kobil_private *priv = usb_get_serial_port_data(port);
Alan Cox's avatar
Alan Cox committed
702 703 704 705
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;
	int result;

Alan Cox's avatar
Alan Cox committed
706 707 708
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
		/* This device doesn't support ioctl calls */
Alan Cox's avatar
Alan Cox committed
709
		return -ENOIOCTLCMD;
Linus Torvalds's avatar
Linus Torvalds committed
710

Alan Cox's avatar
Alan Cox committed
711
	switch (cmd) {
Alan Cox's avatar
Alan Cox committed
712
	case TCFLSH:
713
		transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL);
Alan Cox's avatar
Alan Cox committed
714 715 716 717 718 719 720 721 722 723 724 725
		if (!transfer_buffer)
			return -ENOBUFS;

		result = usb_control_msg(port->serial->dev,
			  usb_rcvctrlpipe(port->serial->dev, 0),
			  SUSBCRequest_Misc,
			  USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT,
			  SUSBCR_MSC_ResetAllQueues,
			  0,
			  NULL, /* transfer_buffer, */
			  0,
			  KOBIL_TIMEOUT
Linus Torvalds's avatar
Linus Torvalds committed
726
			);
Alan Cox's avatar
Alan Cox committed
727

728
		dbg("%s - port %d Send reset_all_queues (FLUSH) URB returns: %i", __func__, port->number, result);
Linus Torvalds's avatar
Linus Torvalds committed
729
		kfree(transfer_buffer);
Alan Cox's avatar
Alan Cox committed
730
		return (result < 0) ? -EIO: 0;
Alan Cox's avatar
Alan Cox committed
731 732
	default:
		return -ENOIOCTLCMD;
Linus Torvalds's avatar
Linus Torvalds committed
733 734 735
	}
}

Alan Cox's avatar
Alan Cox committed
736
static int __init kobil_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
737 738 739 740 741 742
{
	int retval;
	retval = usb_serial_register(&kobil_device);
	if (retval)
		goto failed_usb_serial_register;
	retval = usb_register(&kobil_driver);
Alan Cox's avatar
Alan Cox committed
743
	if (retval)
Linus Torvalds's avatar
Linus Torvalds committed
744 745 746 747 748 749 750 751 752 753 754 755 756
		goto failed_usb_register;

	info(DRIVER_VERSION " " DRIVER_AUTHOR);
	info(DRIVER_DESC);

	return 0;
failed_usb_register:
	usb_serial_deregister(&kobil_device);
failed_usb_serial_register:
	return retval;
}


Alan Cox's avatar
Alan Cox committed
757
static void __exit kobil_exit(void)
Linus Torvalds's avatar
Linus Torvalds committed
758
{
Alan Cox's avatar
Alan Cox committed
759 760
	usb_deregister(&kobil_driver);
	usb_serial_deregister(&kobil_device);
Linus Torvalds's avatar
Linus Torvalds committed
761 762 763 764 765
}

module_init(kobil_init);
module_exit(kobil_exit);

Alan Cox's avatar
Alan Cox committed
766 767 768
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
Linus Torvalds's avatar
Linus Torvalds committed
769 770 771

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");