omninet.c revision 90ab5ee94171b3e28de6bb42ee30b527014e0be7
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * USB ZyXEL omni.net LCD PLUS driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
44d0dce3e0b794942407391c52f8dd2760802f391Greg Kroah-Hartman *	This program is free software; you can redistribute it and/or
54d0dce3e0b794942407391c52f8dd2760802f391Greg Kroah-Hartman *	modify it under the terms of the GNU General Public License version
64d0dce3e0b794942407391c52f8dd2760802f391Greg Kroah-Hartman *	2 as published by the Free Software Foundation.
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox * See Documentation/usb/usb-serial.txt for more information on using this
9f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox * driver
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Please report both successes and troubles to the author at omninet@kroah.com
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_driver.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
22f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox#include <linux/uaccess.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/usb.h>
24a969888ce91673c7f4b86520d851a6f0d5a5fa7dGreg Kroah-Hartman#include <linux/usb/serial.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2690ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug;
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Version Information
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_VERSION "v1.1"
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_AUTHOR "Alessandro Zummo"
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC "USB ZyXEL omni.net LCD PLUS Driver"
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ZYXEL_VENDOR_ID		0x0586
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ZYXEL_OMNINET_ID	0x1000
37f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox/* This one seems to be a re-branded ZyXEL device */
38f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox#define BT_IGNITIONPRO_ID	0x2000
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* function prototypes */
41a509a7e478e4766114d69f12d19d644ac63e9765Alan Coxstatic int  omninet_open(struct tty_struct *tty, struct usb_serial_port *port);
42335f8514f200e63d689113d29cb7253a5c282967Alan Coxstatic void omninet_close(struct usb_serial_port *port);
43f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic void omninet_read_bulk_callback(struct urb *urb);
44f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic void omninet_write_bulk_callback(struct urb *urb);
45f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int  omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
46f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox				const unsigned char *buf, int count);
47f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int  omninet_write_room(struct tty_struct *tty);
48f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void omninet_disconnect(struct usb_serial *serial);
49f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void omninet_release(struct usb_serial *serial);
50f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int omninet_attach(struct usb_serial *serial);
51f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox
527d40d7e85a25e01948bcb4dc3eda1355af318337Németh Mártonstatic const struct usb_device_id id_table[] = {
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ }						/* Terminating entry */
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan CoxMODULE_DEVICE_TABLE(usb, id_table);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct usb_driver omninet_driver = {
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"omninet",
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	usb_serial_probe,
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.disconnect =	usb_serial_disconnect,
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	id_table,
65ba9dc657af86d05d2971633e57d1f6f94ed60472Greg Kroah-Hartman	.no_dynamic_id = 	1,
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
69ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver zyxel_omninet_device = {
7018fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	.driver = {
7118fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman		.owner =	THIS_MODULE,
72269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman		.name =		"omninet",
7318fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	},
74269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman	.description =		"ZyXEL - omni.net lcd plus usb",
75d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl	.usb_driver =		&omninet_driver,
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =		id_table,
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_ports =		1,
785ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	.attach =		omninet_attach,
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			omninet_open,
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		omninet_close,
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		omninet_write,
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		omninet_write_room,
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_bulk_callback =	omninet_read_bulk_callback,
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_bulk_callback =	omninet_write_bulk_callback,
85f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	.disconnect =		omninet_disconnect,
86f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	.release =		omninet_release,
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The protocol.
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The omni.net always exchange 64 bytes of data with the host. The first
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * four bytes are the control header, you can see it in the above structure.
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_seq is a sequence number. Don't know if/how it's used.
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_len is the length of the data bytes in the packet.
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_xxx Bit-mapped, related to handshaking and status info.
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	I normally set it to 0x03 in trasmitted frames.
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	7: Active when the TA is in a CONNECTed state.
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	6: unknown
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	5: handshaking, unknown
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	4: handshaking, unknown
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	3: unknown, usually 0
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	2: unknown, usually 0
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	1: handshaking, unknown, usually set to 1 in trasmitted frames
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	0: handshaking, unknown, usually set to 1 in trasmitted frames
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_pad Probably a pad byte.
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * After the header you will find data bytes if oh_len was greater than zero.
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
113f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstruct omninet_header {
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_seq;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_len;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_xxx;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_pad;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
120f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstruct omninet_data {
121f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	__u8	od_outseq;	/* Sequence number for bulk_out URBs */
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
124f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int omninet_attach(struct usb_serial *serial)
1255ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum{
1265ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	struct omninet_data *od;
1275ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	struct usb_serial_port *port = serial->port[0];
1285ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum
129f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL);
130f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	if (!od) {
131194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n",
132194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, sizeof(struct omninet_data));
1335ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum		return -ENOMEM;
1345ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	}
1355ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	usb_set_serial_port_data(port, od);
1365ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	return 0;
1375ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum}
1385ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum
139a509a7e478e4766114d69f12d19d644ac63e9765Alan Coxstatic int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial	*serial = port->serial;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port	*wport;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			result = 0;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
145441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport = serial->port[1];
1484a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox	tty_port_tty_set(&wport->port, tty);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Start reading from the device */
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
152f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	if (result)
153194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev,
154194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			"%s - failed submitting read urb, error %d\n",
155194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, result);
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
159335f8514f200e63d689113d29cb7253a5c282967Alan Coxstatic void omninet_close(struct usb_serial_port *port)
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
161441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(port->read_urb);
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_DATAOFFSET	0x04
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_HEADERLEN	sizeof(struct omninet_header)
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_BULKOUTSIZE 	(64 - OMNINET_HEADERLEN)
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
170f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic void omninet_read_bulk_callback(struct urb *urb)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
172cdc97792289179974af6dda781c855696358d307Ming Lei	struct usb_serial_port 	*port 	= urb->context;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char 		*data 	= urb->transfer_buffer;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct omninet_header 	*header = (struct omninet_header *) &data[0];
175fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	int status = urb->status;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
1774944d40054d73b2f8d7d546f55c206eb3baf2710Alan Cox	int i;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
179441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	if (status) {
182fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman		dbg("%s - nonzero read bulk status received: %d",
183441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		    __func__, status);
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
187f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	if (debug && header->oh_xxx != 0x30) {
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (urb->actual_length) {
189f45ba776da4fe6c9a9eddd42b0fd5d1f15c260f3Joe Perches			printk(KERN_DEBUG "%s: omninet_read %d: ",
190f45ba776da4fe6c9a9eddd42b0fd5d1f15c260f3Joe Perches			       __FILE__, header->oh_len);
191f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox			for (i = 0; i < (header->oh_len +
192f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox						OMNINET_HEADERLEN); i++)
193f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox				printk("%.2x ", data[i]);
194f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox			printk("\n");
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (urb->actual_length && header->oh_len) {
1994a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		struct tty_struct *tty = tty_port_tty_get(&port->port);
2004a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET,
2014a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox							header->oh_len);
2024a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		tty_flip_buffer_push(tty);
2034a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		tty_kref_put(tty);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Continue trying to always read  */
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(urb, GFP_ATOMIC);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result)
209194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev,
210194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			"%s - failed resubmitting read urb, error %d\n",
211194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, result);
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21495da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
21595da310e66ee8090119596c70ca8432e57f9a97fAlan Cox					const unsigned char *buf, int count)
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
217f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct usb_serial *serial = port->serial;
218f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct usb_serial_port *wport = serial->port[1];
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
220f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct omninet_data *od = usb_get_serial_port_data(port);
221f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct omninet_header *header = (struct omninet_header *)
222f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox					wport->write_urb->transfer_buffer;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			result;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
226441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count == 0) {
229441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - write request of 0 bytes", __func__);
230f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox		return 0;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
232507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman
233120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold	if (!test_and_clear_bit(0, &port->write_urbs_free)) {
234441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - already writing", __func__);
235507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		return 0;
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
240f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET,
241f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox								buf, count);
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
243f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	usb_serial_debug_data(debug, &port->dev, __func__, count,
244f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox					wport->write_urb->transfer_buffer);
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_seq 	= od->od_outseq++;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_len 	= count;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_xxx  = 0x03;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_pad 	= 0x00;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send the data out the bulk port, always 64 bytes */
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport->write_urb->transfer_buffer_length = 64;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
255507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	if (result) {
256120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold		set_bit(0, &wport->write_urbs_free);
257194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev,
258194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			"%s - failed submitting write urb, error %d\n",
259194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, result);
260507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	} else
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		result = count;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
267f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int omninet_write_room(struct tty_struct *tty)
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
26995da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial 	*serial = port->serial;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port 	*wport 	= serial->port[1];
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
273a5b6f60c5a30c494017c7a2d11c4067f90d3d0dfAlan Cox	int room = 0; /* Default: no room */
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
275120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold	if (test_bit(0, &wport->write_urbs_free))
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		room = wport->bulk_out_size - OMNINET_HEADERLEN;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
278441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - returns %d", __func__, room);
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
280f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	return room;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
283f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic void omninet_write_bulk_callback(struct urb *urb)
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
285f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox/*	struct omninet_header	*header = (struct omninet_header  *)
286f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox						urb->transfer_buffer; */
287cdc97792289179974af6dda781c855696358d307Ming Lei	struct usb_serial_port 	*port   =  urb->context;
288fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	int status = urb->status;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
290759f3634267a67ac90f3fa7fc06510dfd43b4e45Joe Perches	dbg("%s - port %0x", __func__, port->number);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
292120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold	set_bit(0, &port->write_urbs_free);
293fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	if (status) {
294fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman		dbg("%s - nonzero write bulk status received: %d",
295441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		    __func__, status);
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
299cf2c7481d2ff7f0c266de873b2fe93883e9782f9Pete Zaitcev	usb_serial_port_softint(port);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
303f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void omninet_disconnect(struct usb_serial *serial)
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3055ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	struct usb_serial_port *wport = serial->port[1];
306f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
307f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	dbg("%s", __func__);
3085ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum
3095ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	usb_kill_urb(wport->write_urb);
310f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern}
311f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
312f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
313f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void omninet_release(struct usb_serial *serial)
314f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern{
315f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	struct usb_serial_port *port = serial->port[0];
316f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
317f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	dbg("%s", __func__);
318f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
3195ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	kfree(usb_get_serial_port_data(port));
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
323f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int __init omninet_init(void)
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int retval;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_serial_register(&zyxel_omninet_device);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_serial_register;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = usb_register(&omninet_driver);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed_usb_register;
332c197a8db59daf06dc5e77acd5a9681329cb22458Greg Kroah-Hartman	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
333c197a8db59daf06dc5e77acd5a9681329cb22458Greg Kroah-Hartman	       DRIVER_DESC "\n");
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_register:
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_serial_deregister(&zyxel_omninet_device);
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfailed_usb_serial_register:
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
342f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic void __exit omninet_exit(void)
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
344f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	usb_deregister(&omninet_driver);
345f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	usb_serial_deregister(&zyxel_omninet_device);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(omninet_init);
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(omninet_exit);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
352f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
353f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan CoxMODULE_DESCRIPTION(DRIVER_DESC);
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, bool, S_IRUGO | S_IWUSR);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Debug enabled or not");
358