aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2009-02-06 21:30:56 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-03-24 19:20:30 -0400
commit648d4e16567eae4c643bd2125e91128f06c0d3ad (patch)
tree961c5f25f161002893b9a879d5e8d0b1c5b44a74 /drivers/usb/serial
parentf6d92a05c86754d62eabc84856d2035d0de3ddc3 (diff)
USB: serial: opticon: add write support
This patch allows data to be sent to the scanner. Cc: Kees Stoop <kees.stoop@opticon.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r--drivers/usb/serial/opticon.c124
1 files changed, 122 insertions, 2 deletions
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 00d5c60adeda..8c87a49ee2bb 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -1,8 +1,8 @@
1/* 1/*
2 * Opticon USB barcode to serial driver 2 * Opticon USB barcode to serial driver
3 * 3 *
4 * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de> 4 * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de>
5 * Copyright (C) 2008 Novell Inc. 5 * Copyright (C) 2008 - 2009 Novell Inc.
6 * 6 *
7 * This program is free software; you can redistribute it and/or 7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version 8 * modify it under the terms of the GNU General Public License version
@@ -40,8 +40,12 @@ struct opticon_private {
40 bool throttled; 40 bool throttled;
41 bool actually_throttled; 41 bool actually_throttled;
42 bool rts; 42 bool rts;
43 int outstanding_urbs;
43}; 44};
44 45
46/* max number of write urbs in flight */
47#define URB_UPPER_LIMIT 4
48
45static void opticon_bulk_callback(struct urb *urb) 49static void opticon_bulk_callback(struct urb *urb)
46{ 50{
47 struct opticon_private *priv = urb->context; 51 struct opticon_private *priv = urb->context;
@@ -188,6 +192,120 @@ static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port,
188 usb_kill_urb(priv->bulk_read_urb); 192 usb_kill_urb(priv->bulk_read_urb);
189} 193}
190 194
195static void opticon_write_bulk_callback(struct urb *urb)
196{
197 struct opticon_private *priv = urb->context;
198 int status = urb->status;
199 unsigned long flags;
200
201 /* free up the transfer buffer, as usb_free_urb() does not do this */
202 kfree(urb->transfer_buffer);
203
204 if (status)
205 dbg("%s - nonzero write bulk status received: %d",
206 __func__, status);
207
208 spin_lock_irqsave(&priv->lock, flags);
209 --priv->outstanding_urbs;
210 spin_unlock_irqrestore(&priv->lock, flags);
211
212 usb_serial_port_softint(priv->port);
213}
214
215static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
216 const unsigned char *buf, int count)
217{
218 struct opticon_private *priv = usb_get_serial_data(port->serial);
219 struct usb_serial *serial = port->serial;
220 struct urb *urb;
221 unsigned char *buffer;
222 unsigned long flags;
223 int status;
224
225 dbg("%s - port %d", __func__, port->number);
226
227 spin_lock_irqsave(&priv->lock, flags);
228 if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
229 spin_unlock_irqrestore(&priv->lock, flags);
230 dbg("%s - write limit hit\n", __func__);
231 return 0;
232 }
233 priv->outstanding_urbs++;
234 spin_unlock_irqrestore(&priv->lock, flags);
235
236 buffer = kmalloc(count, GFP_ATOMIC);
237 if (!buffer) {
238 dev_err(&port->dev, "out of memory\n");
239 count = -ENOMEM;
240 goto error_no_buffer;
241 }
242
243 urb = usb_alloc_urb(0, GFP_ATOMIC);
244 if (!urb) {
245 dev_err(&port->dev, "no more free urbs\n");
246 count = -ENOMEM;
247 goto error_no_urb;
248 }
249
250 memcpy(buffer, buf, count);
251
252 usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
253
254 usb_fill_bulk_urb(urb, serial->dev,
255 usb_sndbulkpipe(serial->dev,
256 port->bulk_out_endpointAddress),
257 buffer, count, opticon_write_bulk_callback, priv);
258
259 /* send it down the pipe */
260 status = usb_submit_urb(urb, GFP_ATOMIC);
261 if (status) {
262 dev_err(&port->dev,
263 "%s - usb_submit_urb(write bulk) failed with status = %d\n",
264 __func__, status);
265 count = status;
266 goto error;
267 }
268
269 /* we are done with this urb, so let the host driver
270 * really free it when it is finished with it */
271 usb_free_urb(urb);
272
273 return count;
274error:
275 usb_free_urb(urb);
276error_no_urb:
277 kfree(buffer);
278error_no_buffer:
279 spin_lock_irqsave(&priv->lock, flags);
280 --priv->outstanding_urbs;
281 spin_unlock_irqrestore(&priv->lock, flags);
282 return count;
283}
284
285static int opticon_write_room(struct tty_struct *tty)
286{
287 struct usb_serial_port *port = tty->driver_data;
288 struct opticon_private *priv = usb_get_serial_data(port->serial);
289 unsigned long flags;
290
291 dbg("%s - port %d", __func__, port->number);
292
293 /*
294 * We really can take almost anything the user throws at us
295 * but let's pick a nice big number to tell the tty
296 * layer that we have lots of free space, unless we don't.
297 */
298 spin_lock_irqsave(&priv->lock, flags);
299 if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) {
300 spin_unlock_irqrestore(&priv->lock, flags);
301 dbg("%s - write limit hit\n", __func__);
302 return 0;
303 }
304 spin_unlock_irqrestore(&priv->lock, flags);
305
306 return 2048;
307}
308
191static void opticon_throttle(struct tty_struct *tty) 309static void opticon_throttle(struct tty_struct *tty)
192{ 310{
193 struct usb_serial_port *port = tty->driver_data; 311 struct usb_serial_port *port = tty->driver_data;
@@ -352,6 +470,8 @@ static struct usb_serial_driver opticon_device = {
352 .attach = opticon_startup, 470 .attach = opticon_startup,
353 .open = opticon_open, 471 .open = opticon_open,
354 .close = opticon_close, 472 .close = opticon_close,
473 .write = opticon_write,
474 .write_room = opticon_write_room,
355 .shutdown = opticon_shutdown, 475 .shutdown = opticon_shutdown,
356 .throttle = opticon_throttle, 476 .throttle = opticon_throttle,
357 .unthrottle = opticon_unthrottle, 477 .unthrottle = opticon_unthrottle,