From: Martin Jansen <martin.jansen@xxxxxxxxxxx> Add support for writing data to the barcode device Add support for RTS and CTS line status Fix header part was also transmitted in bulk read callback Signed-off-by: Martin Jansen <martin.jansen@xxxxxxxxxxx> --- --- linux-2.6.35/drivers/usb/serial/opticon.c.orig 2011-02-22 07:33:50.917536943 -0800 +++ linux-2.6.35/drivers/usb/serial/opticon.c 2011-02-24 01:35:29.950081957 -0800 @@ -1,6 +1,7 @@ /* * Opticon USB barcode to serial driver * + * Copyright (C) 2011 Martin Jansen <martin.jansen@xxxxxxxxxxx> * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@xxxxxxx> * Copyright (C) 2008 - 2009 Novell Inc. * @@ -21,6 +22,16 @@ #include <linux/usb/serial.h> #include <linux/uaccess.h> +#define CONTROL_RTS 0x02 +#define RESEND_CTS_STATE 0x03 + +/* max number of write urbs in flight */ +#define URB_UPPER_LIMIT 8 + +/* This driver works for the Opticon 1D barcode reader + * an examples of 1D barcode types are EAN, UPC, Code39, IATA etc.. */ +#define DRIVER_DESC "Opticon USB barcode to serial driver (1D)" + static int debug; static const struct usb_device_id id_table[] = { @@ -37,18 +48,19 @@ struct opticon_private { unsigned char *bulk_in_buffer; struct urb *bulk_read_urb; int buffer_size; + char ctrl_write_buf[8]; /* Ctrl buffer for the vendor commands */ u8 bulk_address; spinlock_t lock; /* protects the following flags */ bool throttled; bool actually_throttled; bool rts; + bool cts; int outstanding_urbs; }; -/* max number of write urbs in flight */ -#define URB_UPPER_LIMIT 4 -static void opticon_bulk_callback(struct urb *urb) + +static void opticon_read_bulk_callback(struct urb *urb) { struct opticon_private *priv = urb->context; unsigned char *data = urb->transfer_buffer; @@ -57,6 +69,7 @@ static void opticon_bulk_callback(struct struct tty_struct *tty; int result; int data_length; + unsigned long flags; dbg("%s - port %d", __func__, port->number); @@ -87,27 +100,31 @@ static void opticon_bulk_callback(struct * Data from the device comes with a 2 byte header: * * <0x00><0x00>data... - * This is real data to be sent to the tty layer + * This is real data to be sent to the tty layer * <0x00><0x01)level - * This is a RTS level change, the third byte is the RTS - * value (0 for low, 1 for high). + * This is a CTS level change, the third byte is the CTS + * value (0 for low, 1 for high). */ if ((data[0] == 0x00) && (data[1] == 0x00)) { /* real data, send it to the tty layer */ tty = tty_port_tty_get(&port->port); if (tty) { - tty_insert_flip_string(tty, data, - data_length); + tty_insert_flip_string(tty, data + 2, + data_length); tty_flip_buffer_push(tty); tty_kref_put(tty); } } else { if ((data[0] == 0x00) && (data[1] == 0x01)) { + spin_lock_irqsave(&priv->lock, flags); + /* CTS status infomation package */ if (data[2] == 0x00) - priv->rts = false; + priv->cts = false; else - priv->rts = true; + priv->cts = true; + spin_unlock_irqrestore(&priv->lock, flags); } else { + dev_dbg(&priv->udev->dev, "Unknown data packet received from the device:" " %2x %2x\n", @@ -121,25 +138,69 @@ static void opticon_bulk_callback(struct } exit: - spin_lock(&priv->lock); - /* Continue trying to always read if we should */ - if (!priv->throttled) { - usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, + spin_lock_irqsave(&priv->lock, flags); + if (priv->throttled) { + priv->actually_throttled = true; + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + spin_unlock_irqrestore(&priv->lock, flags); + + usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, usb_rcvbulkpipe(priv->udev, - priv->bulk_address), + priv->bulk_address), priv->bulk_in_buffer, priv->buffer_size, - opticon_bulk_callback, priv); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __func__, result); - } else - priv->actually_throttled = true; - spin_unlock(&priv->lock); + opticon_read_bulk_callback, priv); + + result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + +} + +static void opticon_write_control_callback(struct urb *urb) +{ + struct opticon_private *priv = urb->context; + int status = urb->status; + unsigned long flags; + + /* free up the transfer buffer, as usb_free_urb() does not do this */ + kfree(urb->transfer_buffer); + + if (status) + dbg("%s - nonzero write bulk status received: %d", + __func__, status); + + spin_lock_irqsave(&priv->lock, flags); + --priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + + usb_serial_port_softint(priv->port); } + +static int send_control_msg(struct usb_serial_port *port, u8 requesttype, + u8 val) +{ + struct usb_serial *serial = port->serial; + int retval; + u8 buffer[2]; + + buffer[0] = val; + /* Send the message to the vendor control endpoint + * of the connected device */ + retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + requesttype, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + 0, 0, buffer, 1, 0); + + return retval; +} + + static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) { struct opticon_private *priv = usb_get_serial_data(port->serial); @@ -152,19 +213,28 @@ static int opticon_open(struct tty_struc priv->throttled = false; priv->actually_throttled = false; priv->port = port; + priv->rts = false; spin_unlock_irqrestore(&priv->lock, flags); - /* Start reading from the device */ + /* Clear RTS line */ + send_control_msg(port, CONTROL_RTS, 0); + + /* Setup the read URB and start reading from the device */ usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, usb_rcvbulkpipe(priv->udev, priv->bulk_address), priv->bulk_in_buffer, priv->buffer_size, - opticon_bulk_callback, priv); + opticon_read_bulk_callback, priv); + + /* clear the halt status of the enpoint */ + usb_clear_halt(priv->udev, priv->bulk_read_urb->pipe); + result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL); if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __func__, result); + return result; } @@ -178,25 +248,6 @@ static void opticon_close(struct usb_ser usb_kill_urb(priv->bulk_read_urb); } -static void opticon_write_bulk_callback(struct urb *urb) -{ - struct opticon_private *priv = urb->context; - int status = urb->status; - unsigned long flags; - - /* free up the transfer buffer, as usb_free_urb() does not do this */ - kfree(urb->transfer_buffer); - - if (status) - dbg("%s - nonzero write bulk status received: %d", - __func__, status); - - spin_lock_irqsave(&priv->lock, flags); - --priv->outstanding_urbs; - spin_unlock_irqrestore(&priv->lock, flags); - - usb_serial_port_softint(priv->port); -} static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) @@ -207,6 +258,7 @@ static int opticon_write(struct tty_stru unsigned char *buffer; unsigned long flags; int status; + struct usb_ctrlrequest *ctrl_req = NULL; dbg("%s - port %d", __func__, port->number); @@ -223,6 +275,7 @@ static int opticon_write(struct tty_stru if (!buffer) { dev_err(&port->dev, "out of memory\n"); count = -ENOMEM; + goto error_no_buffer; } @@ -237,16 +290,26 @@ static int opticon_write(struct tty_stru usb_serial_debug_data(debug, &port->dev, __func__, count, buffer); - usb_fill_bulk_urb(urb, serial->dev, - usb_sndbulkpipe(serial->dev, - port->bulk_out_endpointAddress), - buffer, count, opticon_write_bulk_callback, priv); + /* Setup the vendor control command */ + ctrl_req = (void *)(priv->ctrl_write_buf); /* ctrl_buf is 8 bytes */ + ctrl_req->bRequestType = + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE; /* 0x41 */ + ctrl_req->bRequest = 0x01; + ctrl_req->wValue = 0; + ctrl_req->wIndex = 0; + ctrl_req->wLength = cpu_to_le16(count); + + usb_fill_control_urb(urb, serial->dev, usb_sndctrlpipe(serial->dev, 0), + (unsigned char *)ctrl_req, + buffer, count, + opticon_write_control_callback, + priv); /* send it down the pipe */ status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { dev_err(&port->dev, - "%s - usb_submit_urb(write bulk) failed with status = %d\n", + "%s - usb_submit_urb(write endpoint) failed status = %d\n", __func__, status); count = status; goto error; @@ -282,6 +345,7 @@ static int opticon_write_room(struct tty * layer that we have lots of free space, unless we don't. */ spin_lock_irqsave(&priv->lock, flags); + if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) { spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - write limit hit", __func__); @@ -338,16 +402,49 @@ static int opticon_tiocmget(struct tty_s int result = 0; dbg("%s - port %d", __func__, port->number); + if (!usb_get_intfdata(port->serial->interface)) + return -ENODEV; spin_lock_irqsave(&priv->lock, flags); if (priv->rts) - result = TIOCM_RTS; + result |= TIOCM_RTS; + if (priv->cts) + result |= TIOCM_CTS; spin_unlock_irqrestore(&priv->lock, flags); dbg("%s - %x", __func__, result); return result; } +static int opticon_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct usb_serial_port *port = tty->driver_data; + struct opticon_private *priv = usb_get_serial_data(port->serial); + unsigned long flags; + bool rts; + bool changed = false; + + if (!usb_get_intfdata(port->serial->interface)) + return -ENODEV; + /* We only support RTS so we only handle that */ + spin_lock_irqsave(&priv->lock, flags); + + rts = priv->rts; + if (set & TIOCM_RTS) + priv->rts = true; + if (clear & TIOCM_RTS) + priv->rts = false; + changed = rts ^ priv->rts; + spin_unlock_irqrestore(&priv->lock, flags); + + if (!changed) + return 0; + + /* Send the new RTS state to the connected device */ + return send_control_msg(port, CONTROL_RTS, !rts); +} + static int get_serial_info(struct opticon_private *priv, struct serial_struct __user *serial) { @@ -409,6 +506,7 @@ static int opticon_startup(struct usb_se priv->serial = serial; priv->port = serial->port[0]; priv->udev = serial->dev; + priv->outstanding_urbs = 0; /* Init the outstanding urbs */ /* find our bulk endpoint */ intf = serial->interface->altsetting; @@ -434,13 +532,6 @@ static int opticon_startup(struct usb_se priv->bulk_address = endpoint->bEndpointAddress; - /* set up our bulk urb */ - usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, - usb_rcvbulkpipe(priv->udev, - endpoint->bEndpointAddress), - priv->bulk_in_buffer, priv->buffer_size, - opticon_bulk_callback, priv); - bulk_in_found = true; break; } @@ -536,6 +627,7 @@ static struct usb_serial_driver opticon_ .unthrottle = opticon_unthrottle, .ioctl = opticon_ioctl, .tiocmget = opticon_tiocmget, + .tiocmset = opticon_tiocmset, }; static int __init opticon_init(void) @@ -559,6 +651,7 @@ static void __exit opticon_exit(void) module_init(opticon_init); module_exit(opticon_exit); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(debug, bool, S_IRUGO | S_IWUSR); -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html