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