omninet.c revision fdc2deb3892e802e916d1df7b1587aa0dbf3b271
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB ZyXEL omni.net LCD PLUS driver
30d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	This program is free software; you can redistribute it and/or
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	modify it under the terms of the GNU General Public License version
6bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	2 as published by the Free Software Foundation.
7bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
8bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * See Documentation/usb/usb-serial.txt for more information on using this driver
9bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
10bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * Please report both successes and troubles to the author at omninet@kroah.com
11bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
12bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * (05/30/2001) gkh
13bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	switched from using spinlock to a semaphore, which fixes lots of problems.
14bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
15bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * (04/08/2001) gb
16bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	Identify version on module load.
17bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
18bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * (11/01/2000) Adam J. Richter
19bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	usb_device_id table support
20bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
21bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * (10/05/2000) gkh
22bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	Fixed bug with urb->dev not being set properly, now that the usb
23bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	core needs it.
24bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *
25bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * (08/28/2000) gkh
26bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie *	Added locks for SMP safeness.
270d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie *	Fixed MOD_INC and MOD_DEC logic and the ability to open a port more
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	than once.
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Fixed potential race in omninet_write_bulk_callback
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3179e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes * (07/19/2000) gkh
3279e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes *	Added module_init and module_exit functions to handle the fact that this
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	driver is a loadable module now.
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
4184b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie#include <linux/tty.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
45d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard#include <linux/spinlock.h>
46d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard#include <asm/uaccess.h>
47d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard#include <linux/usb.h>
48585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes#include <linux/usb/serial.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int debug;
51d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard
52585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes/*
53d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard * Version Information
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_VERSION "v1.1"
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR "Alessandro Zummo"
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB ZyXEL omni.net LCD PLUS Driver"
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ZYXEL_VENDOR_ID		0x0586
6098787c057fdefdce6230ff46f2c1105835005a4cChris Wilson#define ZYXEL_OMNINET_ID	0x1000
6198787c057fdefdce6230ff46f2c1105835005a4cChris Wilson#define BT_IGNITIONPRO_ID	0x2000  /* This one seems to be a re-branded ZyXEL device */
6298787c057fdefdce6230ff46f2c1105835005a4cChris Wilson
6398787c057fdefdce6230ff46f2c1105835005a4cChris Wilson/* function prototypes */
6498787c057fdefdce6230ff46f2c1105835005a4cChris Wilsonstatic int  omninet_open		(struct usb_serial_port *port, struct file *filp);
6598787c057fdefdce6230ff46f2c1105835005a4cChris Wilsonstatic void omninet_close		(struct usb_serial_port *port, struct file *filp);
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_read_bulk_callback	(struct urb *urb);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_write_bulk_callback	(struct urb *urb);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  omninet_write		(struct usb_serial_port *port, const unsigned char *buf, int count);
69d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packardstatic int  omninet_write_room		(struct usb_serial_port *port);
70d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packardstatic void omninet_shutdown		(struct usb_serial *serial);
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int omninet_attach		(struct usb_serial *serial);
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
73d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packardstatic struct usb_device_id id_table [] = {
74d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard	{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
75d3a6d4467ca44833bcb4ba1893a7aeaae939e4d5Keith Packard	{ USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }						/* Terminating entry */
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7820caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, id_table);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packardstatic struct usb_driver omninet_driver = {
82398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.name =		"omninet",
83398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.probe =	usb_serial_probe,
84398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.disconnect =	usb_serial_disconnect,
853043c60c485ad694392d3f71bd7ef9f5c5f7cfddEric Anholt	.id_table =	id_table,
86398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.no_dynamic_id = 	1,
87398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard};
88398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
89398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
90398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packardstatic struct usb_serial_driver zyxel_omninet_device = {
91398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.driver = {
92398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard		.owner =	THIS_MODULE,
93398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard		.name =		"omninet",
94398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	},
95398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.description =		"ZyXEL - omni.net lcd plus usb",
96398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.usb_driver =		&omninet_driver,
97398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.id_table =		id_table,
98398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.num_interrupt_in =	1,
99398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.num_bulk_in =		1,
100398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.num_bulk_out =		2,
101398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.num_ports =		1,
102398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.attach =		omninet_attach,
103398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.open =			omninet_open,
104398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.close =		omninet_close,
105398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.write =		omninet_write,
106398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.write_room =		omninet_write_room,
107398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.read_bulk_callback =	omninet_read_bulk_callback,
108398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.write_bulk_callback =	omninet_write_bulk_callback,
109398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	.shutdown =		omninet_shutdown,
1103043c60c485ad694392d3f71bd7ef9f5c5f7cfddEric Anholt};
111398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
112398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
113398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard/* The protocol.
114398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *
115398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard * The omni.net always exchange 64 bytes of data with the host. The first
116398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard * four bytes are the control header, you can see it in the above structure.
117398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *
118398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard * oh_seq is a sequence number. Don't know if/how it's used.
119398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard * oh_len is the length of the data bytes in the packet.
120398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard * oh_xxx Bit-mapped, related to handshaking and status info.
121398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *	I normally set it to 0x03 in trasmitted frames.
122398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *	7: Active when the TA is in a CONNECTed state.
123398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *	6: unknown
124398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *	5: handshaking, unknown
125398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *	4: handshaking, unknown
126398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard *	3: unknown, usually 0
12784b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie *	2: unknown, usually 0
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	1: handshaking, unknown, usually set to 1 in trasmitted frames
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	0: handshaking, unknown, usually set to 1 in trasmitted frames
1307c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie * oh_pad Probably a pad byte.
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * After the header you will find data bytes if oh_len was greater than zero.
13379e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes *
13479e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes */
13579e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes
13679e539453b34e35f39299a899d263b0a1f1670bdJesse Barnesstruct omninet_header
13779e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes{
13879e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes	__u8	oh_seq;
13979e539453b34e35f39299a899d263b0a1f1670bdJesse Barnes	__u8	oh_len;
140585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes	__u8	oh_xxx;
141585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes	__u8	oh_pad;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct omninet_data
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1467c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie	__u8	od_outseq;	// Sequence number for bulk_out URBs
1477c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie};
1487c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie
1497c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airliestatic int omninet_attach (struct usb_serial *serial)
1507c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie{
1517c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie	struct omninet_data *od;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *port = serial->port[0];
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15484b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie	od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if( !od ) {
156ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes		err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data));
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_set_serial_port_data(port, od);
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
161ed4cb4142b242d8090d3811d5eb4abf6aa985bc8Eric Anholt}
162b5e89ed53ed8d24f83ba1941c07382af00ed238eDave Airlie
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int omninet_open (struct usb_serial_port *port, struct file *filp)
164ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes{
165ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	struct usb_serial	*serial = port->serial;
1663043c60c485ad694392d3f71bd7ef9f5c5f7cfddEric Anholt	struct usb_serial_port	*wport;
1673043c60c485ad694392d3f71bd7ef9f5c5f7cfddEric Anholt	int			result = 0;
168ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes
169ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	dbg("%s - port %d", __FUNCTION__, port->number);
170dc7a93190c21edbf3ed23e678ad04f852b9cff28Wang Zhenyu
171398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	wport = serial->port[1];
172398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard	wport->tty = port->tty;
173398c9cb20b5c6c5d1313912b937d653a46fec578Keith Packard
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Start reading from the device */
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb(port->read_urb, serial->dev,
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
178ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes		      omninet_read_bulk_callback, port);
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
180ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	if (result) {
1817c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie		err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1833a03ac1a0223f779a3de313523408ddb099e5679Dave Airlie
1843a03ac1a0223f779a3de313523408ddb099e5679Dave Airlie	return result;
1853a03ac1a0223f779a3de313523408ddb099e5679Dave Airlie}
1863a03ac1a0223f779a3de313523408ddb099e5679Dave Airlie
1873a03ac1a0223f779a3de313523408ddb099e5679Dave Airliestatic void omninet_close (struct usb_serial_port *port, struct file * filp)
1883a03ac1a0223f779a3de313523408ddb099e5679Dave Airlie{
1893a03ac1a0223f779a3de313523408ddb099e5679Dave Airlie	dbg("%s - port %d", __FUNCTION__, port->number);
1903a03ac1a0223f779a3de313523408ddb099e5679Dave Airlie	usb_kill_urb(port->read_urb);
191673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt}
192673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt
193673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt
194673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt#define OMNINET_DATAOFFSET	0x04
195673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt#define OMNINET_HEADERLEN	sizeof(struct omninet_header)
196673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt#define OMNINET_BULKOUTSIZE 	(64 - OMNINET_HEADERLEN)
197673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_read_bulk_callback (struct urb *urb)
199673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt{
200673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	struct usb_serial_port 	*port 	= (struct usb_serial_port *)urb->context;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char 		*data 	= urb->transfer_buffer;
202673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	struct omninet_header 	*header = (struct omninet_header *) &data[0];
203673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	int status = urb->status;
204673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	int i;
205673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	int result;
206673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
2086fb88588555a18792a27f483887fe1f2af5f9c9bJesse Barnes
209673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	if (status) {
210673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt		dbg("%s - nonzero read bulk status received: %d",
211673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt		    __FUNCTION__, status);
212673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt		return;
213673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	}
214673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt
215673a394b1e3b69be886ff24abfd6df97c52e8d08Eric Anholt	if ((debug) && (header->oh_xxx != 0x30)) {
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (urb->actual_length) {
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk (KERN_DEBUG __FILE__ ": omninet_read %d: ", header->oh_len);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			for (i = 0; i < (header->oh_len + OMNINET_HEADERLEN); i++) {
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk ("%.2x ", data[i]);
220a6b54f3f5050c0cbc0c35dd48064846c6302706bMichel Dänzer			}
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk ("\n");
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2247c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie
2257c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie	if (urb->actual_length && header->oh_len) {
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < header->oh_len; i++) {
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 tty_insert_flip_char(port->tty, data[OMNINET_DATAOFFSET + i], 0);
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  	}
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  	tty_flip_buffer_push(port->tty);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Continue trying to always read  */
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb(urb, port->serial->dev,
23484b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airlie		      usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      urb->transfer_buffer, urb->transfer_buffer_length,
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      omninet_read_bulk_callback, port);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(urb, GFP_ATOMIC);
238bf9d89295233ae2ba7b312c78ee5657307b09f4cHarvey Harrison	if (result)
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
24320caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count)
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial 	*serial	= port->serial;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port 	*wport	= serial->port[1];
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24920caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	struct omninet_data 	*od 	= usb_get_serial_port_data(port);
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct omninet_header	*header = (struct omninet_header *) wport->write_urb->transfer_buffer;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			result;
253dc7a93190c21edbf3ed23e678ad04f852b9cff28Wang Zhenyu
254585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes	dbg("%s - port %d", __FUNCTION__, port->number);
255dc7a93190c21edbf3ed23e678ad04f852b9cff28Wang Zhenyu
256585fb111348f7cdc30c6a1b903987612ddeafb23Jesse Barnes	if (count == 0) {
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - write request of 0 bytes", __FUNCTION__);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (0);
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&wport->lock);
262c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt	if (wport->write_urb_busy) {
263c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt		spin_unlock_bh(&wport->lock);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - already writing", __FUNCTION__);
265c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt		return 0;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport->write_urb_busy = 1;
268c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt	spin_unlock_bh(&wport->lock);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
270ba8bbcf6ff4650712f64c0ef61139c73898e2165Jesse Barnes	count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy (wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count);
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, wport->write_urb->transfer_buffer);
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2760d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie	header->oh_seq 	= od->od_outseq++;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_len 	= count;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_xxx  = 0x03;
27920caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt	header->oh_pad 	= 0x00;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send the data out the bulk port, always 64 bytes */
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport->write_urb->transfer_buffer_length = 64;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport->write_urb->dev = serial->dev;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result) {
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wport->write_urb_busy = 0;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		result = count;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int omninet_write_room (struct usb_serial_port *port)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial 	*serial = port->serial;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port 	*wport 	= serial->port[1];
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int room = 0; // Default: no room
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wport->write_urb_busy)
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		room = wport->bulk_out_size - OMNINET_HEADERLEN;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - returns %d", __FUNCTION__, room);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (room);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_write_bulk_callback (struct urb *urb)
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*	struct omninet_header	*header = (struct omninet_header  *) urb->transfer_buffer; */
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port 	*port   = (struct usb_serial_port *) urb->context;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status = urb->status;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %0x\n", __FUNCTION__, port->number);
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	port->write_urb_busy = 0;
320b5e89ed53ed8d24f83ba1941c07382af00ed238eDave Airlie	if (status) {
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - nonzero write bulk status received: %d",
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    __FUNCTION__, status);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_port_softint(port);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_shutdown (struct usb_serial *serial)
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *wport = serial->port[1];
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port *port = serial->port[0];
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("%s", __FUNCTION__);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(wport->write_urb);
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(usb_get_serial_port_data(port));
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init omninet_init (void)
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_serial_register(&zyxel_omninet_device);
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_serial_register;
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&omninet_driver);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_register;
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info(DRIVER_VERSION ":" DRIVER_DESC);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_register:
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister(&zyxel_omninet_device);
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_serial_register:
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
357bc5f4523f772cc7629c5c5a46cf4f2a07a5500b8Dave Airlie
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit omninet_exit (void)
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister (&omninet_driver);
362201361a54ed187d8595a283e3a4ddb213bc8323bEric Anholt	usb_serial_deregister (&zyxel_omninet_device);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(omninet_init);
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(omninet_exit);
368de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie
36920caafa6ecb2487d9b223aa33e7cc704f912a758Eric AnholtMODULE_AUTHOR( DRIVER_AUTHOR );
370de227f5f32775d86e5c780a7cffdd2e08574f7fbDave AirlieMODULE_DESCRIPTION( DRIVER_DESC );
371c29b669caae4ed1630ef479e54bdde126a0378ecAlan HourihaneMODULE_LICENSE("GPL");
372de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, bool, S_IRUGO | S_IWUSR);
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Debug enabled or not");
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds