18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Symbol USB barcode to serial driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Johan Hovold <jhovold@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2009 Greg Kroah-Hartman <gregkh@suse.de> 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Novell Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/tty.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 148c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/usb.h> 178c2ecf20Sopenharmony_ci#include <linux/usb/serial.h> 188c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = { 218c2ecf20Sopenharmony_ci { USB_DEVICE(0x05e0, 0x0600) }, 228c2ecf20Sopenharmony_ci { }, 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct symbol_private { 278c2ecf20Sopenharmony_ci spinlock_t lock; /* protects the following flags */ 288c2ecf20Sopenharmony_ci bool throttled; 298c2ecf20Sopenharmony_ci bool actually_throttled; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void symbol_int_callback(struct urb *urb) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct usb_serial_port *port = urb->context; 358c2ecf20Sopenharmony_ci struct symbol_private *priv = usb_get_serial_port_data(port); 368c2ecf20Sopenharmony_ci unsigned char *data = urb->transfer_buffer; 378c2ecf20Sopenharmony_ci int status = urb->status; 388c2ecf20Sopenharmony_ci unsigned long flags; 398c2ecf20Sopenharmony_ci int result; 408c2ecf20Sopenharmony_ci int data_length; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci switch (status) { 438c2ecf20Sopenharmony_ci case 0: 448c2ecf20Sopenharmony_ci /* success */ 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci case -ECONNRESET: 478c2ecf20Sopenharmony_ci case -ENOENT: 488c2ecf20Sopenharmony_ci case -ESHUTDOWN: 498c2ecf20Sopenharmony_ci /* this urb is terminated, clean up */ 508c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n", 518c2ecf20Sopenharmony_ci __func__, status); 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci default: 548c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n", 558c2ecf20Sopenharmony_ci __func__, status); 568c2ecf20Sopenharmony_ci goto exit; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* 628c2ecf20Sopenharmony_ci * Data from the device comes with a 1 byte header: 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * <size of data> <data>... 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci if (urb->actual_length > 1) { 678c2ecf20Sopenharmony_ci data_length = data[0]; 688c2ecf20Sopenharmony_ci if (data_length > (urb->actual_length - 1)) 698c2ecf20Sopenharmony_ci data_length = urb->actual_length - 1; 708c2ecf20Sopenharmony_ci tty_insert_flip_string(&port->port, &data[1], data_length); 718c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->port); 728c2ecf20Sopenharmony_ci } else { 738c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s - short packet\n", __func__); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciexit: 778c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Continue trying to always read if we should */ 808c2ecf20Sopenharmony_ci if (!priv->throttled) { 818c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); 828c2ecf20Sopenharmony_ci if (result) 838c2ecf20Sopenharmony_ci dev_err(&port->dev, 848c2ecf20Sopenharmony_ci "%s - failed resubmitting read urb, error %d\n", 858c2ecf20Sopenharmony_ci __func__, result); 868c2ecf20Sopenharmony_ci } else 878c2ecf20Sopenharmony_ci priv->actually_throttled = true; 888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int symbol_open(struct tty_struct *tty, struct usb_serial_port *port) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct symbol_private *priv = usb_get_serial_port_data(port); 948c2ecf20Sopenharmony_ci unsigned long flags; 958c2ecf20Sopenharmony_ci int result = 0; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 988c2ecf20Sopenharmony_ci priv->throttled = false; 998c2ecf20Sopenharmony_ci priv->actually_throttled = false; 1008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Start reading from the device */ 1038c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 1048c2ecf20Sopenharmony_ci if (result) 1058c2ecf20Sopenharmony_ci dev_err(&port->dev, 1068c2ecf20Sopenharmony_ci "%s - failed resubmitting read urb, error %d\n", 1078c2ecf20Sopenharmony_ci __func__, result); 1088c2ecf20Sopenharmony_ci return result; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void symbol_close(struct usb_serial_port *port) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci usb_kill_urb(port->interrupt_in_urb); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void symbol_throttle(struct tty_struct *tty) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 1198c2ecf20Sopenharmony_ci struct symbol_private *priv = usb_get_serial_port_data(port); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci spin_lock_irq(&priv->lock); 1228c2ecf20Sopenharmony_ci priv->throttled = true; 1238c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->lock); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void symbol_unthrottle(struct tty_struct *tty) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 1298c2ecf20Sopenharmony_ci struct symbol_private *priv = usb_get_serial_port_data(port); 1308c2ecf20Sopenharmony_ci int result; 1318c2ecf20Sopenharmony_ci bool was_throttled; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci spin_lock_irq(&priv->lock); 1348c2ecf20Sopenharmony_ci priv->throttled = false; 1358c2ecf20Sopenharmony_ci was_throttled = priv->actually_throttled; 1368c2ecf20Sopenharmony_ci priv->actually_throttled = false; 1378c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->lock); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (was_throttled) { 1408c2ecf20Sopenharmony_ci result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 1418c2ecf20Sopenharmony_ci if (result) 1428c2ecf20Sopenharmony_ci dev_err(&port->dev, 1438c2ecf20Sopenharmony_ci "%s - failed submitting read urb, error %d\n", 1448c2ecf20Sopenharmony_ci __func__, result); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int symbol_port_probe(struct usb_serial_port *port) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct symbol_private *priv; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 1538c2ecf20Sopenharmony_ci if (!priv) 1548c2ecf20Sopenharmony_ci return -ENOMEM; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci usb_set_serial_port_data(port, priv); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int symbol_port_remove(struct usb_serial_port *port) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct symbol_private *priv = usb_get_serial_port_data(port); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci kfree(priv); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic struct usb_serial_driver symbol_device = { 1738c2ecf20Sopenharmony_ci .driver = { 1748c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1758c2ecf20Sopenharmony_ci .name = "symbol", 1768c2ecf20Sopenharmony_ci }, 1778c2ecf20Sopenharmony_ci .id_table = id_table, 1788c2ecf20Sopenharmony_ci .num_ports = 1, 1798c2ecf20Sopenharmony_ci .num_interrupt_in = 1, 1808c2ecf20Sopenharmony_ci .port_probe = symbol_port_probe, 1818c2ecf20Sopenharmony_ci .port_remove = symbol_port_remove, 1828c2ecf20Sopenharmony_ci .open = symbol_open, 1838c2ecf20Sopenharmony_ci .close = symbol_close, 1848c2ecf20Sopenharmony_ci .throttle = symbol_throttle, 1858c2ecf20Sopenharmony_ci .unthrottle = symbol_unthrottle, 1868c2ecf20Sopenharmony_ci .read_int_callback = symbol_int_callback, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = { 1908c2ecf20Sopenharmony_ci &symbol_device, NULL 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 196