157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman/*
257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman * Opticon USB barcode to serial driver
357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman *
4309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen * Copyright (C) 2011 Martin Jansen <martin.jansen@opticon.com>
5648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman * Copyright (C) 2008 - 2009 Greg Kroah-Hartman <gregkh@suse.de>
6648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman * Copyright (C) 2008 - 2009 Novell Inc.
757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman *
857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman *	This program is free software; you can redistribute it and/or
957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman *	modify it under the terms of the GNU General Public License version
1057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman *	2 as published by the Free Software Foundation.
1157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman */
1257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
1357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/kernel.h>
1457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/init.h>
1557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/tty.h>
1657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/tty_driver.h>
175a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
1857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/tty_flip.h>
19faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman#include <linux/serial.h>
2057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/module.h>
2157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/usb.h>
2257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/usb/serial.h>
2357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman#include <linux/uaccess.h>
2457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
25309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen#define CONTROL_RTS			0x02
26309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen#define RESEND_CTS_STATE	0x03
27309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
28309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen/* max number of write urbs in flight */
29309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen#define URB_UPPER_LIMIT	8
30309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
31309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen/* This driver works for the Opticon 1D barcode reader
32309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen * an examples of 1D barcode types are EAN, UPC, Code39, IATA etc.. */
33309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen#define DRIVER_DESC	"Opticon USB barcode to serial driver (1D)"
34309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
3590ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug;
3657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
377d40d7e85a25e01948bcb4dc3eda1355af318337Németh Mártonstatic const struct usb_device_id id_table[] = {
3857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	{ USB_DEVICE(0x065a, 0x0009) },
3957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	{ },
4057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman};
4157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-HartmanMODULE_DEVICE_TABLE(usb, id_table);
4257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
4357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman/* This structure holds all of the individual device information */
4457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanstruct opticon_private {
4557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct usb_device *udev;
4657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct usb_serial *serial;
4757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct usb_serial_port *port;
4857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	unsigned char *bulk_in_buffer;
4957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct urb *bulk_read_urb;
5057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	int buffer_size;
5157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	u8 bulk_address;
5257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spinlock_t lock;	/* protects the following flags */
5357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	bool throttled;
5457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	bool actually_throttled;
5557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	bool rts;
56309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	bool cts;
57648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	int outstanding_urbs;
5857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman};
5957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
60648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
61309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
62309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansenstatic void opticon_read_bulk_callback(struct urb *urb)
6357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman{
6457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct opticon_private *priv = urb->context;
6557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	unsigned char *data = urb->transfer_buffer;
6657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct usb_serial_port *port = priv->port;
6757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	int status = urb->status;
6857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct tty_struct *tty;
6957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	int result;
7057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	int data_length;
71309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	unsigned long flags;
7257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
7357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
7457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
7557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	switch (status) {
7657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	case 0:
7757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		/* success */
7857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		break;
7957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	case -ECONNRESET:
8057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	case -ENOENT:
8157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	case -ESHUTDOWN:
8257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		/* this urb is terminated, clean up */
8357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		dbg("%s - urb shutting down with status: %d",
8457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		    __func__, status);
8557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		return;
8657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	default:
8757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		dbg("%s - nonzero urb status received: %d",
8857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		    __func__, status);
8957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		goto exit;
9057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	}
9157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
9257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length,
9357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			      data);
9457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
9557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	if (urb->actual_length > 2) {
9657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		data_length = urb->actual_length - 2;
9757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
9857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		/*
9957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		 * Data from the device comes with a 2 byte header:
10057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		 *
10157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		 * <0x00><0x00>data...
102309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		 *	This is real data to be sent to the tty layer
10357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		 * <0x00><0x01)level
104309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		 *	This is a CTS level change, the third byte is the CTS
105309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		 *	value (0 for low, 1 for high).
10657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		 */
10757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		if ((data[0] == 0x00) && (data[1] == 0x00)) {
10857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			/* real data, send it to the tty layer */
10957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			tty = tty_port_tty_get(&port->port);
11057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			if (tty) {
11197cd8dc4ca9a1a5efb2cc38758e01492e3b013e2Alon Ziv				tty_insert_flip_string(tty, data + 2,
11297cd8dc4ca9a1a5efb2cc38758e01492e3b013e2Alon Ziv						       data_length);
113a108bfcb372d8c4452701039308fb95747911c59Alan Cox				tty_flip_buffer_push(tty);
11457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman				tty_kref_put(tty);
11557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			}
11657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		} else {
11757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			if ((data[0] == 0x00) && (data[1] == 0x01)) {
118309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen				spin_lock_irqsave(&priv->lock, flags);
11925985edcedea6396277003854657b5f3cb31a628Lucas De Marchi				/* CTS status information package */
12057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman				if (data[2] == 0x00)
121309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen					priv->cts = false;
12257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman				else
123309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen					priv->cts = true;
124309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen				spin_unlock_irqrestore(&priv->lock, flags);
12557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			} else {
126c6f694af8318a526c639306d9d07ee33cb7c168aAlon Ziv				dev_dbg(&priv->udev->dev,
127c6f694af8318a526c639306d9d07ee33cb7c168aAlon Ziv					"Unknown data packet received from the device:"
128c6f694af8318a526c639306d9d07ee33cb7c168aAlon Ziv					" %2x %2x\n",
129c6f694af8318a526c639306d9d07ee33cb7c168aAlon Ziv					data[0], data[1]);
13057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			}
13157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		}
13257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	} else {
13357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		dev_dbg(&priv->udev->dev,
1349ddc5b6f18fbac07d2746566b73b89e89fdd4e6aUwe Kleine-König			"Improper amount of data received from the device, "
13557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			"%d bytes", urb->actual_length);
13657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	}
13757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
13857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanexit:
13957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_lock(&priv->lock);
14057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
14157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	/* Continue trying to always read if we should */
14257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	if (!priv->throttled) {
14357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
14457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman				  usb_rcvbulkpipe(priv->udev,
14557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman						  priv->bulk_address),
14657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman				  priv->bulk_in_buffer, priv->buffer_size,
147309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen				  opticon_read_bulk_callback, priv);
14897cd8dc4ca9a1a5efb2cc38758e01492e3b013e2Alon Ziv		result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC);
14957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		if (result)
15057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			dev_err(&port->dev,
15157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			    "%s - failed resubmitting read urb, error %d\n",
15257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman							__func__, result);
15357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	} else
15457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		priv->actually_throttled = true;
15557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_unlock(&priv->lock);
15657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman}
15757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
158309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansenstatic int send_control_msg(struct usb_serial_port *port, u8 requesttype,
159309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen				u8 val)
160309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen{
161309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	struct usb_serial *serial = port->serial;
162309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	int retval;
163309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	u8 buffer[2];
164309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
165309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	buffer[0] = val;
166309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* Send the message to the vendor control endpoint
167309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	 * of the connected device */
168309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
169309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen				requesttype,
170309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen				USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
171309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen				0, 0, buffer, 1, 0);
172309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
173309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	return retval;
174309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen}
175309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
176a509a7e478e4766114d69f12d19d644ac63e9765Alan Coxstatic int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
17757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman{
17857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
17957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	unsigned long flags;
18057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	int result = 0;
18157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
18257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
18357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
18457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
18557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->throttled = false;
18657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->actually_throttled = false;
18757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->port = port;
188309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	priv->rts = false;
18957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
19057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
191309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* Clear RTS line */
192309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	send_control_msg(port, CONTROL_RTS, 0);
193309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
194309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* Setup the read URB and start reading from the device */
19557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev,
19657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			  usb_rcvbulkpipe(priv->udev,
19757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman					  priv->bulk_address),
19857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			  priv->bulk_in_buffer, priv->buffer_size,
199309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen			  opticon_read_bulk_callback, priv);
200309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
201309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* clear the halt status of the enpoint */
202309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	usb_clear_halt(priv->udev, priv->bulk_read_urb->pipe);
203309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
20457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL);
20557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	if (result)
20657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		dev_err(&port->dev,
20757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			"%s - failed resubmitting read urb, error %d\n",
20857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			__func__, result);
209309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* Request CTS line state, sometimes during opening the current
210309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	 * CTS state can be missed. */
211309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	send_control_msg(port, RESEND_CTS_STATE, 1);
21257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	return result;
21357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman}
21457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
215335f8514f200e63d689113d29cb7253a5c282967Alan Coxstatic void opticon_close(struct usb_serial_port *port)
21657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman{
21757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
21857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
21957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
22057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
22157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	/* shutdown our urbs */
22257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	usb_kill_urb(priv->bulk_read_urb);
22357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman}
22457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
225309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansenstatic void opticon_write_control_callback(struct urb *urb)
226648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman{
227648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	struct opticon_private *priv = urb->context;
228648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	int status = urb->status;
229648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	unsigned long flags;
230648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
231648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	/* free up the transfer buffer, as usb_free_urb() does not do this */
232648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	kfree(urb->transfer_buffer);
233648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
2340d930e51cfe6f748339d7d13b3fad2b91a1d92c2Alon Ziv	/* setup packet may be set if we're using it for writing */
2350d930e51cfe6f748339d7d13b3fad2b91a1d92c2Alon Ziv	kfree(urb->setup_packet);
2360d930e51cfe6f748339d7d13b3fad2b91a1d92c2Alon Ziv
237648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	if (status)
238648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		dbg("%s - nonzero write bulk status received: %d",
239648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		    __func__, status);
240648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
241648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
242648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	--priv->outstanding_urbs;
243648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
244648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
245648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	usb_serial_port_softint(priv->port);
246648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman}
247648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
248648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartmanstatic int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
249648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman			 const unsigned char *buf, int count)
250648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman{
251648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
252648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	struct usb_serial *serial = port->serial;
253648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	struct urb *urb;
254648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	unsigned char *buffer;
255648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	unsigned long flags;
256648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	int status;
257309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	struct usb_ctrlrequest *dr;
258648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
259648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
260648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
261648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
262648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
263648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		spin_unlock_irqrestore(&priv->lock, flags);
264759f3634267a67ac90f3fa7fc06510dfd43b4e45Joe Perches		dbg("%s - write limit hit", __func__);
265648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		return 0;
266648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	}
267648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	priv->outstanding_urbs++;
268648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
269648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
270648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	buffer = kmalloc(count, GFP_ATOMIC);
271648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	if (!buffer) {
272648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		dev_err(&port->dev, "out of memory\n");
273648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		count = -ENOMEM;
274309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
275648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		goto error_no_buffer;
276648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	}
277648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
278648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	urb = usb_alloc_urb(0, GFP_ATOMIC);
279648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	if (!urb) {
280648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		dev_err(&port->dev, "no more free urbs\n");
281648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		count = -ENOMEM;
282648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		goto error_no_urb;
283648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	}
284648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
285648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	memcpy(buffer, buf, count);
286648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
287648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
288648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
289309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* The conncected devices do not have a bulk write endpoint,
290309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	 * to transmit data to de barcode device the control endpoint is used */
291309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
292b0795bbf6dc6bd0a7a37d9d1ef4558e9e2b0acd6Julia Lawall	if (!dr) {
293b0795bbf6dc6bd0a7a37d9d1ef4558e9e2b0acd6Julia Lawall		dev_err(&port->dev, "out of memory\n");
294b0795bbf6dc6bd0a7a37d9d1ef4558e9e2b0acd6Julia Lawall		count = -ENOMEM;
295b0795bbf6dc6bd0a7a37d9d1ef4558e9e2b0acd6Julia Lawall		goto error;
296b0795bbf6dc6bd0a7a37d9d1ef4558e9e2b0acd6Julia Lawall	}
297309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
298309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
299309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	dr->bRequest = 0x01;
300309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	dr->wValue = 0;
301309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	dr->wIndex = 0;
302309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	dr->wLength = cpu_to_le16(count);
303309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
304309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	usb_fill_control_urb(urb, serial->dev,
305309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		usb_sndctrlpipe(serial->dev, 0),
306309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		(unsigned char *)dr, buffer, count,
307309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		opticon_write_control_callback, priv);
308648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
309648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	/* send it down the pipe */
310648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	status = usb_submit_urb(urb, GFP_ATOMIC);
311648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	if (status) {
312648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		dev_err(&port->dev,
313309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		"%s - usb_submit_urb(write endpoint) failed status = %d\n",
314648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman							__func__, status);
315648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		count = status;
316648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		goto error;
317648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	}
318648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
319648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	/* we are done with this urb, so let the host driver
320648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	 * really free it when it is finished with it */
321648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	usb_free_urb(urb);
322648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
323648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	return count;
324648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartmanerror:
325648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	usb_free_urb(urb);
326648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartmanerror_no_urb:
327648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	kfree(buffer);
328648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartmanerror_no_buffer:
329648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
330648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	--priv->outstanding_urbs;
331648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
332648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	return count;
333648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman}
334648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
335648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartmanstatic int opticon_write_room(struct tty_struct *tty)
336648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman{
337648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	struct usb_serial_port *port = tty->driver_data;
338648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
339648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	unsigned long flags;
340648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
341648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
342648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
343648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	/*
344648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	 * We really can take almost anything the user throws at us
345648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	 * but let's pick a nice big number to tell the tty
346648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	 * layer that we have lots of free space, unless we don't.
347648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	 */
348648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
349648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) {
350648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		spin_unlock_irqrestore(&priv->lock, flags);
351759f3634267a67ac90f3fa7fc06510dfd43b4e45Joe Perches		dbg("%s - write limit hit", __func__);
352648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman		return 0;
353648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	}
354648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
355648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
356648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	return 2048;
357648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman}
358648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman
35957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanstatic void opticon_throttle(struct tty_struct *tty)
36057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman{
36157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct usb_serial_port *port = tty->driver_data;
36257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
36357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	unsigned long flags;
36457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
36557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
36657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
36757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->throttled = true;
36857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
36957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman}
37057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
37157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
37257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanstatic void opticon_unthrottle(struct tty_struct *tty)
37357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman{
37457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct usb_serial_port *port = tty->driver_data;
37557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
37657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	unsigned long flags;
37788fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum	int result, was_throttled;
37857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
37957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
38057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
38157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
38257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->throttled = false;
38388fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum	was_throttled = priv->actually_throttled;
38457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->actually_throttled = false;
38557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
38657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
38788fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum	if (was_throttled) {
38888fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum		result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC);
38988fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum		if (result)
39088fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum			dev_err(&port->dev,
39188fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum				"%s - failed submitting read urb, error %d\n",
39257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman							__func__, result);
39388fa6590b30ef8c4d41923449ada104f915d8df8Oliver Neukum	}
39457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman}
39557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
39660b33c133ca0b7c0b6072c87234b63fee6e80558Alan Coxstatic int opticon_tiocmget(struct tty_struct *tty)
397faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman{
398faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	struct usb_serial_port *port = tty->driver_data;
399faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
400faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	unsigned long flags;
401faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	int result = 0;
402faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
403faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	dbg("%s - port %d", __func__, port->number);
404309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	if (!usb_get_intfdata(port->serial->interface))
405309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		return -ENODEV;
406faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
407faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	spin_lock_irqsave(&priv->lock, flags);
408faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	if (priv->rts)
409309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		result |= TIOCM_RTS;
410309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	if (priv->cts)
411309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		result |= TIOCM_CTS;
412faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	spin_unlock_irqrestore(&priv->lock, flags);
413faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
414faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	dbg("%s - %x", __func__, result);
415faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	return result;
416faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman}
417faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
4184acfaf829dacb8f8170b439d30065e8d2cfdaac9Randy Dunlapstatic int opticon_tiocmset(struct tty_struct *tty,
419309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen			   unsigned int set, unsigned int clear)
420309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen{
421309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	struct usb_serial_port *port = tty->driver_data;
422309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	struct opticon_private *priv = usb_get_serial_data(port->serial);
423309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	unsigned long flags;
424309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	bool rts;
425309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	bool changed = false;
426309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
427309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	if (!usb_get_intfdata(port->serial->interface))
428309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		return -ENODEV;
429309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* We only support RTS so we only handle that */
430309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	spin_lock_irqsave(&priv->lock, flags);
431309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
432309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	rts = priv->rts;
433309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	if (set & TIOCM_RTS)
434309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		priv->rts = true;
435309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	if (clear & TIOCM_RTS)
436309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		priv->rts = false;
437309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	changed = rts ^ priv->rts;
438309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	spin_unlock_irqrestore(&priv->lock, flags);
439309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
440309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	if (!changed)
441309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen		return 0;
442309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
443309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	/* Send the new RTS state to the connected device */
444309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	return send_control_msg(port, CONTROL_RTS, !rts);
445309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen}
446309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen
447faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartmanstatic int get_serial_info(struct opticon_private *priv,
448faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman			   struct serial_struct __user *serial)
449faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman{
450faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	struct serial_struct tmp;
451faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
452faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	if (!serial)
453faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman		return -EFAULT;
454faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
455faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	memset(&tmp, 0x00, sizeof(tmp));
456faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
457faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	/* fake emulate a 16550 uart to make userspace code happy */
458faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.type		= PORT_16550A;
459faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.line		= priv->serial->minor;
460faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.port		= 0;
461faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.irq			= 0;
462faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.flags		= ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
463faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.xmit_fifo_size	= 1024;
464faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.baud_base		= 9600;
465faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.close_delay		= 5*HZ;
466faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	tmp.closing_wait	= 30*HZ;
467faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
468faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	if (copy_to_user(serial, &tmp, sizeof(*serial)))
469faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman		return -EFAULT;
470faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	return 0;
471faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman}
472faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
47300a0d0d65b61241a718d0aee96f46b9a2d93bf26Alan Coxstatic int opticon_ioctl(struct tty_struct *tty,
474faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman			 unsigned int cmd, unsigned long arg)
475faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman{
476faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	struct usb_serial_port *port = tty->driver_data;
477faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(port->serial);
478faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
479faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
480faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
481faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	switch (cmd) {
482faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	case TIOCGSERIAL:
483faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman		return get_serial_info(priv,
484faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman				       (struct serial_struct __user *)arg);
485faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	}
486faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
487faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	return -ENOIOCTLCMD;
488faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman}
489faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman
49057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanstatic int opticon_startup(struct usb_serial *serial)
49157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman{
49257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct opticon_private *priv;
49357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct usb_host_interface *intf;
49457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	int i;
49557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	int retval = -ENOMEM;
49657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	bool bulk_in_found = false;
49757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
49857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	/* create our private serial structure */
49957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
50057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	if (priv == NULL) {
50157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
50257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		return -ENOMEM;
50357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	}
50457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	spin_lock_init(&priv->lock);
50557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->serial = serial;
50657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->port = serial->port[0];
50757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	priv->udev = serial->dev;
508309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	priv->outstanding_urbs = 0;	/* Init the outstanding urbs */
50957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
51057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	/* find our bulk endpoint */
51157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	intf = serial->interface->altsetting;
51257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	for (i = 0; i < intf->desc.bNumEndpoints; ++i) {
51357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		struct usb_endpoint_descriptor *endpoint;
51457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
51557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		endpoint = &intf->endpoint[i].desc;
51657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		if (!usb_endpoint_is_bulk_in(endpoint))
51757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			continue;
51857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
51957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		priv->bulk_read_urb = usb_alloc_urb(0, GFP_KERNEL);
52057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		if (!priv->bulk_read_urb) {
52157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			dev_err(&priv->udev->dev, "out of memory\n");
52257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			goto error;
52357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		}
52457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
52529cc88979a8818cd8c5019426e945aed118b400eKuninori Morimoto		priv->buffer_size = usb_endpoint_maxp(endpoint) * 2;
52657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL);
52757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		if (!priv->bulk_in_buffer) {
52857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			dev_err(&priv->udev->dev, "out of memory\n");
52957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			goto error;
53057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		}
53157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
53257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		priv->bulk_address = endpoint->bEndpointAddress;
53357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
53457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		bulk_in_found = true;
53557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		break;
53657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		}
53757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
53857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	if (!bulk_in_found) {
53957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		dev_err(&priv->udev->dev,
54057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman			"Error - the proper endpoints were not found!\n");
54157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		goto error;
54257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	}
54357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
54457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	usb_set_serial_data(serial, priv);
54557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	return 0;
54657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
54757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanerror:
54857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	usb_free_urb(priv->bulk_read_urb);
54957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	kfree(priv->bulk_in_buffer);
55057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	kfree(priv);
55157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	return retval;
55257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman}
55357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
554f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void opticon_disconnect(struct usb_serial *serial)
55557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman{
55657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	struct opticon_private *priv = usb_get_serial_data(serial);
55757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
55857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	dbg("%s", __func__);
55957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
56057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	usb_kill_urb(priv->bulk_read_urb);
56157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	usb_free_urb(priv->bulk_read_urb);
562f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern}
563f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
564f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void opticon_release(struct usb_serial *serial)
565f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern{
566f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	struct opticon_private *priv = usb_get_serial_data(serial);
567f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
568f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	dbg("%s", __func__);
569f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
57057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	kfree(priv->bulk_in_buffer);
57157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	kfree(priv);
57257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman}
57357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
574d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukumstatic int opticon_suspend(struct usb_interface *intf, pm_message_t message)
575d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum{
576d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	struct usb_serial *serial = usb_get_intfdata(intf);
577d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	struct opticon_private *priv = usb_get_serial_data(serial);
578d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum
579d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	usb_kill_urb(priv->bulk_read_urb);
580d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	return 0;
581d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum}
582d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum
583d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukumstatic int opticon_resume(struct usb_interface *intf)
584d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum{
585d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	struct usb_serial *serial = usb_get_intfdata(intf);
586d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	struct opticon_private *priv = usb_get_serial_data(serial);
587d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	struct usb_serial_port *port = serial->port[0];
588d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	int result;
589d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum
59082fc5943430e3cbf15033ed4186a73f90906345dAlan Cox	mutex_lock(&port->port.mutex);
5912a0785ea375fe93cd480599bb40d0c837ff72a2eAlan Cox	/* This is protected by the port mutex against close/open */
5922a0785ea375fe93cd480599bb40d0c837ff72a2eAlan Cox	if (test_bit(ASYNCB_INITIALIZED, &port->port.flags))
593d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum		result = usb_submit_urb(priv->bulk_read_urb, GFP_NOIO);
594d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	else
595d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum		result = 0;
59682fc5943430e3cbf15033ed4186a73f90906345dAlan Cox	mutex_unlock(&port->port.mutex);
597d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	return result;
598d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum}
59957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
60057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanstatic struct usb_driver opticon_driver = {
60157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.name =		"opticon",
60257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.probe =	usb_serial_probe,
60357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.disconnect =	usb_serial_disconnect,
604d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	.suspend =	opticon_suspend,
605d1c0713daea5d1d881ecc8707458ca6746031376Oliver Neukum	.resume =	opticon_resume,
60657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.id_table =	id_table,
60757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman};
60857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
60957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanstatic struct usb_serial_driver opticon_device = {
61057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.driver = {
61157262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		.owner =	THIS_MODULE,
61257262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman		.name =		"opticon",
61357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	},
61457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.id_table =		id_table,
61557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.num_ports =		1,
61657262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.attach =		opticon_startup,
61757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.open =			opticon_open,
61857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.close =		opticon_close,
619648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	.write =		opticon_write,
620648d4e16567eae4c643bd2125e91128f06c0d3adGreg Kroah-Hartman	.write_room = 		opticon_write_room,
621f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	.disconnect =		opticon_disconnect,
622f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	.release =		opticon_release,
62357262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.throttle = 		opticon_throttle,
62457262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman	.unthrottle =		opticon_unthrottle,
625faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	.ioctl =		opticon_ioctl,
626faac64ad9c7b1aa56a10be6b5f9b813789e81dfdGreg Kroah-Hartman	.tiocmget =		opticon_tiocmget,
627309a057932ab20057da9fe4cb18fb61803dfc924Martin Jansen	.tiocmset =		opticon_tiocmset,
62857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman};
62957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
630f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Sternstatic struct usb_serial_driver * const serial_drivers[] = {
631f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Stern	&opticon_device, NULL
632f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Stern};
633f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Stern
634e206b7f831da365a38b22f72ba42db288db71c65Greg Kroah-Hartmanmodule_usb_serial_driver(opticon_driver, serial_drivers);
63557262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
636309a057932ab20057da9fe4cb18fb61803dfc924Martin JansenMODULE_DESCRIPTION(DRIVER_DESC);
63757262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-HartmanMODULE_LICENSE("GPL");
63857262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartman
63957262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-Hartmanmodule_param(debug, bool, S_IRUGO | S_IWUSR);
64057262b82d601c5ca8e3db219aebd332950f62ba1Greg Kroah-HartmanMODULE_PARM_DESC(debug, "Debug enabled or not");
641