15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
268b44eaeSGreg Kroah-Hartman /*
368b44eaeSGreg Kroah-Hartman * Symbol USB barcode to serial driver
468b44eaeSGreg Kroah-Hartman *
5a85796eeSJohan Hovold * Copyright (C) 2013 Johan Hovold <jhovold@gmail.com>
668b44eaeSGreg Kroah-Hartman * Copyright (C) 2009 Greg Kroah-Hartman <gregkh@suse.de>
768b44eaeSGreg Kroah-Hartman * Copyright (C) 2009 Novell Inc.
868b44eaeSGreg Kroah-Hartman */
968b44eaeSGreg Kroah-Hartman
1068b44eaeSGreg Kroah-Hartman #include <linux/kernel.h>
1168b44eaeSGreg Kroah-Hartman #include <linux/tty.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
1368b44eaeSGreg Kroah-Hartman #include <linux/tty_driver.h>
1468b44eaeSGreg Kroah-Hartman #include <linux/tty_flip.h>
1568b44eaeSGreg Kroah-Hartman #include <linux/module.h>
1668b44eaeSGreg Kroah-Hartman #include <linux/usb.h>
1768b44eaeSGreg Kroah-Hartman #include <linux/usb/serial.h>
1868b44eaeSGreg Kroah-Hartman #include <linux/uaccess.h>
1968b44eaeSGreg Kroah-Hartman
207d40d7e8SNémeth Márton static const struct usb_device_id id_table[] = {
2168b44eaeSGreg Kroah-Hartman { USB_DEVICE(0x05e0, 0x0600) },
2268b44eaeSGreg Kroah-Hartman { },
2368b44eaeSGreg Kroah-Hartman };
2468b44eaeSGreg Kroah-Hartman MODULE_DEVICE_TABLE(usb, id_table);
2568b44eaeSGreg Kroah-Hartman
2668b44eaeSGreg Kroah-Hartman struct symbol_private {
2768b44eaeSGreg Kroah-Hartman spinlock_t lock; /* protects the following flags */
2868b44eaeSGreg Kroah-Hartman bool throttled;
2968b44eaeSGreg Kroah-Hartman bool actually_throttled;
3068b44eaeSGreg Kroah-Hartman };
3168b44eaeSGreg Kroah-Hartman
symbol_int_callback(struct urb * urb)3268b44eaeSGreg Kroah-Hartman static void symbol_int_callback(struct urb *urb)
3368b44eaeSGreg Kroah-Hartman {
34cced926fSJohan Hovold struct usb_serial_port *port = urb->context;
35a85796eeSJohan Hovold struct symbol_private *priv = usb_get_serial_port_data(port);
3668b44eaeSGreg Kroah-Hartman unsigned char *data = urb->transfer_buffer;
3768b44eaeSGreg Kroah-Hartman int status = urb->status;
385e02bfcfSJohn Ogness unsigned long flags;
3968b44eaeSGreg Kroah-Hartman int result;
4068b44eaeSGreg Kroah-Hartman int data_length;
4168b44eaeSGreg Kroah-Hartman
4268b44eaeSGreg Kroah-Hartman switch (status) {
4368b44eaeSGreg Kroah-Hartman case 0:
4468b44eaeSGreg Kroah-Hartman /* success */
4568b44eaeSGreg Kroah-Hartman break;
4668b44eaeSGreg Kroah-Hartman case -ECONNRESET:
4768b44eaeSGreg Kroah-Hartman case -ENOENT:
4868b44eaeSGreg Kroah-Hartman case -ESHUTDOWN:
4968b44eaeSGreg Kroah-Hartman /* this urb is terminated, clean up */
50e4083ea5SGreg Kroah-Hartman dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
5168b44eaeSGreg Kroah-Hartman __func__, status);
5268b44eaeSGreg Kroah-Hartman return;
5368b44eaeSGreg Kroah-Hartman default:
54e4083ea5SGreg Kroah-Hartman dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
5568b44eaeSGreg Kroah-Hartman __func__, status);
5668b44eaeSGreg Kroah-Hartman goto exit;
5768b44eaeSGreg Kroah-Hartman }
5868b44eaeSGreg Kroah-Hartman
5959d33f2fSGreg Kroah-Hartman usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
6068b44eaeSGreg Kroah-Hartman
6168b44eaeSGreg Kroah-Hartman /*
6268b44eaeSGreg Kroah-Hartman * Data from the device comes with a 1 byte header:
6368b44eaeSGreg Kroah-Hartman *
648ae25a35SPhilipp Hachtmann * <size of data> <data>...
6568b44eaeSGreg Kroah-Hartman */
668ae25a35SPhilipp Hachtmann if (urb->actual_length > 1) {
678ae25a35SPhilipp Hachtmann data_length = data[0];
688ae25a35SPhilipp Hachtmann if (data_length > (urb->actual_length - 1))
698ae25a35SPhilipp Hachtmann data_length = urb->actual_length - 1;
702e124b4aSJiri Slaby tty_insert_flip_string(&port->port, &data[1], data_length);
712e124b4aSJiri Slaby tty_flip_buffer_push(&port->port);
7268b44eaeSGreg Kroah-Hartman } else {
73d9a38a87SJohan Hovold dev_dbg(&port->dev, "%s - short packet\n", __func__);
7468b44eaeSGreg Kroah-Hartman }
7568b44eaeSGreg Kroah-Hartman
7668b44eaeSGreg Kroah-Hartman exit:
775e02bfcfSJohn Ogness spin_lock_irqsave(&priv->lock, flags);
7868b44eaeSGreg Kroah-Hartman
7968b44eaeSGreg Kroah-Hartman /* Continue trying to always read if we should */
8068b44eaeSGreg Kroah-Hartman if (!priv->throttled) {
81cced926fSJohan Hovold result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
8268b44eaeSGreg Kroah-Hartman if (result)
8368b44eaeSGreg Kroah-Hartman dev_err(&port->dev,
8468b44eaeSGreg Kroah-Hartman "%s - failed resubmitting read urb, error %d\n",
8568b44eaeSGreg Kroah-Hartman __func__, result);
8668b44eaeSGreg Kroah-Hartman } else
8768b44eaeSGreg Kroah-Hartman priv->actually_throttled = true;
885e02bfcfSJohn Ogness spin_unlock_irqrestore(&priv->lock, flags);
8968b44eaeSGreg Kroah-Hartman }
9068b44eaeSGreg Kroah-Hartman
symbol_open(struct tty_struct * tty,struct usb_serial_port * port)91a509a7e4SAlan Cox static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port)
9268b44eaeSGreg Kroah-Hartman {
93951d3793SPhilipp Hachtmann struct symbol_private *priv = usb_get_serial_port_data(port);
9468b44eaeSGreg Kroah-Hartman unsigned long flags;
9568b44eaeSGreg Kroah-Hartman int result = 0;
9668b44eaeSGreg Kroah-Hartman
9768b44eaeSGreg Kroah-Hartman spin_lock_irqsave(&priv->lock, flags);
9868b44eaeSGreg Kroah-Hartman priv->throttled = false;
9968b44eaeSGreg Kroah-Hartman priv->actually_throttled = false;
10068b44eaeSGreg Kroah-Hartman spin_unlock_irqrestore(&priv->lock, flags);
10168b44eaeSGreg Kroah-Hartman
10268b44eaeSGreg Kroah-Hartman /* Start reading from the device */
103cced926fSJohan Hovold result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
10468b44eaeSGreg Kroah-Hartman if (result)
10568b44eaeSGreg Kroah-Hartman dev_err(&port->dev,
10668b44eaeSGreg Kroah-Hartman "%s - failed resubmitting read urb, error %d\n",
10768b44eaeSGreg Kroah-Hartman __func__, result);
10868b44eaeSGreg Kroah-Hartman return result;
10968b44eaeSGreg Kroah-Hartman }
11068b44eaeSGreg Kroah-Hartman
symbol_close(struct usb_serial_port * port)111335f8514SAlan Cox static void symbol_close(struct usb_serial_port *port)
11268b44eaeSGreg Kroah-Hartman {
113cced926fSJohan Hovold usb_kill_urb(port->interrupt_in_urb);
11468b44eaeSGreg Kroah-Hartman }
11568b44eaeSGreg Kroah-Hartman
symbol_throttle(struct tty_struct * tty)11668b44eaeSGreg Kroah-Hartman static void symbol_throttle(struct tty_struct *tty)
11768b44eaeSGreg Kroah-Hartman {
11868b44eaeSGreg Kroah-Hartman struct usb_serial_port *port = tty->driver_data;
119951d3793SPhilipp Hachtmann struct symbol_private *priv = usb_get_serial_port_data(port);
12068b44eaeSGreg Kroah-Hartman
12163832515SOliver Neukum spin_lock_irq(&priv->lock);
12268b44eaeSGreg Kroah-Hartman priv->throttled = true;
12363832515SOliver Neukum spin_unlock_irq(&priv->lock);
12468b44eaeSGreg Kroah-Hartman }
12568b44eaeSGreg Kroah-Hartman
symbol_unthrottle(struct tty_struct * tty)12668b44eaeSGreg Kroah-Hartman static void symbol_unthrottle(struct tty_struct *tty)
12768b44eaeSGreg Kroah-Hartman {
12868b44eaeSGreg Kroah-Hartman struct usb_serial_port *port = tty->driver_data;
129951d3793SPhilipp Hachtmann struct symbol_private *priv = usb_get_serial_port_data(port);
13068b44eaeSGreg Kroah-Hartman int result;
131b2a5cf1bSOliver Neukum bool was_throttled;
13268b44eaeSGreg Kroah-Hartman
13363832515SOliver Neukum spin_lock_irq(&priv->lock);
13468b44eaeSGreg Kroah-Hartman priv->throttled = false;
135b2a5cf1bSOliver Neukum was_throttled = priv->actually_throttled;
13668b44eaeSGreg Kroah-Hartman priv->actually_throttled = false;
13763832515SOliver Neukum spin_unlock_irq(&priv->lock);
13868b44eaeSGreg Kroah-Hartman
139b2a5cf1bSOliver Neukum if (was_throttled) {
140cced926fSJohan Hovold result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
14168b44eaeSGreg Kroah-Hartman if (result)
14268b44eaeSGreg Kroah-Hartman dev_err(&port->dev,
14368b44eaeSGreg Kroah-Hartman "%s - failed submitting read urb, error %d\n",
14468b44eaeSGreg Kroah-Hartman __func__, result);
14568b44eaeSGreg Kroah-Hartman }
146b2a5cf1bSOliver Neukum }
14768b44eaeSGreg Kroah-Hartman
symbol_port_probe(struct usb_serial_port * port)148a85796eeSJohan Hovold static int symbol_port_probe(struct usb_serial_port *port)
149f9c99bb8SAlan Stern {
150a85796eeSJohan Hovold struct symbol_private *priv;
151a85796eeSJohan Hovold
152a85796eeSJohan Hovold priv = kzalloc(sizeof(*priv), GFP_KERNEL);
153a85796eeSJohan Hovold if (!priv)
154a85796eeSJohan Hovold return -ENOMEM;
155a85796eeSJohan Hovold
156a85796eeSJohan Hovold spin_lock_init(&priv->lock);
157a85796eeSJohan Hovold
158a85796eeSJohan Hovold usb_set_serial_port_data(port, priv);
159a85796eeSJohan Hovold
160a85796eeSJohan Hovold return 0;
161a85796eeSJohan Hovold }
162a85796eeSJohan Hovold
symbol_port_remove(struct usb_serial_port * port)163*c5d1448fSUwe Kleine-König static void symbol_port_remove(struct usb_serial_port *port)
164a85796eeSJohan Hovold {
165a85796eeSJohan Hovold struct symbol_private *priv = usb_get_serial_port_data(port);
166f9c99bb8SAlan Stern
16768b44eaeSGreg Kroah-Hartman kfree(priv);
16868b44eaeSGreg Kroah-Hartman }
16968b44eaeSGreg Kroah-Hartman
17068b44eaeSGreg Kroah-Hartman static struct usb_serial_driver symbol_device = {
17168b44eaeSGreg Kroah-Hartman .driver = {
17268b44eaeSGreg Kroah-Hartman .owner = THIS_MODULE,
17368b44eaeSGreg Kroah-Hartman .name = "symbol",
17468b44eaeSGreg Kroah-Hartman },
17568b44eaeSGreg Kroah-Hartman .id_table = id_table,
17668b44eaeSGreg Kroah-Hartman .num_ports = 1,
177e2cd017fSJohan Hovold .num_interrupt_in = 1,
178a85796eeSJohan Hovold .port_probe = symbol_port_probe,
179a85796eeSJohan Hovold .port_remove = symbol_port_remove,
18068b44eaeSGreg Kroah-Hartman .open = symbol_open,
18168b44eaeSGreg Kroah-Hartman .close = symbol_close,
18268b44eaeSGreg Kroah-Hartman .throttle = symbol_throttle,
18368b44eaeSGreg Kroah-Hartman .unthrottle = symbol_unthrottle,
184cced926fSJohan Hovold .read_int_callback = symbol_int_callback,
18568b44eaeSGreg Kroah-Hartman };
18668b44eaeSGreg Kroah-Hartman
187d860322fSAlan Stern static struct usb_serial_driver * const serial_drivers[] = {
188d860322fSAlan Stern &symbol_device, NULL
189d860322fSAlan Stern };
190d860322fSAlan Stern
19168e24113SGreg Kroah-Hartman module_usb_serial_driver(serial_drivers, id_table);
19268b44eaeSGreg Kroah-Hartman
193627cfa89SJohan Hovold MODULE_LICENSE("GPL v2");
194