blob: 8758e01289b9f5548c05d791869707b3b990dcdd [file] [log] [blame]
Aleksey Babahin43d186f2012-03-08 13:18:43 -08001/*
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -08002 Some of this code is credited to Linux USB open source files that are
3 distributed with Linux.
Aleksey Babahin43d186f2012-03-08 13:18:43 -08004
5 Copyright: 2007 Metrologic Instruments. All rights reserved.
6 Copyright: 2011 Azimut Ltd. <http://azimutrzn.ru/>
Aleksey Babahin43d186f2012-03-08 13:18:43 -08007*/
8
9#include <linux/kernel.h>
10#include <linux/init.h>
11#include <linux/tty.h>
12#include <linux/module.h>
13#include <linux/usb.h>
14#include <linux/errno.h>
15#include <linux/slab.h>
16#include <linux/tty_driver.h>
17#include <linux/tty_flip.h>
18#include <linux/moduleparam.h>
19#include <linux/spinlock.h>
Aleksey Babahin43d186f2012-03-08 13:18:43 -080020#include <linux/errno.h>
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -080021#include <linux/uaccess.h>
Aleksey Babahin43d186f2012-03-08 13:18:43 -080022#include <linux/usb/serial.h>
23
24/* Version Information */
25#define DRIVER_VERSION "v1.2.0.0"
26#define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver"
27
Greg Kroah-Hartman159d4d82012-03-08 13:42:41 -080028/* Product information. */
29#define FOCUS_VENDOR_ID 0x0C2E
30#define FOCUS_PRODUCT_ID 0x0720
31#define FOCUS_PRODUCT_ID_UNI 0x0710
32
33#define METROUSB_SET_REQUEST_TYPE 0x40
34#define METROUSB_SET_MODEM_CTRL_REQUEST 10
35#define METROUSB_SET_BREAK_REQUEST 0x40
36#define METROUSB_MCR_NONE 0x08 /* Deactivate DTR and RTS. */
37#define METROUSB_MCR_RTS 0x0a /* Activate RTS. */
38#define METROUSB_MCR_DTR 0x09 /* Activate DTR. */
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -080039#define WDR_TIMEOUT 5000 /* default urb timeout. */
Greg Kroah-Hartman159d4d82012-03-08 13:42:41 -080040
41/* Private data structure. */
42struct metrousb_private {
43 spinlock_t lock;
44 int throttled;
45 unsigned long control_state;
46};
47
Aleksey Babahin43d186f2012-03-08 13:18:43 -080048/* Device table list. */
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -080049static struct usb_device_id id_table[] = {
Aleksey Babahin43d186f2012-03-08 13:18:43 -080050 { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID) },
51 { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) },
Aleksey Babahin43d186f2012-03-08 13:18:43 -080052 { }, /* Terminating entry. */
53};
54MODULE_DEVICE_TABLE(usb, id_table);
55
56/* Input parameter constants. */
Greg Kroah-Hartmanfdac0f62012-03-08 13:37:32 -080057static bool debug;
Aleksey Babahin43d186f2012-03-08 13:18:43 -080058
Aleksey Babahin43d186f2012-03-08 13:18:43 -080059/* ----------------------------------------------------------------------------------------------
60 Description:
61 Read the port from the read interrupt.
62
63 Input:
64 struct urb *: urb structure to get data.
65 struct pt_regs *: pt_regs structure.
66
67 Output:
68 None:
69*/
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -080070static void metrousb_read_int_callback(struct urb *urb)
Aleksey Babahin43d186f2012-03-08 13:18:43 -080071{
72 struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
73 struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
74 struct tty_struct *tty;
75 unsigned char *data = urb->transfer_buffer;
76 int throttled = 0;
77 int result = 0;
78 unsigned long flags = 0;
79
80 dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);
81
82 switch (urb->status) {
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -080083 case 0:
84 /* Success status, read from the port. */
85 break;
86 case -ECONNRESET:
87 case -ENOENT:
88 case -ESHUTDOWN:
89 /* urb has been terminated. */
90 dbg("METRO-USB - %s - urb shutting down, port number=%d, error code=%d",
91 __FUNCTION__, port->number, result);
92 return;
93 default:
94 dbg("METRO-USB - %s - non-zero urb received, port number=%d, error code=%d",
95 __FUNCTION__, port->number, result);
96 goto exit;
Aleksey Babahin43d186f2012-03-08 13:18:43 -080097 }
98
99
100 /* Set the data read from the usb port into the serial port buffer. */
101 tty = tty_port_tty_get(&port->port);
102 if (!tty) {
103 dbg("%s - bad tty pointer - exiting", __func__);
104 return;
105 }
106
107 if (tty && urb->actual_length) {
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800108 /* Loop through the data copying each byte to the tty layer. */
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800109 tty_insert_flip_string(tty, data, urb->actual_length);
110
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800111 /* Force the data to the tty layer. */
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800112 tty_flip_buffer_push(tty);
113 }
114 tty_kref_put(tty);
115
116 /* Set any port variables. */
117 spin_lock_irqsave(&metro_priv->lock, flags);
118 throttled = metro_priv->throttled;
119 spin_unlock_irqrestore(&metro_priv->lock, flags);
120
121 /* Continue trying to read if set. */
122 if (!throttled) {
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800123 usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
124 usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress),
125 port->interrupt_in_urb->transfer_buffer,
126 port->interrupt_in_urb->transfer_buffer_length,
127 metrousb_read_int_callback, port, 1);
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800128
129 result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
130
131 if (result) {
132 dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d",
133 __FUNCTION__, port->number, result);
134 }
135 }
136 return;
137
138exit:
139 /* Try to resubmit the urb. */
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800140 result = usb_submit_urb(urb, GFP_ATOMIC);
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800141 if (result) {
142 dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d",
143 __FUNCTION__, port->number, result);
144 }
145}
146
147/* ----------------------------------------------------------------------------------------------
148 Description:
Greg Kroah-Hartman9fbd1642012-03-08 13:55:41 -0800149 Clean up any urbs and port information.
150
151 Input:
152 struct usb_serial_port *: pointer to a usb_serial_port structure.
153
154 Output:
155 int: Returns true (0) if successful, false otherwise.
156*/
157static void metrousb_cleanup(struct usb_serial_port *port)
158{
159 dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);
160
161 if (port->serial->dev) {
162 /* Shutdown any interrupt in urbs. */
163 if (port->interrupt_in_urb) {
164 usb_unlink_urb(port->interrupt_in_urb);
165 usb_kill_urb(port->interrupt_in_urb);
166 }
167 }
168}
169
170/* ----------------------------------------------------------------------------------------------
171 Description:
172 Open the drivers serial port.
173
174 Input:
175 struct usb_serial_port *: pointer to a usb_serial_port structure.
176 struct file *: pointer to a file structure.
177
178 Output:
179 int: Returns true (0) if successful, false otherwise.
180*/
181static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port)
182{
183 struct usb_serial *serial = port->serial;
184 struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
185 unsigned long flags = 0;
186 int result = 0;
187
188 dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);
189
190 /* Make sure the urb is initialized. */
191 if (!port->interrupt_in_urb) {
192 dbg("METRO-USB - %s - interrupt urb not initialized for port number=%d", __FUNCTION__, port->number);
193 return -ENODEV;
194 }
195
196 /* Set the private data information for the port. */
197 spin_lock_irqsave(&metro_priv->lock, flags);
198 metro_priv->control_state = 0;
199 metro_priv->throttled = 0;
200 spin_unlock_irqrestore(&metro_priv->lock, flags);
201
202 /*
203 * Force low_latency on so that our tty_push actually forces the data
204 * through, otherwise it is scheduled, and with high data rates (like
205 * with OHCI) data can get lost.
206 */
207 if (tty)
208 tty->low_latency = 1;
209
210 /* Clear the urb pipe. */
211 usb_clear_halt(serial->dev, port->interrupt_in_urb->pipe);
212
213 /* Start reading from the device */
214 usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
215 usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
216 port->interrupt_in_urb->transfer_buffer,
217 port->interrupt_in_urb->transfer_buffer_length,
218 metrousb_read_int_callback, port, 1);
219 result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
220
221 if (result) {
222 dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d"
223 , __FUNCTION__, port->number, result);
224 goto exit;
225 }
226
227 dbg("METRO-USB - %s - port open for port number=%d", __FUNCTION__, port->number);
228exit:
229 return result;
230}
231
232/* ----------------------------------------------------------------------------------------------
233 Description:
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800234 Set the modem control state for the entered serial port.
235
236 Input:
237 struct usb_serial_port *: pointer to a usb_serial_port structure.
238 unsigned int: control state value to set.
239
240 Output:
241 int: Returns true (0) if successful, false otherwise.
242*/
243static int metrousb_set_modem_ctrl(struct usb_serial *serial, unsigned int control_state)
244{
245 int retval = 0;
246 unsigned char mcr = METROUSB_MCR_NONE;
247
248 dbg("METRO-USB - %s - control state=%d", __FUNCTION__, control_state);
249
250 /* Set the modem control value. */
251 if (control_state & TIOCM_DTR)
252 mcr |= METROUSB_MCR_DTR;
253 if (control_state & TIOCM_RTS)
254 mcr |= METROUSB_MCR_RTS;
255
256 /* Send the command to the usb port. */
257 retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
258 METROUSB_SET_REQUEST_TYPE, METROUSB_SET_MODEM_CTRL_REQUEST,
259 control_state, 0, NULL, 0, WDR_TIMEOUT);
260 if (retval < 0)
261 dbg("METRO-USB - %s - set modem ctrl=0x%x failed, error code=%d", __FUNCTION__, mcr, retval);
262
263 return retval;
264}
265
266
267/* ----------------------------------------------------------------------------------------------
268 Description:
269 Shutdown the driver.
270
271 Input:
272 struct usb_serial *: pointer to a usb-serial structure.
273
274 Output:
275 int: Returns true (0) if successful, false otherwise.
276*/
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800277static void metrousb_shutdown(struct usb_serial *serial)
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800278{
279 int i = 0;
280
281 dbg("METRO-USB - %s", __FUNCTION__);
282
283 /* Stop reading and writing on all ports. */
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800284 for (i = 0; i < serial->num_ports; ++i) {
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800285 /* Close any open urbs. */
286 metrousb_cleanup(serial->port[i]);
287
288 /* Free memory. */
289 kfree(usb_get_serial_port_data(serial->port[i]));
290 usb_set_serial_port_data(serial->port[i], NULL);
291
292 dbg("METRO-USB - %s - freed port number=%d", __FUNCTION__, serial->port[i]->number);
293 }
294}
295
296/* ----------------------------------------------------------------------------------------------
297 Description:
298 Startup the driver.
299
300 Input:
301 struct usb_serial *: pointer to a usb-serial structure.
302
303 Output:
304 int: Returns true (0) if successful, false otherwise.
305*/
306static int metrousb_startup(struct usb_serial *serial)
307{
308 struct metrousb_private *metro_priv;
309 struct usb_serial_port *port;
310 int i = 0;
311
312 dbg("METRO-USB - %s", __FUNCTION__);
313
314 /* Loop through the serial ports setting up the private structures.
315 * Currently we only use one port. */
316 for (i = 0; i < serial->num_ports; ++i) {
317 port = serial->port[i];
318
319 /* Declare memory. */
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800320 metro_priv = kmalloc(sizeof(struct metrousb_private), GFP_KERNEL);
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800321 if (!metro_priv)
322 return -ENOMEM;
323
324 /* Clear memory. */
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800325 memset(metro_priv, 0x00, sizeof(struct metrousb_private));
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800326
327 /* Initialize memory. */
328 spin_lock_init(&metro_priv->lock);
329 usb_set_serial_port_data(port, metro_priv);
330
331 dbg("METRO-USB - %s - port number=%d.", __FUNCTION__, port->number);
332 }
333
334 return 0;
335}
336
337/* ----------------------------------------------------------------------------------------------
338 Description:
339 Set the serial port throttle to stop reading from the port.
340
341 Input:
342 struct usb_serial_port *: pointer to a usb_serial_port structure.
343
344 Output:
345 None:
346*/
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800347static void metrousb_throttle(struct tty_struct *tty)
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800348{
349 struct usb_serial_port *port = tty->driver_data;
350 struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
351 unsigned long flags = 0;
352
353 dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);
354
355 /* Set the private information for the port to stop reading data. */
356 spin_lock_irqsave(&metro_priv->lock, flags);
357 metro_priv->throttled = 1;
358 spin_unlock_irqrestore(&metro_priv->lock, flags);
359}
360
361/* ----------------------------------------------------------------------------------------------
362 Description:
363 Get the serial port control line states.
364
365 Input:
366 struct usb_serial_port *: pointer to a usb_serial_port structure.
367 struct file *: pointer to a file structure.
368
369 Output:
370 int: Returns the state of the control lines.
371*/
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800372static int metrousb_tiocmget(struct tty_struct *tty)
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800373{
374 unsigned long control_state = 0;
375 struct usb_serial_port *port = tty->driver_data;
376 struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
377 unsigned long flags = 0;
378
379 dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);
380
381 spin_lock_irqsave(&metro_priv->lock, flags);
382 control_state = metro_priv->control_state;
383 spin_unlock_irqrestore(&metro_priv->lock, flags);
384
385 return control_state;
386}
387
388/* ----------------------------------------------------------------------------------------------
389 Description:
390 Set the serial port control line states.
391
392 Input:
393 struct usb_serial_port *: pointer to a usb_serial_port structure.
394 struct file *: pointer to a file structure.
395 unsigned int: line state to set.
396 unsigned int: line state to clear.
397
398 Output:
399 int: Returns the state of the control lines.
400*/
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800401static int metrousb_tiocmset(struct tty_struct *tty,
402 unsigned int set, unsigned int clear)
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800403{
404 struct usb_serial_port *port = tty->driver_data;
405 struct usb_serial *serial = port->serial;
406 struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
407 unsigned long flags = 0;
408 unsigned long control_state = 0;
409
410 dbg("METRO-USB - %s - port number=%d, set=%d, clear=%d", __FUNCTION__, port->number, set, clear);
411
412 spin_lock_irqsave(&metro_priv->lock, flags);
413 control_state = metro_priv->control_state;
414
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800415 /* Set the RTS and DTR values. */
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800416 if (set & TIOCM_RTS)
417 control_state |= TIOCM_RTS;
418 if (set & TIOCM_DTR)
419 control_state |= TIOCM_DTR;
420 if (clear & TIOCM_RTS)
421 control_state &= ~TIOCM_RTS;
422 if (clear & TIOCM_DTR)
423 control_state &= ~TIOCM_DTR;
424
425 metro_priv->control_state = control_state;
426 spin_unlock_irqrestore(&metro_priv->lock, flags);
427 return metrousb_set_modem_ctrl(serial, control_state);
428}
429
430/* ----------------------------------------------------------------------------------------------
431 Description:
432 Set the serial port unthrottle to resume reading from the port.
433
434 Input:
435 struct usb_serial_port *: pointer to a usb_serial_port structure.
436
437 Output:
438 None:
439*/
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800440static void metrousb_unthrottle(struct tty_struct *tty)
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800441{
442 struct usb_serial_port *port = tty->driver_data;
443 struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
444 unsigned long flags = 0;
445 int result = 0;
446
447 dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);
448
449 /* Set the private information for the port to resume reading data. */
450 spin_lock_irqsave(&metro_priv->lock, flags);
451 metro_priv->throttled = 0;
452 spin_unlock_irqrestore(&metro_priv->lock, flags);
453
454 /* Submit the urb to read from the port. */
455 port->interrupt_in_urb->dev = port->serial->dev;
456 result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
457 if (result) {
458 dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d",
459 __FUNCTION__, port->number, result);
460 }
461}
462
Greg Kroah-Hartman9fbd1642012-03-08 13:55:41 -0800463/* Driver structure. */
464static struct usb_driver metrousb_driver = {
465 .name = "metro-usb",
466 .probe = usb_serial_probe,
467 .disconnect = usb_serial_disconnect,
468 .id_table = id_table
469};
470
471/* Device structure. */
472static struct usb_serial_driver metrousb_device = {
473 .driver = {
474 .owner = THIS_MODULE,
475 .name = "metro-usb",
476 },
477 .description = "Metrologic USB to serial converter.",
478 .id_table = id_table,
479 .num_ports = 1,
480 .open = metrousb_open,
481 .close = metrousb_cleanup,
482 .read_int_callback = metrousb_read_int_callback,
483 .attach = metrousb_startup,
484 .release = metrousb_shutdown,
485 .throttle = metrousb_throttle,
486 .unthrottle = metrousb_unthrottle,
487 .tiocmget = metrousb_tiocmget,
488 .tiocmset = metrousb_tiocmset,
489};
490
491static struct usb_serial_driver * const serial_drivers[] = {
492 &metrousb_device,
493 NULL,
494};
495
Greg Kroah-Hartman1935e352012-03-08 13:39:53 -0800496module_usb_serial_driver(metrousb_driver, serial_drivers);
497
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800498MODULE_LICENSE("GPL");
Greg Kroah-Hartmand4cbd6e2012-03-08 13:50:54 -0800499MODULE_AUTHOR("Philip Nicastro");
500MODULE_AUTHOR("Aleksey Babahin <tamerlan311@gmail.com>");
501MODULE_DESCRIPTION(DRIVER_DESC);
Aleksey Babahin43d186f2012-03-08 13:18:43 -0800502
503/* Module input parameters */
504module_param(debug, bool, S_IRUGO | S_IWUSR);
505MODULE_PARM_DESC(debug, "Print debug info (bool 1=on, 0=off)");