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,
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
68ea65370d025f5005649e5cb37c4d025e92c6fc38Greg Kroah-Hartmanstatic struct usb_serial_driver zyxel_omninet_device = {
6918fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	.driver = {
7018fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman		.owner =	THIS_MODULE,
71269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman		.name =		"omninet",
7218fcac353fdc7cd072b0d24c8667042e675a4c11Greg Kroah-Hartman	},
73269bda1c123c7caf88e1deb2264f9086f0344192Greg Kroah-Hartman	.description =		"ZyXEL - omni.net lcd plus usb",
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =		id_table,
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.num_ports =		1,
765ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	.attach =		omninet_attach,
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open =			omninet_open,
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close =		omninet_close,
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write =		omninet_write,
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room =		omninet_write_room,
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_bulk_callback =	omninet_read_bulk_callback,
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_bulk_callback =	omninet_write_bulk_callback,
83f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	.disconnect =		omninet_disconnect,
84f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	.release =		omninet_release,
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
87f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Sternstatic struct usb_serial_driver * const serial_drivers[] = {
88f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Stern	&zyxel_omninet_device, NULL
89f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Stern};
90f667ddad41e303ebc2c6d5bf3105dffe2fbdd717Alan Stern
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The protocol.
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The omni.net always exchange 64 bytes of data with the host. The first
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * four bytes are the control header, you can see it in the above structure.
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_seq is a sequence number. Don't know if/how it's used.
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_len is the length of the data bytes in the packet.
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_xxx Bit-mapped, related to handshaking and status info.
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	I normally set it to 0x03 in trasmitted frames.
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	7: Active when the TA is in a CONNECTed state.
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	6: unknown
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	5: handshaking, unknown
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	4: handshaking, unknown
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	3: unknown, usually 0
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	2: unknown, usually 0
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	1: handshaking, unknown, usually set to 1 in trasmitted frames
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	0: handshaking, unknown, usually set to 1 in trasmitted frames
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * oh_pad Probably a pad byte.
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * After the header you will find data bytes if oh_len was greater than zero.
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
115f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstruct omninet_header {
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_seq;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_len;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_xxx;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u8	oh_pad;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
122f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstruct omninet_data {
123f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	__u8	od_outseq;	/* Sequence number for bulk_out URBs */
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
126f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int omninet_attach(struct usb_serial *serial)
1275ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum{
1285ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	struct omninet_data *od;
1295ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	struct usb_serial_port *port = serial->port[0];
1305ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum
131f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL);
132f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	if (!od) {
133194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n",
134194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, sizeof(struct omninet_data));
1355ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum		return -ENOMEM;
1365ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	}
1375ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	usb_set_serial_port_data(port, od);
1385ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	return 0;
1395ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum}
1405ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum
141a509a7e478e4766114d69f12d19d644ac63e9765Alan Coxstatic int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial	*serial = port->serial;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port	*wport;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			result = 0;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
147441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport = serial->port[1];
1504a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox	tty_port_tty_set(&wport->port, tty);
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Start reading from the device */
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
154f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	if (result)
155194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev,
156194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			"%s - failed submitting read urb, error %d\n",
157194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, result);
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161335f8514f200e63d689113d29cb7253a5c282967Alan Coxstatic void omninet_close(struct usb_serial_port *port)
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
163441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	usb_kill_urb(port->read_urb);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_DATAOFFSET	0x04
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_HEADERLEN	sizeof(struct omninet_header)
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define OMNINET_BULKOUTSIZE 	(64 - OMNINET_HEADERLEN)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
172f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic void omninet_read_bulk_callback(struct urb *urb)
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
174cdc97792289179974af6dda781c855696358d307Ming Lei	struct usb_serial_port 	*port 	= urb->context;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char 		*data 	= urb->transfer_buffer;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct omninet_header 	*header = (struct omninet_header *) &data[0];
177fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	int status = urb->status;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result;
1794944d40054d73b2f8d7d546f55c206eb3baf2710Alan Cox	int i;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
183fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	if (status) {
184fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman		dbg("%s - nonzero read bulk status received: %d",
185441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		    __func__, status);
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
189f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	if (debug && header->oh_xxx != 0x30) {
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (urb->actual_length) {
191f45ba776da4fe6c9a9eddd42b0fd5d1f15c260f3Joe Perches			printk(KERN_DEBUG "%s: omninet_read %d: ",
192f45ba776da4fe6c9a9eddd42b0fd5d1f15c260f3Joe Perches			       __FILE__, header->oh_len);
193f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox			for (i = 0; i < (header->oh_len +
194f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox						OMNINET_HEADERLEN); i++)
195f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox				printk("%.2x ", data[i]);
196f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox			printk("\n");
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (urb->actual_length && header->oh_len) {
2014a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		struct tty_struct *tty = tty_port_tty_get(&port->port);
2024a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET,
2034a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox							header->oh_len);
2044a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		tty_flip_buffer_push(tty);
2054a90f09b20f4622dcbff1f0e1e6bae1704f8ad8cAlan Cox		tty_kref_put(tty);
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Continue trying to always read  */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(urb, GFP_ATOMIC);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result)
211194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman		dev_err(&port->dev,
212194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			"%s - failed resubmitting read urb, error %d\n",
213194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, result);
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21695da310e66ee8090119596c70ca8432e57f9a97fAlan Coxstatic int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
21795da310e66ee8090119596c70ca8432e57f9a97fAlan Cox					const unsigned char *buf, int count)
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
219f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct usb_serial *serial = port->serial;
220f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct usb_serial_port *wport = serial->port[1];
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
222f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct omninet_data *od = usb_get_serial_port_data(port);
223f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	struct omninet_header *header = (struct omninet_header *)
224f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox					wport->write_urb->transfer_buffer;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			result;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
228441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - port %d", __func__, port->number);
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count == 0) {
231441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - write request of 0 bytes", __func__);
232f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox		return 0;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
234507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman
235120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold	if (!test_and_clear_bit(0, &port->write_urbs_free)) {
236441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		dbg("%s - already writing", __func__);
237507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman		return 0;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
242f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET,
243f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox								buf, count);
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
245f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	usb_serial_debug_data(debug, &port->dev, __func__, count,
246f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox					wport->write_urb->transfer_buffer);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_seq 	= od->od_outseq++;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_len 	= count;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_xxx  = 0x03;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	header->oh_pad 	= 0x00;
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send the data out the bulk port, always 64 bytes */
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wport->write_urb->transfer_buffer_length = 64;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
257507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	if (result) {
258120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold		set_bit(0, &wport->write_urbs_free);
25922a416c4e0f2179b57028e084ac0ed2c110333bdJohan Hovold		dev_err_console(port,
260194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			"%s - failed submitting write urb, error %d\n",
261194343d9364ea07c9f27c4505380a15a905e8a24Greg Kroah-Hartman			__func__, result);
262507ca9bc0476662f3463888d583864834eab1e11Greg Kroah-Hartman	} else
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		result = count;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
269f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic int omninet_write_room(struct tty_struct *tty)
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
27195da310e66ee8090119596c70ca8432e57f9a97fAlan Cox	struct usb_serial_port *port = tty->driver_data;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial 	*serial = port->serial;
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct usb_serial_port 	*wport 	= serial->port[1];
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
275a5b6f60c5a30c494017c7a2d11c4067f90d3d0dfAlan Cox	int room = 0; /* Default: no room */
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
277120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold	if (test_bit(0, &wport->write_urbs_free))
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		room = wport->bulk_out_size - OMNINET_HEADERLEN;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
280441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison	dbg("%s - returns %d", __func__, room);
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
282f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	return room;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
285f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Coxstatic void omninet_write_bulk_callback(struct urb *urb)
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
287f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox/*	struct omninet_header	*header = (struct omninet_header  *)
288f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox						urb->transfer_buffer; */
289cdc97792289179974af6dda781c855696358d307Ming Lei	struct usb_serial_port 	*port   =  urb->context;
290fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	int status = urb->status;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
292759f3634267a67ac90f3fa7fc06510dfd43b4e45Joe Perches	dbg("%s - port %0x", __func__, port->number);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
294120f9dbc968c67f9448a4be535dfff6edc3ce711Johan Hovold	set_bit(0, &port->write_urbs_free);
295fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman	if (status) {
296fdc2deb3892e802e916d1df7b1587aa0dbf3b271Greg Kroah-Hartman		dbg("%s - nonzero write bulk status received: %d",
297441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison		    __func__, status);
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301cf2c7481d2ff7f0c266de873b2fe93883e9782f9Pete Zaitcev	usb_serial_port_softint(port);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
305f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void omninet_disconnect(struct usb_serial *serial)
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3075ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	struct usb_serial_port *wport = serial->port[1];
308f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
309f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan Cox	dbg("%s", __func__);
3105ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum
3115ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	usb_kill_urb(wport->write_urb);
312f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern}
313f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
314f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
315f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Sternstatic void omninet_release(struct usb_serial *serial)
316f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern{
317f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	struct usb_serial_port *port = serial->port[0];
318f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
319f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern	dbg("%s", __func__);
320f9c99bb8b3a1ec81af68d484a551307326c2e933Alan Stern
3215ec1862e7b612d804ca10a0475dccf98c857efecOliver Neukum	kfree(usb_get_serial_port_data(port));
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3246384eaefd337b465417e9b64ebf74717703c0788Greg Kroah-Hartmanmodule_usb_serial_driver(omninet_driver, serial_drivers);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
326f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan CoxMODULE_AUTHOR(DRIVER_AUTHOR);
327f89d0dff2507b6bd486b7db59a5f6a733fbfaa12Alan CoxMODULE_DESCRIPTION(DRIVER_DESC);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, bool, S_IRUGO | S_IWUSR);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Debug enabled or not");
332