kobil_sct.c 18.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *  KOBIL USB Smart Card Terminal Driver
 *
A
Alan Cox 已提交
4
 *  Copyright (C) 2002  KOBIL Systems GmbH
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *  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)
 */


#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>
A
Alan Cox 已提交
35
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
36
#include <linux/usb.h>
37
#include <linux/usb/serial.h>
L
Linus Torvalds 已提交
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
#include <linux/ioctl.h>
#include "kobil_sct.h"

/* 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 */
A
Alan Cox 已提交
57
static int  kobil_startup(struct usb_serial *serial);
58
static void kobil_release(struct usb_serial *serial);
59
static int  kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
60
static void kobil_close(struct usb_serial_port *port);
A
Alan Cox 已提交
61
static int  kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
L
Linus Torvalds 已提交
62
			 const unsigned char *buf, int count);
A
Alan Cox 已提交
63
static int  kobil_write_room(struct tty_struct *tty);
64
static int  kobil_ioctl(struct tty_struct *tty,
L
Linus Torvalds 已提交
65
			unsigned int cmd, unsigned long arg);
66
static int  kobil_tiocmget(struct tty_struct *tty);
67
static int  kobil_tiocmset(struct tty_struct *tty,
L
Linus Torvalds 已提交
68
			   unsigned int set, unsigned int clear);
A
Alan Cox 已提交
69 70 71
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,
A
Alan Cox 已提交
72
			struct usb_serial_port *port, struct ktermios *old);
A
Alan Cox 已提交
73
static void kobil_init_termios(struct tty_struct *tty);
L
Linus Torvalds 已提交
74

75
static const struct usb_device_id id_table[] = {
L
Linus Torvalds 已提交
76 77 78 79 80 81
	{ 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 */
};
A
Alan Cox 已提交
82
MODULE_DEVICE_TABLE(usb, id_table);
L
Linus Torvalds 已提交
83

84
static struct usb_serial_driver kobil_device = {
85 86
	.driver = {
		.owner =	THIS_MODULE,
87
		.name =		"kobil",
88
	},
89
	.description =		"KOBIL USB smart card terminal",
L
Linus Torvalds 已提交
90 91 92
	.id_table =		id_table,
	.num_ports =		1,
	.attach =		kobil_startup,
93
	.release =		kobil_release,
L
Linus Torvalds 已提交
94
	.ioctl =		kobil_ioctl,
A
Alan Cox 已提交
95
	.set_termios =		kobil_set_termios,
A
Alan Cox 已提交
96
	.init_termios =		kobil_init_termios,
L
Linus Torvalds 已提交
97 98 99 100 101 102 103 104 105
	.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,
};

106 107 108
static struct usb_serial_driver * const serial_drivers[] = {
	&kobil_device, NULL
};
L
Linus Torvalds 已提交
109 110 111 112

struct kobil_private {
	int write_int_endpoint_address;
	int read_int_endpoint_address;
A
Alan Cox 已提交
113 114 115
	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 */
L
Linus Torvalds 已提交
116 117 118 119
	__u16 device_type;
};


A
Alan Cox 已提交
120
static int kobil_startup(struct usb_serial *serial)
L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130
{
	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);
A
Alan Cox 已提交
131
	if (!priv)
L
Linus Torvalds 已提交
132 133 134 135 136 137
		return -ENOMEM;

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

A
Alan Cox 已提交
138
	switch (priv->device_type) {
L
Linus Torvalds 已提交
139 140 141 142
	case KOBIL_ADAPTER_B_PRODUCT_ID:
		printk(KERN_DEBUG "KOBIL B1 PRO / KAAN PRO detected\n");
		break;
	case KOBIL_ADAPTER_K_PRODUCT_ID:
A
Alan Cox 已提交
143 144
		printk(KERN_DEBUG
		  "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
L
Linus Torvalds 已提交
145 146 147 148 149 150 151 152 153 154
		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);

A
Alan Cox 已提交
155
	/* search for the necessary endpoints */
L
Linus Torvalds 已提交
156
	pdev = serial->dev;
A
Alan Cox 已提交
157 158
	actconfig = pdev->actconfig;
	interface = actconfig->interface[0];
L
Linus Torvalds 已提交
159
	altsetting = interface->cur_altsetting;
A
Alan Cox 已提交
160 161 162
	endpoint = altsetting->endpoint;

	for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
L
Linus Torvalds 已提交
163
		endpoint = &altsetting->endpoint[i];
164
		if (usb_endpoint_is_int_out(&endpoint->desc)) {
165 166
			dev_dbg(&serial->dev->dev,
				"%s Found interrupt out endpoint. Address: %d\n",
A
Alan Cox 已提交
167 168 169 170
				__func__, endpoint->desc.bEndpointAddress);
			priv->write_int_endpoint_address =
				endpoint->desc.bEndpointAddress;
		}
171
		if (usb_endpoint_is_int_in(&endpoint->desc)) {
172 173
			dev_dbg(&serial->dev->dev,
				"%s Found interrupt in  endpoint. Address: %d\n",
A
Alan Cox 已提交
174 175 176 177
				__func__, endpoint->desc.bEndpointAddress);
			priv->read_int_endpoint_address =
				endpoint->desc.bEndpointAddress;
		}
L
Linus Torvalds 已提交
178 179 180 181 182
	}
	return 0;
}


183
static void kobil_release(struct usb_serial *serial)
L
Linus Torvalds 已提交
184 185 186
{
	int i;

187
	for (i = 0; i < serial->num_ports; ++i)
L
Linus Torvalds 已提交
188 189 190
		kfree(usb_get_serial_port_data(serial->port[i]));
}

A
Alan Cox 已提交
191 192 193 194 195 196 197 198 199
static void kobil_init_termios(struct tty_struct *tty)
{
	/* Default to echo off and other sane device settings */
	tty->termios->c_lflag = 0;
	tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE);
	tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF;
	/* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */
	tty->termios->c_oflag &= ~ONLCR;
}
L
Linus Torvalds 已提交
200

201
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
L
Linus Torvalds 已提交
202
{
203
	struct device *dev = &port->dev;
A
Alan Cox 已提交
204
	int result = 0;
L
Linus Torvalds 已提交
205 206 207 208 209 210 211
	struct kobil_private *priv;
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;
	int write_urb_transfer_buffer_length = 8;

	priv = usb_get_serial_port_data(port);

A
Alan Cox 已提交
212
	/* allocate memory for transfer buffer */
213
	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
A
Alan Cox 已提交
214
	if (!transfer_buffer)
L
Linus Torvalds 已提交
215
		return -ENOMEM;
A
Alan Cox 已提交
216 217 218

	/* allocate write_urb */
	if (!port->write_urb) {
219
		dev_dbg(dev, "%s - Allocating port->write_urb\n", __func__);
A
Alan Cox 已提交
220
		port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
L
Linus Torvalds 已提交
221
		if (!port->write_urb) {
222
			dev_dbg(dev, "%s - usb_alloc_urb failed\n", __func__);
L
Linus Torvalds 已提交
223 224 225 226 227
			kfree(transfer_buffer);
			return -ENOMEM;
		}
	}

A
Alan Cox 已提交
228 229 230 231
	/* 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) {
L
Linus Torvalds 已提交
232 233 234 235
		kfree(transfer_buffer);
		usb_free_urb(port->write_urb);
		port->write_urb = NULL;
		return -ENOMEM;
A
Alan Cox 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248
	}

	/* 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
	);
249 250 251
	dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
	dev_dbg(dev, "Harware version: %i.%i.%i\n", transfer_buffer[0],
		transfer_buffer[1], transfer_buffer[2]);
A
Alan Cox 已提交
252 253 254 255 256 257 258 259 260 261 262 263

	/* 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
	);
264 265 266
	dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
	dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
		transfer_buffer[1], transfer_buffer[2]);
A
Alan Cox 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280

	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
L
Linus Torvalds 已提交
281
		);
282
		dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result);
A
Alan Cox 已提交
283 284 285 286 287 288 289 290 291 292 293

		/* 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
L
Linus Torvalds 已提交
294
		);
295
		dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result);
L
Linus Torvalds 已提交
296
	}
A
Alan Cox 已提交
297 298
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
	    priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
L
Linus Torvalds 已提交
299
	    priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
A
Alan Cox 已提交
300 301
		/* start reading (Adapter B 'cause PNP string) */
		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
302
		dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result);
L
Linus Torvalds 已提交
303 304 305 306 307 308 309
	}

	kfree(transfer_buffer);
	return 0;
}


310
static void kobil_close(struct usb_serial_port *port)
L
Linus Torvalds 已提交
311
{
312
	/* FIXME: Add rts/dtr methods */
L
Linus Torvalds 已提交
313
	if (port->write_urb) {
J
Johan Hovold 已提交
314 315
		usb_poison_urb(port->write_urb);
		kfree(port->write_urb->transfer_buffer);
A
Alan Cox 已提交
316
		usb_free_urb(port->write_urb);
L
Linus Torvalds 已提交
317 318
		port->write_urb = NULL;
	}
319
	usb_kill_urb(port->interrupt_in_urb);
L
Linus Torvalds 已提交
320 321 322
}


323
static void kobil_read_int_callback(struct urb *urb)
L
Linus Torvalds 已提交
324 325
{
	int result;
326
	struct usb_serial_port *port = urb->context;
L
Linus Torvalds 已提交
327
	struct tty_struct *tty;
328 329
	unsigned char *data = urb->transfer_buffer;
	int status = urb->status;
L
Linus Torvalds 已提交
330

331
	if (status) {
332
		dev_dbg(&port->dev, "%s - Read int status not zero: %d\n", __func__, status);
L
Linus Torvalds 已提交
333 334
		return;
	}
335

A
Alan Cox 已提交
336
	tty = tty_port_tty_get(&port->port);
337
	if (tty && urb->actual_length) {
338

A
Alan Cox 已提交
339
		/* BEGIN DEBUG */
L
Linus Torvalds 已提交
340
		/*
341 342
		  char *dbg_data;

A
Alan Cox 已提交
343 344
		  dbg_data = kzalloc((3 *  purb->actual_length + 10)
						* sizeof(char), GFP_KERNEL);
L
Linus Torvalds 已提交
345
		  if (! dbg_data) {
A
Alan Cox 已提交
346
			  return;
L
Linus Torvalds 已提交
347
		  }
A
Alan Cox 已提交
348 349
		  for (i = 0; i < purb->actual_length; i++) {
			  sprintf(dbg_data +3*i, "%02X ", data[i]);
L
Linus Torvalds 已提交
350
		  }
351
		  dev_dbg(&port->dev, " <-- %s\n", dbg_data);
L
Linus Torvalds 已提交
352 353
		  kfree(dbg_data);
		*/
A
Alan Cox 已提交
354
		/* END DEBUG */
L
Linus Torvalds 已提交
355

356
		tty_insert_flip_string(tty, data, urb->actual_length);
L
Linus Torvalds 已提交
357 358
		tty_flip_buffer_push(tty);
	}
A
Alan Cox 已提交
359
	tty_kref_put(tty);
L
Linus Torvalds 已提交
360

361
	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
362
	dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
L
Linus Torvalds 已提交
363 364 365
}


A
Alan Cox 已提交
366
static void kobil_write_callback(struct urb *purb)
L
Linus Torvalds 已提交
367 368 369 370
{
}


A
Alan Cox 已提交
371
static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
L
Linus Torvalds 已提交
372 373 374 375 376
			const unsigned char *buf, int count)
{
	int length = 0;
	int result = 0;
	int todo = 0;
A
Alan Cox 已提交
377
	struct kobil_private *priv;
L
Linus Torvalds 已提交
378 379

	if (count == 0) {
380
		dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
L
Linus Torvalds 已提交
381 382 383 384 385 386
		return 0;
	}

	priv = usb_get_serial_port_data(port);

	if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
387
		dev_dbg(&port->dev, "%s - Error: write request bigger than buffer size\n", __func__);
L
Linus Torvalds 已提交
388 389 390
		return -ENOMEM;
	}

A
Alan Cox 已提交
391 392
	/* Copy data to buffer */
	memcpy(priv->buf + priv->filled, buf, count);
393
	usb_serial_debug_data(&port->dev, __func__, count, priv->buf + priv->filled);
L
Linus Torvalds 已提交
394 395
	priv->filled = priv->filled + count;

A
Alan Cox 已提交
396 397 398 399 400 401 402
	/* 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))
L
Linus Torvalds 已提交
403 404 405 406
			usb_kill_urb(port->interrupt_in_urb);

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

A
Alan Cox 已提交
407 408
		while (todo > 0) {
			/* max 8 byte in one urb (endpoint size) */
L
Linus Torvalds 已提交
409
			length = (todo < 8) ? todo : 8;
A
Alan Cox 已提交
410 411 412 413 414 415 416 417 418 419 420 421 422
			/* 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
			);
L
Linus Torvalds 已提交
423 424

			priv->cur_pos = priv->cur_pos + length;
A
Alan Cox 已提交
425
			result = usb_submit_urb(port->write_urb, GFP_NOIO);
426
			dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
L
Linus Torvalds 已提交
427 428
			todo = priv->filled - priv->cur_pos;

A
Alan Cox 已提交
429
			if (todo > 0)
L
Linus Torvalds 已提交
430
				msleep(24);
A
Alan Cox 已提交
431
		}
L
Linus Torvalds 已提交
432 433 434 435

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

A
Alan Cox 已提交
436 437 438 439 440
		/* start reading (except TWIN and KAAN SIM) */
		if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
			priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
			result = usb_submit_urb(port->interrupt_in_urb,
								GFP_NOIO);
441
			dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
L
Linus Torvalds 已提交
442 443 444 445 446 447
		}
	}
	return count;
}


A
Alan Cox 已提交
448
static int kobil_write_room(struct tty_struct *tty)
L
Linus Torvalds 已提交
449
{
A
Alan Cox 已提交
450
	/* FIXME */
L
Linus Torvalds 已提交
451 452 453 454
	return 8;
}


455
static int kobil_tiocmget(struct tty_struct *tty)
L
Linus Torvalds 已提交
456
{
A
Alan Cox 已提交
457
	struct usb_serial_port *port = tty->driver_data;
A
Alan Cox 已提交
458
	struct kobil_private *priv;
L
Linus Torvalds 已提交
459 460 461 462 463
	int result;
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;

	priv = usb_get_serial_port_data(port);
A
Alan Cox 已提交
464 465 466
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
			|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
		/* This device doesn't support ioctl calls */
L
Linus Torvalds 已提交
467 468 469
		return -EINVAL;
	}

A
Alan Cox 已提交
470
	/* allocate memory for transfer buffer */
471
	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
A
Alan Cox 已提交
472
	if (!transfer_buffer)
L
Linus Torvalds 已提交
473 474
		return -ENOMEM;

A
Alan Cox 已提交
475 476 477 478 479 480 481 482 483 484
	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);

485 486
	dev_dbg(&port->dev, "%s - Send get_status_line_state URB returns: %i. Statusline: %02x\n",
		__func__, result, transfer_buffer[0]);
L
Linus Torvalds 已提交
487

488 489 490
	result = 0;
	if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
		result = TIOCM_DSR;
L
Linus Torvalds 已提交
491
	kfree(transfer_buffer);
492
	return result;
L
Linus Torvalds 已提交
493 494
}

495
static int kobil_tiocmset(struct tty_struct *tty,
L
Linus Torvalds 已提交
496 497
			   unsigned int set, unsigned int clear)
{
A
Alan Cox 已提交
498
	struct usb_serial_port *port = tty->driver_data;
499
	struct device *dev = &port->dev;
A
Alan Cox 已提交
500
	struct kobil_private *priv;
L
Linus Torvalds 已提交
501 502 503 504 505 506
	int result;
	int dtr = 0;
	int rts = 0;
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;

507
	/* FIXME: locking ? */
L
Linus Torvalds 已提交
508
	priv = usb_get_serial_port_data(port);
A
Alan Cox 已提交
509 510 511
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID
		|| priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
		/* This device doesn't support ioctl calls */
L
Linus Torvalds 已提交
512 513 514
		return -EINVAL;
	}

A
Alan Cox 已提交
515
	/* allocate memory for transfer buffer */
516
	transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL);
A
Alan Cox 已提交
517
	if (!transfer_buffer)
L
Linus Torvalds 已提交
518 519 520 521 522 523 524 525 526 527 528 529 530
		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)
531
			dev_dbg(dev, "%s - Setting DTR\n", __func__);
L
Linus Torvalds 已提交
532
		else
533
			dev_dbg(dev, "%s - Clearing DTR\n", __func__);
A
Alan Cox 已提交
534 535 536 537 538 539 540 541 542
		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);
L
Linus Torvalds 已提交
543 544
	} else {
		if (rts != 0)
545
			dev_dbg(dev, "%s - Setting RTS\n", __func__);
L
Linus Torvalds 已提交
546
		else
547
			dev_dbg(dev, "%s - Clearing RTS\n", __func__);
A
Alan Cox 已提交
548 549 550 551 552 553 554 555 556
		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);
L
Linus Torvalds 已提交
557
	}
558
	dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result);
L
Linus Torvalds 已提交
559 560 561 562
	kfree(transfer_buffer);
	return (result < 0) ? result : 0;
}

A
Alan Cox 已提交
563 564
static void kobil_set_termios(struct tty_struct *tty,
			struct usb_serial_port *port, struct ktermios *old)
L
Linus Torvalds 已提交
565
{
A
Alan Cox 已提交
566
	struct kobil_private *priv;
L
Linus Torvalds 已提交
567 568
	int result;
	unsigned short urb_val = 0;
A
Alan Cox 已提交
569
	int c_cflag = tty->termios->c_cflag;
A
Alan Cox 已提交
570
	speed_t speed;
L
Linus Torvalds 已提交
571 572

	priv = usb_get_serial_port_data(port);
A
Alan Cox 已提交
573
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
A
Alan Cox 已提交
574
			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
A
Alan Cox 已提交
575
		/* This device doesn't support ioctl calls */
A
Alan Cox 已提交
576
		*tty->termios = *old;
A
Alan Cox 已提交
577
		return;
A
Alan Cox 已提交
578
	}
L
Linus Torvalds 已提交
579

A
Alan Cox 已提交
580 581 582 583 584 585 586 587 588 589
	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;
A
Alan Cox 已提交
590
	}
A
Alan Cox 已提交
591 592
	urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits :
							SUSBCR_SPASB_1StopBit;
A
Alan Cox 已提交
593
	if (c_cflag & PARENB) {
594
		if  (c_cflag & PARODD)
A
Alan Cox 已提交
595
			urb_val |= SUSBCR_SPASB_OddParity;
596
		else
A
Alan Cox 已提交
597
			urb_val |= SUSBCR_SPASB_EvenParity;
598
	} else
A
Alan Cox 已提交
599
		urb_val |= SUSBCR_SPASB_NoParity;
A
Alan Cox 已提交
600 601
	tty->termios->c_cflag &= ~CMSPAR;
	tty_encode_baud_rate(tty, speed, speed);
L
Linus Torvalds 已提交
602

A
Alan Cox 已提交
603 604 605 606 607 608
	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,
609
		  NULL,
A
Alan Cox 已提交
610 611
		  0,
		  KOBIL_TIMEOUT
A
Alan Cox 已提交
612 613
		);
}
L
Linus Torvalds 已提交
614

615
static int kobil_ioctl(struct tty_struct *tty,
A
Alan Cox 已提交
616
					unsigned int cmd, unsigned long arg)
A
Alan Cox 已提交
617
{
A
Alan Cox 已提交
618
	struct usb_serial_port *port = tty->driver_data;
A
Alan Cox 已提交
619
	struct kobil_private *priv = usb_get_serial_port_data(port);
A
Alan Cox 已提交
620 621 622 623
	unsigned char *transfer_buffer;
	int transfer_buffer_length = 8;
	int result;

A
Alan Cox 已提交
624 625 626
	if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
			priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID)
		/* This device doesn't support ioctl calls */
A
Alan Cox 已提交
627
		return -ENOIOCTLCMD;
L
Linus Torvalds 已提交
628

A
Alan Cox 已提交
629
	switch (cmd) {
A
Alan Cox 已提交
630
	case TCFLSH:
631
		transfer_buffer = kmalloc(transfer_buffer_length, GFP_KERNEL);
A
Alan Cox 已提交
632 633 634 635 636 637 638 639 640 641 642 643
		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
L
Linus Torvalds 已提交
644
			);
A
Alan Cox 已提交
645

646 647
		dev_dbg(&port->dev,
			"%s - Send reset_all_queues (FLUSH) URB returns: %i", __func__, result);
L
Linus Torvalds 已提交
648
		kfree(transfer_buffer);
A
Alan Cox 已提交
649
		return (result < 0) ? -EIO: 0;
A
Alan Cox 已提交
650 651
	default:
		return -ENOIOCTLCMD;
L
Linus Torvalds 已提交
652 653 654
	}
}

655
module_usb_serial_driver(serial_drivers, id_table);
L
Linus Torvalds 已提交
656

A
Alan Cox 已提交
657 658 659
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");