omninet.c revision ba9dc657af86d05d2971633e57d1f6f94ed60472
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB ZyXEL omni.net LCD PLUS driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	This program is free software; you can redistribute it and/or modify
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	it under the terms of the GNU General Public License as published by
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	the Free Software Foundation; either version 2 of the License, or
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	(at your option) any later version.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * See Documentation/usb/usb-serial.txt for more information on using this driver
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Please report both successes and troubles to the author at omninet@kroah.com
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (05/30/2001) gkh
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	switched from using spinlock to a semaphore, which fixes lots of problems.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (04/08/2001) gb
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Identify version on module load.
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (11/01/2000) Adam J. Richter
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	usb_device_id table support
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (10/05/2000) gkh
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Fixed bug with urb->dev not being set properly, now that the usb
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	core needs it.
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (08/28/2000) gkh
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Added locks for SMP safeness.
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Fixed MOD_INC and MOD_DEC logic and the ability to open a port more
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	than once.
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Fixed potential race in omninet_write_bulk_callback
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (07/19/2000) gkh
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Added module_init and module_exit functions to handle the fact that this
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	driver is a loadable module now.
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/config.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "usb-serial.h"
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int debug;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_VERSION "v1.1"
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR "Alessandro Zummo"
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB ZyXEL omni.net LCD PLUS Driver"
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ZYXEL_VENDOR_ID		0x0586
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ZYXEL_OMNINET_ID	0x1000
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define BT_IGNITIONPRO_ID	0x2000  /* This one seems to be a re-branded ZyXEL device */
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* function prototypes */
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  omninet_open		(struct usb_serial_port *port, struct file *filp);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_close		(struct usb_serial_port *port, struct file *filp);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_read_bulk_callback	(struct urb *urb, struct pt_regs *regs);
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_write_bulk_callback	(struct urb *urb, struct pt_regs *regs);
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  omninet_write		(struct usb_serial_port *port, const unsigned char *buf, int count);
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int  omninet_write_room		(struct usb_serial_port *port);
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_shutdown		(struct usb_serial *serial);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_device_id id_table [] = {
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }						/* Terminating entry */
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE (usb, id_table);
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver omninet_driver = {
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner =	THIS_MODULE,
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"omninet",
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	usb_serial_probe,
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	usb_serial_disconnect,
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	id_table,
88ba9dc657af86d05d2971633e57d1f6f94ed60472Greg Kroah-Hartman	.no_dynamic_id = 	1,
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
92ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver zyxel_omninet_device = {
9318fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	.driver = {
9418fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman		.owner =	THIS_MODULE,
95269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman		.name =		"omninet",
9618fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	},
97269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman	.description =		"ZyXEL - omni.net lcd plus usb",
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =		id_table,
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_interrupt_in =	1,
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_in =		1,
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_bulk_out =		2,
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_ports =		1,
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			omninet_open,
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		omninet_close,
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		omninet_write,
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		omninet_write_room,
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_bulk_callback =	omninet_read_bulk_callback,
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_bulk_callback =	omninet_write_bulk_callback,
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.shutdown =		omninet_shutdown,
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The protocol.
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The omni.net always exchange 64 bytes of data with the host. The first
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * four bytes are the control header, you can see it in the above structure.
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_seq is a sequence number. Don't know if/how it's used.
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_len is the length of the data bytes in the packet.
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_xxx Bit-mapped, related to handshaking and status info.
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	I normally set it to 0x03 in trasmitted frames.
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	7: Active when the TA is in a CONNECTed state.
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	6: unknown
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	5: handshaking, unknown
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	4: handshaking, unknown
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	3: unknown, usually 0
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	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
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 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.
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct omninet_header
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_seq;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_len;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_xxx;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_pad;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct omninet_data
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	od_outseq;	// Sequence number for bulk_out URBs
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int omninet_open (struct usb_serial_port *port, struct file *filp)
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial	*serial = port->serial;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port	*wport;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct omninet_data	*od;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			result = 0;
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL );
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if( !od ) {
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data));
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_set_serial_port_data(port, od);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport = serial->port[1];
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport->tty = port->tty;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Start reading from the device */
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_fill_bulk_urb(port->read_urb, serial->dev,
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      omninet_read_bulk_callback, port);
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result)
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_close (struct usb_serial_port *port, struct file * filp)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial 	*serial = port->serial;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port 	*wport;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg("%s - port %d", __FUNCTION__, port->number);
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport = serial->port[1];
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(wport->write_urb);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(port->read_urb);
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911bc3c9e1e44c2059fe2ffa6ff70ad0a925d7b05fJesper Juhl	kfree(usb_get_serial_port_data(port));
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_DATAOFFSET	0x04
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_HEADERLEN	sizeof(struct omninet_header)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_BULKOUTSIZE 	(64 - OMNINET_HEADERLEN)
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port 	*port 	= (struct usb_serial_port *)urb->context;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char 		*data 	= urb->transfer_buffer;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct omninet_header 	*header = (struct omninet_header *) &data[0];
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	dbg("omninet_read_bulk_callback");
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (urb->status) {
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	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]);
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk ("\n");
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	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,
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      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);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result)
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
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
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	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;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	dbg("omninet_write port %d", port->number);
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count == 0) {
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - write request of 0 bytes", __FUNCTION__);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (0);
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
260507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman
261507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	spin_lock(&port->lock);
262507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	if (port->write_urb_busy) {
263507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		spin_unlock(&port->lock);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - already writing", __FUNCTION__);
265507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		return 0;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
267507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	port->write_urb_busy = 1;
268507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	spin_unlock(&port->lock);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	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
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_seq 	= od->od_outseq++;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_len 	= count;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_xxx  = 0x03;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	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);
286507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	if (result) {
287507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		port->write_urb_busy = 0;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
289507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	} 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
303507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	if (wport->write_urb_busy)
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		room = wport->bulk_out_size - OMNINET_HEADERLEN;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	dbg("omninet_write_room returns %d", room);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (room);
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
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
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	dbg("omninet_write_bulk_callback, port %0x\n", port);
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
318507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	port->write_urb_busy = 0;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (urb->status) {
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	schedule_work(&port->work);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	dbg("omninet_write_bulk_callback, tty %0x\n", tty);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void omninet_shutdown (struct usb_serial *serial)
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dbg ("%s", __FUNCTION__);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init omninet_init (void)
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_serial_register(&zyxel_omninet_device);
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_serial_register;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&omninet_driver);
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_register;
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info(DRIVER_VERSION ":" DRIVER_DESC);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_register:
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister(&zyxel_omninet_device);
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_serial_register:
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit omninet_exit (void)
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_deregister (&omninet_driver);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister (&zyxel_omninet_device);
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(omninet_init);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(omninet_exit);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR( DRIVER_AUTHOR );
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION( DRIVER_DESC );
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, bool, S_IRUGO | S_IWUSR);
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Debug enabled or not");
370