diff options
author | Martin Jansen <martin.jansen@opticon.com> | 2011-02-24 08:50:16 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-02-25 14:41:12 -0500 |
commit | 309a057932ab20057da9fe4cb18fb61803dfc924 (patch) | |
tree | 18c3287eae4f7cc1fc127b93d6dc16a88a25f10f /drivers/usb/serial/opticon.c | |
parent | c9642374d0e969e8c17f4f31cd1a2bd111634227 (diff) |
USB: opticon: add rts and cts support
Add support for RTS and CTS line status
Signed-off-by: Martin Jansen <martin.jansen@opticon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/serial/opticon.c')
-rw-r--r-- | drivers/usb/serial/opticon.c | 156 |
1 files changed, 112 insertions, 44 deletions
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index eda1f9266c4e..ce82396fc4e8 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Opticon USB barcode to serial driver | 2 | * Opticon USB barcode to serial driver |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Martin Jansen <martin.jansen@opticon.com> | ||
4 | * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de> | 5 | * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de> |
5 | * Copyright (C) 2008 - 2009 Novell Inc. | 6 | * Copyright (C) 2008 - 2009 Novell Inc. |
6 | * | 7 | * |
@@ -21,6 +22,16 @@ | |||
21 | #include <linux/usb/serial.h> | 22 | #include <linux/usb/serial.h> |
22 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
23 | 24 | ||
25 | #define CONTROL_RTS 0x02 | ||
26 | #define RESEND_CTS_STATE 0x03 | ||
27 | |||
28 | /* max number of write urbs in flight */ | ||
29 | #define URB_UPPER_LIMIT 8 | ||
30 | |||
31 | /* This driver works for the Opticon 1D barcode reader | ||
32 | * an examples of 1D barcode types are EAN, UPC, Code39, IATA etc.. */ | ||
33 | #define DRIVER_DESC "Opticon USB barcode to serial driver (1D)" | ||
34 | |||
24 | static int debug; | 35 | static int debug; |
25 | 36 | ||
26 | static const struct usb_device_id id_table[] = { | 37 | static const struct usb_device_id id_table[] = { |
@@ -42,13 +53,13 @@ struct opticon_private { | |||
42 | bool throttled; | 53 | bool throttled; |
43 | bool actually_throttled; | 54 | bool actually_throttled; |
44 | bool rts; | 55 | bool rts; |
56 | bool cts; | ||
45 | int outstanding_urbs; | 57 | int outstanding_urbs; |
46 | }; | 58 | }; |
47 | 59 | ||
48 | /* max number of write urbs in flight */ | ||
49 | #define URB_UPPER_LIMIT 4 | ||
50 | 60 | ||
51 | static void opticon_bulk_callback(struct urb *urb) | 61 | |
62 | static void opticon_read_bulk_callback(struct urb *urb) | ||
52 | { | 63 | { |
53 | struct opticon_private *priv = urb->context; | 64 | struct opticon_private *priv = urb->context; |
54 | unsigned char *data = urb->transfer_buffer; | 65 | unsigned char *data = urb->transfer_buffer; |
@@ -57,6 +68,7 @@ static void opticon_bulk_callback(struct urb *urb) | |||
57 | struct tty_struct *tty; | 68 | struct tty_struct *tty; |
58 | int result; | 69 | int result; |
59 | int data_length; | 70 | int data_length; |
71 | unsigned long flags; | ||
60 | 72 | ||
61 | dbg("%s - port %d", __func__, port->number); | 73 | dbg("%s - port %d", __func__, port->number); |
62 | 74 | ||
@@ -87,10 +99,10 @@ static void opticon_bulk_callback(struct urb *urb) | |||
87 | * Data from the device comes with a 2 byte header: | 99 | * Data from the device comes with a 2 byte header: |
88 | * | 100 | * |
89 | * <0x00><0x00>data... | 101 | * <0x00><0x00>data... |
90 | * This is real data to be sent to the tty layer | 102 | * This is real data to be sent to the tty layer |
91 | * <0x00><0x01)level | 103 | * <0x00><0x01)level |
92 | * This is a RTS level change, the third byte is the RTS | 104 | * This is a CTS level change, the third byte is the CTS |
93 | * value (0 for low, 1 for high). | 105 | * value (0 for low, 1 for high). |
94 | */ | 106 | */ |
95 | if ((data[0] == 0x00) && (data[1] == 0x00)) { | 107 | if ((data[0] == 0x00) && (data[1] == 0x00)) { |
96 | /* real data, send it to the tty layer */ | 108 | /* real data, send it to the tty layer */ |
@@ -103,10 +115,13 @@ static void opticon_bulk_callback(struct urb *urb) | |||
103 | } | 115 | } |
104 | } else { | 116 | } else { |
105 | if ((data[0] == 0x00) && (data[1] == 0x01)) { | 117 | if ((data[0] == 0x00) && (data[1] == 0x01)) { |
118 | spin_lock_irqsave(&priv->lock, flags); | ||
119 | /* CTS status infomation package */ | ||
106 | if (data[2] == 0x00) | 120 | if (data[2] == 0x00) |
107 | priv->rts = false; | 121 | priv->cts = false; |
108 | else | 122 | else |
109 | priv->rts = true; | 123 | priv->cts = true; |
124 | spin_unlock_irqrestore(&priv->lock, flags); | ||
110 | } else { | 125 | } else { |
111 | dev_dbg(&priv->udev->dev, | 126 | dev_dbg(&priv->udev->dev, |
112 | "Unknown data packet received from the device:" | 127 | "Unknown data packet received from the device:" |
@@ -129,7 +144,7 @@ exit: | |||
129 | usb_rcvbulkpipe(priv->udev, | 144 | usb_rcvbulkpipe(priv->udev, |
130 | priv->bulk_address), | 145 | priv->bulk_address), |
131 | priv->bulk_in_buffer, priv->buffer_size, | 146 | priv->bulk_in_buffer, priv->buffer_size, |
132 | opticon_bulk_callback, priv); | 147 | opticon_read_bulk_callback, priv); |
133 | result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); | 148 | result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); |
134 | if (result) | 149 | if (result) |
135 | dev_err(&port->dev, | 150 | dev_err(&port->dev, |
@@ -140,6 +155,24 @@ exit: | |||
140 | spin_unlock(&priv->lock); | 155 | spin_unlock(&priv->lock); |
141 | } | 156 | } |
142 | 157 | ||
158 | static int send_control_msg(struct usb_serial_port *port, u8 requesttype, | ||
159 | u8 val) | ||
160 | { | ||
161 | struct usb_serial *serial = port->serial; | ||
162 | int retval; | ||
163 | u8 buffer[2]; | ||
164 | |||
165 | buffer[0] = val; | ||
166 | /* Send the message to the vendor control endpoint | ||
167 | * of the connected device */ | ||
168 | retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), | ||
169 | requesttype, | ||
170 | USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, | ||
171 | 0, 0, buffer, 1, 0); | ||
172 | |||
173 | return retval; | ||
174 | } | ||
175 | |||
143 | static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) | 176 | static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) |
144 | { | 177 | { |
145 | struct opticon_private *priv = usb_get_serial_data(port->serial); | 178 | struct opticon_private *priv = usb_get_serial_data(port->serial); |
@@ -152,19 +185,30 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) | |||
152 | priv->throttled = false; | 185 | priv->throttled = false; |
153 | priv->actually_throttled = false; | 186 | priv->actually_throttled = false; |
154 | priv->port = port; | 187 | priv->port = port; |
188 | priv->rts = false; | ||
155 | spin_unlock_irqrestore(&priv->lock, flags); | 189 | spin_unlock_irqrestore(&priv->lock, flags); |
156 | 190 | ||
157 | /* Start reading from the device */ | 191 | /* Clear RTS line */ |
192 | send_control_msg(port, CONTROL_RTS, 0); | ||
193 | |||
194 | /* Setup the read URB and start reading from the device */ | ||
158 | usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, | 195 | usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, |
159 | usb_rcvbulkpipe(priv->udev, | 196 | usb_rcvbulkpipe(priv->udev, |
160 | priv->bulk_address), | 197 | priv->bulk_address), |
161 | priv->bulk_in_buffer, priv->buffer_size, | 198 | priv->bulk_in_buffer, priv->buffer_size, |
162 | opticon_bulk_callback, priv); | 199 | opticon_read_bulk_callback, priv); |
200 | |||
201 | /* clear the halt status of the enpoint */ | ||
202 | usb_clear_halt(priv->udev, priv->bulk_read_urb->pipe); | ||
203 | |||
163 | result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL); | 204 | result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL); |
164 | if (result) | 205 | if (result) |
165 | dev_err(&port->dev, | 206 | dev_err(&port->dev, |
166 | "%s - failed resubmitting read urb, error %d\n", | 207 | "%s - failed resubmitting read urb, error %d\n", |
167 | __func__, result); | 208 | __func__, result); |
209 | /* Request CTS line state, sometimes during opening the current | ||
210 | * CTS state can be missed. */ | ||
211 | send_control_msg(port, RESEND_CTS_STATE, 1); | ||
168 | return result; | 212 | return result; |
169 | } | 213 | } |
170 | 214 | ||
@@ -178,7 +222,7 @@ static void opticon_close(struct usb_serial_port *port) | |||
178 | usb_kill_urb(priv->bulk_read_urb); | 222 | usb_kill_urb(priv->bulk_read_urb); |
179 | } | 223 | } |
180 | 224 | ||
181 | static void opticon_write_bulk_callback(struct urb *urb) | 225 | static void opticon_write_control_callback(struct urb *urb) |
182 | { | 226 | { |
183 | struct opticon_private *priv = urb->context; | 227 | struct opticon_private *priv = urb->context; |
184 | int status = urb->status; | 228 | int status = urb->status; |
@@ -210,6 +254,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
210 | unsigned char *buffer; | 254 | unsigned char *buffer; |
211 | unsigned long flags; | 255 | unsigned long flags; |
212 | int status; | 256 | int status; |
257 | struct usb_ctrlrequest *dr; | ||
213 | 258 | ||
214 | dbg("%s - port %d", __func__, port->number); | 259 | dbg("%s - port %d", __func__, port->number); |
215 | 260 | ||
@@ -226,6 +271,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
226 | if (!buffer) { | 271 | if (!buffer) { |
227 | dev_err(&port->dev, "out of memory\n"); | 272 | dev_err(&port->dev, "out of memory\n"); |
228 | count = -ENOMEM; | 273 | count = -ENOMEM; |
274 | |||
229 | goto error_no_buffer; | 275 | goto error_no_buffer; |
230 | } | 276 | } |
231 | 277 | ||
@@ -240,35 +286,28 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, | |||
240 | 286 | ||
241 | usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); | 287 | usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); |
242 | 288 | ||
243 | if (port->bulk_out_endpointAddress) { | 289 | /* The conncected devices do not have a bulk write endpoint, |
244 | usb_fill_bulk_urb(urb, serial->dev, | 290 | * to transmit data to de barcode device the control endpoint is used */ |
245 | usb_sndbulkpipe(serial->dev, | 291 | dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); |
246 | port->bulk_out_endpointAddress), | 292 | if (!dr) |
247 | buffer, count, opticon_write_bulk_callback, priv); | 293 | return -ENOMEM; |
248 | } else { | 294 | |
249 | struct usb_ctrlrequest *dr; | 295 | dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT; |
250 | 296 | dr->bRequest = 0x01; | |
251 | dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); | 297 | dr->wValue = 0; |
252 | if (!dr) | 298 | dr->wIndex = 0; |
253 | return -ENOMEM; | 299 | dr->wLength = cpu_to_le16(count); |
254 | 300 | ||
255 | dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT; | 301 | usb_fill_control_urb(urb, serial->dev, |
256 | dr->bRequest = 0x01; | 302 | usb_sndctrlpipe(serial->dev, 0), |
257 | dr->wValue = 0; | 303 | (unsigned char *)dr, buffer, count, |
258 | dr->wIndex = 0; | 304 | opticon_write_control_callback, priv); |
259 | dr->wLength = cpu_to_le16(count); | ||
260 | |||
261 | usb_fill_control_urb(urb, serial->dev, | ||
262 | usb_sndctrlpipe(serial->dev, 0), | ||
263 | (unsigned char *)dr, buffer, count, | ||
264 | opticon_write_bulk_callback, priv); | ||
265 | } | ||
266 | 305 | ||
267 | /* send it down the pipe */ | 306 | /* send it down the pipe */ |
268 | status = usb_submit_urb(urb, GFP_ATOMIC); | 307 | status = usb_submit_urb(urb, GFP_ATOMIC); |
269 | if (status) { | 308 | if (status) { |
270 | dev_err(&port->dev, | 309 | dev_err(&port->dev, |
271 | "%s - usb_submit_urb(write bulk) failed with status = %d\n", | 310 | "%s - usb_submit_urb(write endpoint) failed status = %d\n", |
272 | __func__, status); | 311 | __func__, status); |
273 | count = status; | 312 | count = status; |
274 | goto error; | 313 | goto error; |
@@ -360,16 +399,49 @@ static int opticon_tiocmget(struct tty_struct *tty, struct file *file) | |||
360 | int result = 0; | 399 | int result = 0; |
361 | 400 | ||
362 | dbg("%s - port %d", __func__, port->number); | 401 | dbg("%s - port %d", __func__, port->number); |
402 | if (!usb_get_intfdata(port->serial->interface)) | ||
403 | return -ENODEV; | ||
363 | 404 | ||
364 | spin_lock_irqsave(&priv->lock, flags); | 405 | spin_lock_irqsave(&priv->lock, flags); |
365 | if (priv->rts) | 406 | if (priv->rts) |
366 | result = TIOCM_RTS; | 407 | result |= TIOCM_RTS; |
408 | if (priv->cts) | ||
409 | result |= TIOCM_CTS; | ||
367 | spin_unlock_irqrestore(&priv->lock, flags); | 410 | spin_unlock_irqrestore(&priv->lock, flags); |
368 | 411 | ||
369 | dbg("%s - %x", __func__, result); | 412 | dbg("%s - %x", __func__, result); |
370 | return result; | 413 | return result; |
371 | } | 414 | } |
372 | 415 | ||
416 | static int opticon_tiocmset(struct tty_struct *tty, struct file *file, | ||
417 | unsigned int set, unsigned int clear) | ||
418 | { | ||
419 | struct usb_serial_port *port = tty->driver_data; | ||
420 | struct opticon_private *priv = usb_get_serial_data(port->serial); | ||
421 | unsigned long flags; | ||
422 | bool rts; | ||
423 | bool changed = false; | ||
424 | |||
425 | if (!usb_get_intfdata(port->serial->interface)) | ||
426 | return -ENODEV; | ||
427 | /* We only support RTS so we only handle that */ | ||
428 | spin_lock_irqsave(&priv->lock, flags); | ||
429 | |||
430 | rts = priv->rts; | ||
431 | if (set & TIOCM_RTS) | ||
432 | priv->rts = true; | ||
433 | if (clear & TIOCM_RTS) | ||
434 | priv->rts = false; | ||
435 | changed = rts ^ priv->rts; | ||
436 | spin_unlock_irqrestore(&priv->lock, flags); | ||
437 | |||
438 | if (!changed) | ||
439 | return 0; | ||
440 | |||
441 | /* Send the new RTS state to the connected device */ | ||
442 | return send_control_msg(port, CONTROL_RTS, !rts); | ||
443 | } | ||
444 | |||
373 | static int get_serial_info(struct opticon_private *priv, | 445 | static int get_serial_info(struct opticon_private *priv, |
374 | struct serial_struct __user *serial) | 446 | struct serial_struct __user *serial) |
375 | { | 447 | { |
@@ -431,6 +503,7 @@ static int opticon_startup(struct usb_serial *serial) | |||
431 | priv->serial = serial; | 503 | priv->serial = serial; |
432 | priv->port = serial->port[0]; | 504 | priv->port = serial->port[0]; |
433 | priv->udev = serial->dev; | 505 | priv->udev = serial->dev; |
506 | priv->outstanding_urbs = 0; /* Init the outstanding urbs */ | ||
434 | 507 | ||
435 | /* find our bulk endpoint */ | 508 | /* find our bulk endpoint */ |
436 | intf = serial->interface->altsetting; | 509 | intf = serial->interface->altsetting; |
@@ -456,13 +529,6 @@ static int opticon_startup(struct usb_serial *serial) | |||
456 | 529 | ||
457 | priv->bulk_address = endpoint->bEndpointAddress; | 530 | priv->bulk_address = endpoint->bEndpointAddress; |
458 | 531 | ||
459 | /* set up our bulk urb */ | ||
460 | usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, | ||
461 | usb_rcvbulkpipe(priv->udev, | ||
462 | endpoint->bEndpointAddress), | ||
463 | priv->bulk_in_buffer, priv->buffer_size, | ||
464 | opticon_bulk_callback, priv); | ||
465 | |||
466 | bulk_in_found = true; | 532 | bulk_in_found = true; |
467 | break; | 533 | break; |
468 | } | 534 | } |
@@ -558,6 +624,7 @@ static struct usb_serial_driver opticon_device = { | |||
558 | .unthrottle = opticon_unthrottle, | 624 | .unthrottle = opticon_unthrottle, |
559 | .ioctl = opticon_ioctl, | 625 | .ioctl = opticon_ioctl, |
560 | .tiocmget = opticon_tiocmget, | 626 | .tiocmget = opticon_tiocmget, |
627 | .tiocmset = opticon_tiocmset, | ||
561 | }; | 628 | }; |
562 | 629 | ||
563 | static int __init opticon_init(void) | 630 | static int __init opticon_init(void) |
@@ -581,6 +648,7 @@ static void __exit opticon_exit(void) | |||
581 | 648 | ||
582 | module_init(opticon_init); | 649 | module_init(opticon_init); |
583 | module_exit(opticon_exit); | 650 | module_exit(opticon_exit); |
651 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
584 | MODULE_LICENSE("GPL"); | 652 | MODULE_LICENSE("GPL"); |
585 | 653 | ||
586 | module_param(debug, bool, S_IRUGO | S_IWUSR); | 654 | module_param(debug, bool, S_IRUGO | S_IWUSR); |