|  | /* | 
|  | * Opticon USB barcode to serial driver | 
|  | * | 
|  | * Copyright (C) 2011 - 2012 Johan Hovold <jhovold@gmail.com> | 
|  | * Copyright (C) 2011 Martin Jansen <martin.jansen@opticon.com> | 
|  | * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de> | 
|  | * Copyright (C) 2008 - 2009 Novell Inc. | 
|  | * | 
|  | *	This program is free software; you can redistribute it and/or | 
|  | *	modify it under the terms of the GNU General Public License version | 
|  | *	2 as published by the Free Software Foundation. | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/tty.h> | 
|  | #include <linux/tty_driver.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/tty_flip.h> | 
|  | #include <linux/serial.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/usb.h> | 
|  | #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 const struct usb_device_id id_table[] = { | 
|  | { USB_DEVICE(0x065a, 0x0009) }, | 
|  | { }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(usb, id_table); | 
|  |  | 
|  | /* This structure holds all of the individual device information */ | 
|  | struct opticon_private { | 
|  | spinlock_t lock;	/* protects the following flags */ | 
|  | bool rts; | 
|  | bool cts; | 
|  | int outstanding_urbs; | 
|  | }; | 
|  |  | 
|  |  | 
|  | static void opticon_process_data_packet(struct usb_serial_port *port, | 
|  | const unsigned char *buf, size_t len) | 
|  | { | 
|  | tty_insert_flip_string(&port->port, buf, len); | 
|  | tty_flip_buffer_push(&port->port); | 
|  | } | 
|  |  | 
|  | static void opticon_process_status_packet(struct usb_serial_port *port, | 
|  | const unsigned char *buf, size_t len) | 
|  | { | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  | unsigned long flags; | 
|  |  | 
|  | spin_lock_irqsave(&priv->lock, flags); | 
|  | if (buf[0] == 0x00) | 
|  | priv->cts = false; | 
|  | else | 
|  | priv->cts = true; | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | } | 
|  |  | 
|  | static void opticon_process_read_urb(struct urb *urb) | 
|  | { | 
|  | struct usb_serial_port *port = urb->context; | 
|  | const unsigned char *hdr = urb->transfer_buffer; | 
|  | const unsigned char *data = hdr + 2; | 
|  | size_t data_len = urb->actual_length - 2; | 
|  |  | 
|  | if (urb->actual_length <= 2) { | 
|  | dev_dbg(&port->dev, "malformed packet received: %d bytes\n", | 
|  | urb->actual_length); | 
|  | return; | 
|  | } | 
|  | /* | 
|  | * Data from the device comes with a 2 byte header: | 
|  | * | 
|  | * <0x00><0x00>data... | 
|  | *      This is real data to be sent to the tty layer | 
|  | * <0x00><0x01>level | 
|  | *      This is a CTS level change, the third byte is the CTS | 
|  | *      value (0 for low, 1 for high). | 
|  | */ | 
|  | if ((hdr[0] == 0x00) && (hdr[1] == 0x00)) { | 
|  | opticon_process_data_packet(port, data, data_len); | 
|  | } else if ((hdr[0] == 0x00) && (hdr[1] == 0x01)) { | 
|  | opticon_process_status_packet(port, data, data_len); | 
|  | } else { | 
|  | dev_dbg(&port->dev, "unknown packet received: %02x %02x\n", | 
|  | hdr[0], hdr[1]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int send_control_msg(struct usb_serial_port *port, u8 requesttype, | 
|  | u8 val) | 
|  | { | 
|  | struct usb_serial *serial = port->serial; | 
|  | int retval; | 
|  | u8 *buffer; | 
|  |  | 
|  | buffer = kzalloc(1, GFP_KERNEL); | 
|  | if (!buffer) | 
|  | return -ENOMEM; | 
|  |  | 
|  | 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); | 
|  | kfree(buffer); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) | 
|  | { | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  | unsigned long flags; | 
|  | int res; | 
|  |  | 
|  | spin_lock_irqsave(&priv->lock, flags); | 
|  | priv->rts = false; | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  |  | 
|  | /* Clear RTS line */ | 
|  | send_control_msg(port, CONTROL_RTS, 0); | 
|  |  | 
|  | /* clear the halt status of the enpoint */ | 
|  | usb_clear_halt(port->serial->dev, port->read_urb->pipe); | 
|  |  | 
|  | res = usb_serial_generic_open(tty, port); | 
|  | if (!res) | 
|  | return res; | 
|  |  | 
|  | /* Request CTS line state, sometimes during opening the current | 
|  | * CTS state can be missed. */ | 
|  | send_control_msg(port, RESEND_CTS_STATE, 1); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static void opticon_write_control_callback(struct urb *urb) | 
|  | { | 
|  | struct usb_serial_port *port = urb->context; | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  | int status = urb->status; | 
|  | unsigned long flags; | 
|  |  | 
|  | /* free up the transfer buffer, as usb_free_urb() does not do this */ | 
|  | kfree(urb->transfer_buffer); | 
|  |  | 
|  | /* setup packet may be set if we're using it for writing */ | 
|  | kfree(urb->setup_packet); | 
|  |  | 
|  | if (status) | 
|  | dev_dbg(&port->dev, | 
|  | "%s - non-zero urb status received: %d\n", | 
|  | __func__, status); | 
|  |  | 
|  | spin_lock_irqsave(&priv->lock, flags); | 
|  | --priv->outstanding_urbs; | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  |  | 
|  | usb_serial_port_softint(port); | 
|  | } | 
|  |  | 
|  | static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, | 
|  | const unsigned char *buf, int count) | 
|  | { | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  | struct usb_serial *serial = port->serial; | 
|  | struct urb *urb; | 
|  | unsigned char *buffer; | 
|  | unsigned long flags; | 
|  | int status; | 
|  | struct usb_ctrlrequest *dr; | 
|  |  | 
|  | spin_lock_irqsave(&priv->lock, flags); | 
|  | if (priv->outstanding_urbs > URB_UPPER_LIMIT) { | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | dev_dbg(&port->dev, "%s - write limit hit\n", __func__); | 
|  | return 0; | 
|  | } | 
|  | priv->outstanding_urbs++; | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  |  | 
|  | buffer = kmalloc(count, GFP_ATOMIC); | 
|  | if (!buffer) { | 
|  | dev_err(&port->dev, "out of memory\n"); | 
|  | count = -ENOMEM; | 
|  |  | 
|  | goto error_no_buffer; | 
|  | } | 
|  |  | 
|  | urb = usb_alloc_urb(0, GFP_ATOMIC); | 
|  | if (!urb) { | 
|  | dev_err(&port->dev, "no more free urbs\n"); | 
|  | count = -ENOMEM; | 
|  | goto error_no_urb; | 
|  | } | 
|  |  | 
|  | memcpy(buffer, buf, count); | 
|  |  | 
|  | usb_serial_debug_data(&port->dev, __func__, count, buffer); | 
|  |  | 
|  | /* The conncected devices do not have a bulk write endpoint, | 
|  | * to transmit data to de barcode device the control endpoint is used */ | 
|  | dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); | 
|  | if (!dr) { | 
|  | dev_err(&port->dev, "out of memory\n"); | 
|  | count = -ENOMEM; | 
|  | goto error_no_dr; | 
|  | } | 
|  |  | 
|  | dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT; | 
|  | dr->bRequest = 0x01; | 
|  | dr->wValue = 0; | 
|  | dr->wIndex = 0; | 
|  | dr->wLength = cpu_to_le16(count); | 
|  |  | 
|  | usb_fill_control_urb(urb, serial->dev, | 
|  | usb_sndctrlpipe(serial->dev, 0), | 
|  | (unsigned char *)dr, buffer, count, | 
|  | opticon_write_control_callback, port); | 
|  |  | 
|  | /* send it down the pipe */ | 
|  | status = usb_submit_urb(urb, GFP_ATOMIC); | 
|  | if (status) { | 
|  | dev_err(&port->dev, | 
|  | "%s - usb_submit_urb(write endpoint) failed status = %d\n", | 
|  | __func__, status); | 
|  | count = status; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* we are done with this urb, so let the host driver | 
|  | * really free it when it is finished with it */ | 
|  | usb_free_urb(urb); | 
|  |  | 
|  | return count; | 
|  | error: | 
|  | kfree(dr); | 
|  | error_no_dr: | 
|  | usb_free_urb(urb); | 
|  | error_no_urb: | 
|  | kfree(buffer); | 
|  | error_no_buffer: | 
|  | spin_lock_irqsave(&priv->lock, flags); | 
|  | --priv->outstanding_urbs; | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  | return count; | 
|  | } | 
|  |  | 
|  | static int opticon_write_room(struct tty_struct *tty) | 
|  | { | 
|  | struct usb_serial_port *port = tty->driver_data; | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  | unsigned long flags; | 
|  |  | 
|  | /* | 
|  | * We really can take almost anything the user throws at us | 
|  | * but let's pick a nice big number to tell the 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); | 
|  | dev_dbg(&port->dev, "%s - write limit hit\n", __func__); | 
|  | return 0; | 
|  | } | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  |  | 
|  | return 2048; | 
|  | } | 
|  |  | 
|  | static int opticon_tiocmget(struct tty_struct *tty) | 
|  | { | 
|  | struct usb_serial_port *port = tty->driver_data; | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  | unsigned long flags; | 
|  | int result = 0; | 
|  |  | 
|  | spin_lock_irqsave(&priv->lock, flags); | 
|  | if (priv->rts) | 
|  | result |= TIOCM_RTS; | 
|  | if (priv->cts) | 
|  | result |= TIOCM_CTS; | 
|  | spin_unlock_irqrestore(&priv->lock, flags); | 
|  |  | 
|  | dev_dbg(&port->dev, "%s - %x\n", __func__, result); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static int opticon_tiocmset(struct tty_struct *tty, | 
|  | unsigned int set, unsigned int clear) | 
|  | { | 
|  | struct usb_serial_port *port = tty->driver_data; | 
|  | struct usb_serial *serial = port->serial; | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  | unsigned long flags; | 
|  | bool rts; | 
|  | bool changed = false; | 
|  | int ret; | 
|  |  | 
|  | /* 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 */ | 
|  | mutex_lock(&serial->disc_mutex); | 
|  | if (!serial->disconnected) | 
|  | ret = send_control_msg(port, CONTROL_RTS, !rts); | 
|  | else | 
|  | ret = -ENODEV; | 
|  | mutex_unlock(&serial->disc_mutex); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int get_serial_info(struct usb_serial_port *port, | 
|  | struct serial_struct __user *serial) | 
|  | { | 
|  | struct serial_struct tmp; | 
|  |  | 
|  | if (!serial) | 
|  | return -EFAULT; | 
|  |  | 
|  | memset(&tmp, 0x00, sizeof(tmp)); | 
|  |  | 
|  | /* fake emulate a 16550 uart to make userspace code happy */ | 
|  | tmp.type		= PORT_16550A; | 
|  | tmp.line		= port->serial->minor; | 
|  | tmp.port		= 0; | 
|  | tmp.irq			= 0; | 
|  | tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; | 
|  | tmp.xmit_fifo_size	= 1024; | 
|  | tmp.baud_base		= 9600; | 
|  | tmp.close_delay		= 5*HZ; | 
|  | tmp.closing_wait	= 30*HZ; | 
|  |  | 
|  | if (copy_to_user(serial, &tmp, sizeof(*serial))) | 
|  | return -EFAULT; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int opticon_ioctl(struct tty_struct *tty, | 
|  | unsigned int cmd, unsigned long arg) | 
|  | { | 
|  | struct usb_serial_port *port = tty->driver_data; | 
|  |  | 
|  | dev_dbg(&port->dev, "%s - port %d, cmd = 0x%x\n", __func__, port->number, cmd); | 
|  |  | 
|  | switch (cmd) { | 
|  | case TIOCGSERIAL: | 
|  | return get_serial_info(port, | 
|  | (struct serial_struct __user *)arg); | 
|  | } | 
|  |  | 
|  | return -ENOIOCTLCMD; | 
|  | } | 
|  |  | 
|  | static int opticon_startup(struct usb_serial *serial) | 
|  | { | 
|  | if (!serial->num_bulk_in) { | 
|  | dev_err(&serial->dev->dev, "no bulk in endpoint\n"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int opticon_port_probe(struct usb_serial_port *port) | 
|  | { | 
|  | struct opticon_private *priv; | 
|  |  | 
|  | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | 
|  | if (!priv) | 
|  | return -ENOMEM; | 
|  |  | 
|  | spin_lock_init(&priv->lock); | 
|  |  | 
|  | usb_set_serial_port_data(port, priv); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int opticon_port_remove(struct usb_serial_port *port) | 
|  | { | 
|  | struct opticon_private *priv = usb_get_serial_port_data(port); | 
|  |  | 
|  | kfree(priv); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct usb_serial_driver opticon_device = { | 
|  | .driver = { | 
|  | .owner =	THIS_MODULE, | 
|  | .name =		"opticon", | 
|  | }, | 
|  | .id_table =		id_table, | 
|  | .num_ports =		1, | 
|  | .bulk_in_size =		256, | 
|  | .attach =		opticon_startup, | 
|  | .port_probe =		opticon_port_probe, | 
|  | .port_remove =		opticon_port_remove, | 
|  | .open =			opticon_open, | 
|  | .write =		opticon_write, | 
|  | .write_room = 		opticon_write_room, | 
|  | .throttle =		usb_serial_generic_throttle, | 
|  | .unthrottle =		usb_serial_generic_unthrottle, | 
|  | .ioctl =		opticon_ioctl, | 
|  | .tiocmget =		opticon_tiocmget, | 
|  | .tiocmset =		opticon_tiocmset, | 
|  | .process_read_urb =	opticon_process_read_urb, | 
|  | }; | 
|  |  | 
|  | static struct usb_serial_driver * const serial_drivers[] = { | 
|  | &opticon_device, NULL | 
|  | }; | 
|  |  | 
|  | module_usb_serial_driver(serial_drivers, id_table); | 
|  |  | 
|  | MODULE_DESCRIPTION(DRIVER_DESC); | 
|  | MODULE_LICENSE("GPL"); |