143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin/* 2d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman Some of this code is credited to Linux USB open source files that are 3d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman distributed with Linux. 443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin Copyright: 2007 Metrologic Instruments. All rights reserved. 643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin Copyright: 2011 Azimut Ltd. <http://azimutrzn.ru/> 743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin*/ 843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/kernel.h> 1043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/init.h> 1143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/tty.h> 1243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/module.h> 1343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/usb.h> 1443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/errno.h> 1543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/slab.h> 1643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/tty_driver.h> 1743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/tty_flip.h> 1843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/moduleparam.h> 1943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/spinlock.h> 2043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/errno.h> 21d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman#include <linux/uaccess.h> 2243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#include <linux/usb/serial.h> 2343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin/* Version Information */ 2543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#define DRIVER_VERSION "v1.2.0.0" 2643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin#define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver" 2743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 28159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman/* Product information. */ 29159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman#define FOCUS_VENDOR_ID 0x0C2E 303a450850e2bb0f92cacb12da90fe98eccd105468Aleksey Babahin#define FOCUS_PRODUCT_ID_BI 0x0720 313a450850e2bb0f92cacb12da90fe98eccd105468Aleksey Babahin#define FOCUS_PRODUCT_ID_UNI 0x0700 32159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman 33159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman#define METROUSB_SET_REQUEST_TYPE 0x40 34159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman#define METROUSB_SET_MODEM_CTRL_REQUEST 10 35159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman#define METROUSB_SET_BREAK_REQUEST 0x40 36159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman#define METROUSB_MCR_NONE 0x08 /* Deactivate DTR and RTS. */ 37159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman#define METROUSB_MCR_RTS 0x0a /* Activate RTS. */ 38159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman#define METROUSB_MCR_DTR 0x09 /* Activate DTR. */ 39d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman#define WDR_TIMEOUT 5000 /* default urb timeout. */ 40159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman 41159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman/* Private data structure. */ 42159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartmanstruct metrousb_private { 43159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman spinlock_t lock; 44159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman int throttled; 45159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman unsigned long control_state; 46159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman}; 47159d4d8d5e9416dba78b84d4be10d7b1172728eeGreg Kroah-Hartman 4843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin/* Device table list. */ 49d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartmanstatic struct usb_device_id id_table[] = { 503a450850e2bb0f92cacb12da90fe98eccd105468Aleksey Babahin { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) }, 5143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) }, 5243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin { }, /* Terminating entry. */ 5343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin}; 5443d186fe992da93bb1dd34a7dd4534719624431cAleksey BabahinMODULE_DEVICE_TABLE(usb, id_table); 5543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 5643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin/* Input parameter constants. */ 57fdac0f647a2cf12e7152dc1d94dd08a1af4a2a82Greg Kroah-Hartmanstatic bool debug; 5843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 599fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartmanstatic void metrousb_read_int_callback(struct urb *urb) 6043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 618111e4ecf9373f6d76504416b0e76b18372f3598Greg Kroah-Hartman struct usb_serial_port *port = urb->context; 629fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman struct metrousb_private *metro_priv = usb_get_serial_port_data(port); 639fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman struct tty_struct *tty; 649fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman unsigned char *data = urb->transfer_buffer; 659fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman int throttled = 0; 669fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman int result = 0; 679fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman unsigned long flags = 0; 689fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 695db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, "%s\n", __func__); 7043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 719fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman switch (urb->status) { 729fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman case 0: 739fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Success status, read from the port. */ 749fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman break; 759fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman case -ECONNRESET: 769fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman case -ENOENT: 779fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman case -ESHUTDOWN: 789fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* urb has been terminated. */ 795db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, 805db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman "%s - urb shutting down, error code=%d\n", 815db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, result); 829fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman return; 839fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman default: 845db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, 855db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman "%s - non-zero urb received, error code=%d\n", 865db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, result); 879fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman goto exit; 889fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman } 899fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 909fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 919fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Set the data read from the usb port into the serial port buffer. */ 929fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman tty = tty_port_tty_get(&port->port); 939fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman if (!tty) { 945db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, "%s - bad tty pointer - exiting\n", 955db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__); 969fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman return; 979fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman } 989fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 999fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman if (tty && urb->actual_length) { 1009fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Loop through the data copying each byte to the tty layer. */ 1019fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman tty_insert_flip_string(tty, data, urb->actual_length); 1029fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 1039fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Force the data to the tty layer. */ 1049fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman tty_flip_buffer_push(tty); 1059fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman } 1069fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman tty_kref_put(tty); 1079fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 1089fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Set any port variables. */ 1099fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman spin_lock_irqsave(&metro_priv->lock, flags); 1109fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman throttled = metro_priv->throttled; 1119fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman spin_unlock_irqrestore(&metro_priv->lock, flags); 1129fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 1139fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Continue trying to read if set. */ 1149fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman if (!throttled) { 1159fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, 1169fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress), 1179fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman port->interrupt_in_urb->transfer_buffer, 1189fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman port->interrupt_in_urb->transfer_buffer_length, 1199fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman metrousb_read_int_callback, port, 1); 1209fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 1219fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); 1229fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 1235db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman if (result) 1245db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, 1255db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman "%s - failed submitting interrupt in urb, error code=%d\n", 1265db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, result); 12743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin } 1289fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman return; 1299fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 1309fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartmanexit: 1319fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Try to resubmit the urb. */ 1329fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman result = usb_submit_urb(urb, GFP_ATOMIC); 1335db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman if (result) 1345db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, 1355db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman "%s - failed submitting interrupt in urb, error code=%d\n", 1365db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, result); 13743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 13843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 1399fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartmanstatic void metrousb_cleanup(struct usb_serial_port *port) 14043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 1415db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, "%s\n", __func__); 1429fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 1439fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman if (port->serial->dev) { 1449fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman /* Shutdown any interrupt in urbs. */ 1459fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman if (port->interrupt_in_urb) { 1469fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman usb_unlink_urb(port->interrupt_in_urb); 1479fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman usb_kill_urb(port->interrupt_in_urb); 1489fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman } 1499fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman } 15043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 15143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 152d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartmanstatic int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) 15343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 15443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct usb_serial *serial = port->serial; 15543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct metrousb_private *metro_priv = usb_get_serial_port_data(port); 15643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned long flags = 0; 15743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin int result = 0; 15843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 1595db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, "%s\n", __func__); 16043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 16143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Make sure the urb is initialized. */ 16243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (!port->interrupt_in_urb) { 1635db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, "%s - interrupt urb not initialized\n", 1645db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__); 16543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin return -ENODEV; 16643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin } 16743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 16843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Set the private data information for the port. */ 16943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_lock_irqsave(&metro_priv->lock, flags); 17043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin metro_priv->control_state = 0; 17143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin metro_priv->throttled = 0; 17243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_unlock_irqrestore(&metro_priv->lock, flags); 17343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 17443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Clear the urb pipe. */ 17543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin usb_clear_halt(serial->dev, port->interrupt_in_urb->pipe); 17643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 17743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Start reading from the device */ 178d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman usb_fill_int_urb(port->interrupt_in_urb, serial->dev, 179d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress), 18043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin port->interrupt_in_urb->transfer_buffer, 18143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin port->interrupt_in_urb->transfer_buffer_length, 18243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin metrousb_read_int_callback, port, 1); 18343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 18443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 18543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (result) { 1865db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, 1875db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman "%s - failed submitting interrupt in urb, error code=%d\n", 1885db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, result); 18943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin goto exit; 19043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin } 19143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 1925db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&port->dev, "%s - port open\n", __func__); 19343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahinexit: 19443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin return result; 19543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 19643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 19743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahinstatic int metrousb_set_modem_ctrl(struct usb_serial *serial, unsigned int control_state) 19843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 19943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin int retval = 0; 20043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned char mcr = METROUSB_MCR_NONE; 20143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2025db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&serial->dev->dev, "%s - control state = %d\n", 2035db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, control_state); 20443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 20543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Set the modem control value. */ 20643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (control_state & TIOCM_DTR) 20743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin mcr |= METROUSB_MCR_DTR; 20843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (control_state & TIOCM_RTS) 20943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin mcr |= METROUSB_MCR_RTS; 21043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 21143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Send the command to the usb port. */ 21243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 21343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin METROUSB_SET_REQUEST_TYPE, METROUSB_SET_MODEM_CTRL_REQUEST, 21443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin control_state, 0, NULL, 0, WDR_TIMEOUT); 21543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (retval < 0) 2165db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&serial->dev->dev, 2175db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman "%s - set modem ctrl=0x%x failed, error code=%d\n", 2185db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, mcr, retval); 21943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 22043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin return retval; 22143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 22243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 223d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartmanstatic void metrousb_shutdown(struct usb_serial *serial) 22443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 22543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin int i = 0; 22643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2275db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&serial->dev->dev, "%s\n", __func__); 22843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 22943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Stop reading and writing on all ports. */ 230d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman for (i = 0; i < serial->num_ports; ++i) { 23143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Close any open urbs. */ 23243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin metrousb_cleanup(serial->port[i]); 23343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 23443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Free memory. */ 23543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin kfree(usb_get_serial_port_data(serial->port[i])); 23643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin usb_set_serial_port_data(serial->port[i], NULL); 23743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2385db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&serial->dev->dev, "%s - freed port number=%d\n", 2395db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, serial->port[i]->number); 24043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin } 24143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 24243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 24343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahinstatic int metrousb_startup(struct usb_serial *serial) 24443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 24543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct metrousb_private *metro_priv; 24643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct usb_serial_port *port; 24743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin int i = 0; 24843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2495db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&serial->dev->dev, "%s\n", __func__); 25043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 25143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Loop through the serial ports setting up the private structures. 25243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin * Currently we only use one port. */ 25343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin for (i = 0; i < serial->num_ports; ++i) { 25443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin port = serial->port[i]; 25543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 25643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Declare memory. */ 2578111e4ecf9373f6d76504416b0e76b18372f3598Greg Kroah-Hartman metro_priv = kzalloc(sizeof(struct metrousb_private), GFP_KERNEL); 25843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (!metro_priv) 25943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin return -ENOMEM; 26043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 26143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Initialize memory. */ 26243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_lock_init(&metro_priv->lock); 26343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin usb_set_serial_port_data(port, metro_priv); 26443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2655db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(&serial->dev->dev, "%s - port number=%d\n ", 2665db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman __func__, port->number); 26743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin } 26843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 26943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin return 0; 27043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 27143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 272d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartmanstatic void metrousb_throttle(struct tty_struct *tty) 27343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 27443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct usb_serial_port *port = tty->driver_data; 27543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct metrousb_private *metro_priv = usb_get_serial_port_data(port); 27643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned long flags = 0; 27743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2785db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(tty->dev, "%s\n", __func__); 27943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 28043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Set the private information for the port to stop reading data. */ 28143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_lock_irqsave(&metro_priv->lock, flags); 28243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin metro_priv->throttled = 1; 28343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_unlock_irqrestore(&metro_priv->lock, flags); 28443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 28543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 286d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartmanstatic int metrousb_tiocmget(struct tty_struct *tty) 28743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 28843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned long control_state = 0; 28943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct usb_serial_port *port = tty->driver_data; 29043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct metrousb_private *metro_priv = usb_get_serial_port_data(port); 29143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned long flags = 0; 29243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 2935db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(tty->dev, "%s\n", __func__); 29443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 29543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_lock_irqsave(&metro_priv->lock, flags); 29643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin control_state = metro_priv->control_state; 29743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_unlock_irqrestore(&metro_priv->lock, flags); 29843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 29943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin return control_state; 30043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 30143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 302d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartmanstatic int metrousb_tiocmset(struct tty_struct *tty, 303d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman unsigned int set, unsigned int clear) 30443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 30543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct usb_serial_port *port = tty->driver_data; 30643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct usb_serial *serial = port->serial; 30743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct metrousb_private *metro_priv = usb_get_serial_port_data(port); 30843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned long flags = 0; 30943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned long control_state = 0; 31043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 3115db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(tty->dev, "%s - set=%d, clear=%d\n", __func__, set, clear); 31243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 31343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_lock_irqsave(&metro_priv->lock, flags); 31443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin control_state = metro_priv->control_state; 31543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 316d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartman /* Set the RTS and DTR values. */ 31743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (set & TIOCM_RTS) 31843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin control_state |= TIOCM_RTS; 31943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (set & TIOCM_DTR) 32043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin control_state |= TIOCM_DTR; 32143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (clear & TIOCM_RTS) 32243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin control_state &= ~TIOCM_RTS; 32343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin if (clear & TIOCM_DTR) 32443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin control_state &= ~TIOCM_DTR; 32543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 32643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin metro_priv->control_state = control_state; 32743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_unlock_irqrestore(&metro_priv->lock, flags); 32843d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin return metrousb_set_modem_ctrl(serial, control_state); 32943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 33043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 331d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-Hartmanstatic void metrousb_unthrottle(struct tty_struct *tty) 33243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin{ 33343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct usb_serial_port *port = tty->driver_data; 33443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin struct metrousb_private *metro_priv = usb_get_serial_port_data(port); 33543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin unsigned long flags = 0; 33643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin int result = 0; 33743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 3385db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(tty->dev, "%s\n", __func__); 33943d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 34043d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Set the private information for the port to resume reading data. */ 34143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_lock_irqsave(&metro_priv->lock, flags); 34243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin metro_priv->throttled = 0; 34343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin spin_unlock_irqrestore(&metro_priv->lock, flags); 34443d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 34543d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin /* Submit the urb to read from the port. */ 34643d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin port->interrupt_in_urb->dev = port->serial->dev; 34743d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); 3485db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman if (result) 3495db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman dev_dbg(tty->dev, 3505db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman "failed submitting interrupt in urb error code=%d\n", 3515db51b50c10f3bf56d5c636832c5556ead90562dGreg Kroah-Hartman result); 35243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin} 35343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 3549fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartmanstatic struct usb_driver metrousb_driver = { 3559fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .name = "metro-usb", 3569fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .probe = usb_serial_probe, 3579fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .disconnect = usb_serial_disconnect, 3589fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .id_table = id_table 3599fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman}; 3609fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 3619fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartmanstatic struct usb_serial_driver metrousb_device = { 3629fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .driver = { 3639fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .owner = THIS_MODULE, 3649fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .name = "metro-usb", 3659fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman }, 3669fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .description = "Metrologic USB to serial converter.", 3679fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .id_table = id_table, 3689fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .num_ports = 1, 3699fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .open = metrousb_open, 3709fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .close = metrousb_cleanup, 3719fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .read_int_callback = metrousb_read_int_callback, 3729fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .attach = metrousb_startup, 3739fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .release = metrousb_shutdown, 3749fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .throttle = metrousb_throttle, 3759fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .unthrottle = metrousb_unthrottle, 3769fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .tiocmget = metrousb_tiocmget, 3779fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman .tiocmset = metrousb_tiocmset, 3789fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman}; 3799fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 3809fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartmanstatic struct usb_serial_driver * const serial_drivers[] = { 3819fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman &metrousb_device, 3829fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman NULL, 3839fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman}; 3849fbd1649d54edc614c64ab075f7485622fd6450aGreg Kroah-Hartman 3851935e357bb2a3031772730293a3725e3cca07778Greg Kroah-Hartmanmodule_usb_serial_driver(metrousb_driver, serial_drivers); 3861935e357bb2a3031772730293a3725e3cca07778Greg Kroah-Hartman 38743d186fe992da93bb1dd34a7dd4534719624431cAleksey BabahinMODULE_LICENSE("GPL"); 388d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-HartmanMODULE_AUTHOR("Philip Nicastro"); 389d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-HartmanMODULE_AUTHOR("Aleksey Babahin <tamerlan311@gmail.com>"); 390d4cbd6e990a798d21577ee2f42a3880da09edf3aGreg Kroah-HartmanMODULE_DESCRIPTION(DRIVER_DESC); 39143d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin 39243d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahin/* Module input parameters */ 39343d186fe992da93bb1dd34a7dd4534719624431cAleksey Babahinmodule_param(debug, bool, S_IRUGO | S_IWUSR); 39443d186fe992da93bb1dd34a7dd4534719624431cAleksey BabahinMODULE_PARM_DESC(debug, "Print debug info (bool 1=on, 0=off)"); 395