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