omninet.c 10.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 * USB ZyXEL omni.net LCD PLUS driver
 *
4 5 6
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License version
 *	2 as published by the Free Software Foundation.
L
Linus Torvalds 已提交
7
 *
A
Alan Cox 已提交
8 9
 * See Documentation/usb/usb-serial.txt for more information on using this
 * driver
L
Linus Torvalds 已提交
10 11
 *
 * Please report both successes and troubles to the author at omninet@kroah.com
A
Alan Cox 已提交
12
 *
L
Linus Torvalds 已提交
13
 * (05/30/2001) gkh
A
Alan Cox 已提交
14 15
 *	switched from using spinlock to a semaphore, which fixes lots of
 *	problems.
L
Linus Torvalds 已提交
16 17 18 19 20 21
 *
 * (04/08/2001) gb
 *	Identify version on module load.
 *
 * (11/01/2000) Adam J. Richter
 *	usb_device_id table support
A
Alan Cox 已提交
22
 *
L
Linus Torvalds 已提交
23 24 25
 * (10/05/2000) gkh
 *	Fixed bug with urb->dev not being set properly, now that the usb
 *	core needs it.
A
Alan Cox 已提交
26
 *
L
Linus Torvalds 已提交
27 28
 * (08/28/2000) gkh
 *	Added locks for SMP safeness.
A
Alan Cox 已提交
29
 *	Fixed MOD_INC and MOD_DEC logic and the ability to open a port more
L
Linus Torvalds 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
 *	than once.
 *	Fixed potential race in omninet_write_bulk_callback
 *
 * (07/19/2000) gkh
 *	Added module_init and module_exit functions to handle the fact that this
 *	driver is a loadable module now.
 *
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
A
Alan Cox 已提交
48
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
49
#include <linux/usb.h>
50
#include <linux/usb/serial.h>
L
Linus Torvalds 已提交
51 52 53 54 55 56 57 58 59 60 61 62

static int debug;

/*
 * Version Information
 */
#define DRIVER_VERSION "v1.1"
#define DRIVER_AUTHOR "Alessandro Zummo"
#define DRIVER_DESC "USB ZyXEL omni.net LCD PLUS Driver"

#define ZYXEL_VENDOR_ID		0x0586
#define ZYXEL_OMNINET_ID	0x1000
A
Alan Cox 已提交
63 64
/* This one seems to be a re-branded ZyXEL device */
#define BT_IGNITIONPRO_ID	0x2000
L
Linus Torvalds 已提交
65 66

/* function prototypes */
A
Alan Cox 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79
static int  omninet_open(struct tty_struct *tty, struct usb_serial_port *port,
							struct file *filp);
static void omninet_close(struct tty_struct *tty, struct usb_serial_port *port,
							struct file *filp);
static void omninet_read_bulk_callback(struct urb *urb);
static void omninet_write_bulk_callback(struct urb *urb);
static int  omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
				const unsigned char *buf, int count);
static int  omninet_write_room(struct tty_struct *tty);
static void omninet_shutdown(struct usb_serial *serial);
static int omninet_attach(struct usb_serial *serial);

static struct usb_device_id id_table[] = {
L
Linus Torvalds 已提交
80 81 82 83 84
	{ USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
	{ USB_DEVICE(ZYXEL_VENDOR_ID, BT_IGNITIONPRO_ID) },
	{ }						/* Terminating entry */
};

A
Alan Cox 已提交
85
MODULE_DEVICE_TABLE(usb, id_table);
L
Linus Torvalds 已提交
86 87 88 89 90 91

static struct usb_driver omninet_driver = {
	.name =		"omninet",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	id_table,
92
	.no_dynamic_id = 	1,
L
Linus Torvalds 已提交
93 94 95
};


96
static struct usb_serial_driver zyxel_omninet_device = {
97 98
	.driver = {
		.owner =	THIS_MODULE,
99
		.name =		"omninet",
100
	},
101
	.description =		"ZyXEL - omni.net lcd plus usb",
102
	.usb_driver =		&omninet_driver,
L
Linus Torvalds 已提交
103 104
	.id_table =		id_table,
	.num_ports =		1,
105
	.attach =		omninet_attach,
L
Linus Torvalds 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
	.open =			omninet_open,
	.close =		omninet_close,
	.write =		omninet_write,
	.write_room =		omninet_write_room,
	.read_bulk_callback =	omninet_read_bulk_callback,
	.write_bulk_callback =	omninet_write_bulk_callback,
	.shutdown =		omninet_shutdown,
};


/* The protocol.
 *
 * The omni.net always exchange 64 bytes of data with the host. The first
 * four bytes are the control header, you can see it in the above structure.
 *
 * oh_seq is a sequence number. Don't know if/how it's used.
 * oh_len is the length of the data bytes in the packet.
 * oh_xxx Bit-mapped, related to handshaking and status info.
 *	I normally set it to 0x03 in trasmitted frames.
 *	7: Active when the TA is in a CONNECTed state.
 *	6: unknown
 *	5: handshaking, unknown
 *	4: handshaking, unknown
 *	3: unknown, usually 0
 *	2: unknown, usually 0
 *	1: handshaking, unknown, usually set to 1 in trasmitted frames
 *	0: handshaking, unknown, usually set to 1 in trasmitted frames
 * oh_pad Probably a pad byte.
 *
 * After the header you will find data bytes if oh_len was greater than zero.
 *
 */

A
Alan Cox 已提交
139
struct omninet_header {
L
Linus Torvalds 已提交
140 141 142 143 144 145
	__u8	oh_seq;
	__u8	oh_len;
	__u8	oh_xxx;
	__u8	oh_pad;
};

A
Alan Cox 已提交
146 147
struct omninet_data {
	__u8	od_outseq;	/* Sequence number for bulk_out URBs */
L
Linus Torvalds 已提交
148 149
};

A
Alan Cox 已提交
150
static int omninet_attach(struct usb_serial *serial)
151 152 153 154
{
	struct omninet_data *od;
	struct usb_serial_port *port = serial->port[0];

A
Alan Cox 已提交
155 156 157 158
	od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL);
	if (!od) {
		err("%s- kmalloc(%Zd) failed.",
				__func__, sizeof(struct omninet_data));
159 160 161 162 163 164
		return -ENOMEM;
	}
	usb_set_serial_port_data(port, od);
	return 0;
}

A
Alan Cox 已提交
165 166
static int omninet_open(struct tty_struct *tty,
			struct usb_serial_port *port, struct file *filp)
L
Linus Torvalds 已提交
167 168 169 170 171
{
	struct usb_serial	*serial = port->serial;
	struct usb_serial_port	*wport;
	int			result = 0;

172
	dbg("%s - port %d", __func__, port->number);
L
Linus Torvalds 已提交
173 174

	wport = serial->port[1];
A
Alan Cox 已提交
175
	wport->port.tty = tty;		/* FIXME */
L
Linus Torvalds 已提交
176 177

	/* Start reading from the device */
A
Alan Cox 已提交
178 179 180 181 182 183
	usb_fill_bulk_urb(port->read_urb, serial->dev,
			usb_rcvbulkpipe(serial->dev,
				port->bulk_in_endpointAddress),
			port->read_urb->transfer_buffer,
			port->read_urb->transfer_buffer_length,
			omninet_read_bulk_callback, port);
L
Linus Torvalds 已提交
184
	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
A
Alan Cox 已提交
185 186 187
	if (result)
		err("%s - failed submitting read urb, error %d",
							__func__, result);
L
Linus Torvalds 已提交
188 189 190
	return result;
}

A
Alan Cox 已提交
191
static void omninet_close(struct tty_struct *tty,
A
Alan Cox 已提交
192
			struct usb_serial_port *port, struct file *filp)
L
Linus Torvalds 已提交
193
{
194
	dbg("%s - port %d", __func__, port->number);
L
Linus Torvalds 已提交
195 196 197 198 199 200 201 202
	usb_kill_urb(port->read_urb);
}


#define OMNINET_DATAOFFSET	0x04
#define OMNINET_HEADERLEN	sizeof(struct omninet_header)
#define OMNINET_BULKOUTSIZE 	(64 - OMNINET_HEADERLEN)

A
Alan Cox 已提交
203
static void omninet_read_bulk_callback(struct urb *urb)
L
Linus Torvalds 已提交
204
{
205
	struct usb_serial_port 	*port 	= urb->context;
L
Linus Torvalds 已提交
206 207
	unsigned char 		*data 	= urb->transfer_buffer;
	struct omninet_header 	*header = (struct omninet_header *) &data[0];
208
	int status = urb->status;
L
Linus Torvalds 已提交
209
	int result;
A
Alan Cox 已提交
210
	int i;
L
Linus Torvalds 已提交
211

212
	dbg("%s - port %d", __func__, port->number);
L
Linus Torvalds 已提交
213

214 215
	if (status) {
		dbg("%s - nonzero read bulk status received: %d",
216
		    __func__, status);
L
Linus Torvalds 已提交
217 218 219
		return;
	}

A
Alan Cox 已提交
220
	if (debug && header->oh_xxx != 0x30) {
L
Linus Torvalds 已提交
221
		if (urb->actual_length) {
A
Alan Cox 已提交
222 223 224 225 226 227
			printk(KERN_DEBUG __FILE__
					": omninet_read %d: ", header->oh_len);
			for (i = 0; i < (header->oh_len +
						OMNINET_HEADERLEN); i++)
				printk("%.2x ", data[i]);
			printk("\n");
L
Linus Torvalds 已提交
228 229 230 231
		}
	}

	if (urb->actual_length && header->oh_len) {
A
Alan Cox 已提交
232 233
		tty_insert_flip_string(port->port.tty,
			data + OMNINET_DATAOFFSET, header->oh_len);
A
Alan Cox 已提交
234
		tty_flip_buffer_push(port->port.tty);
L
Linus Torvalds 已提交
235 236 237
	}

	/* Continue trying to always read  */
A
Alan Cox 已提交
238 239 240 241 242
	usb_fill_bulk_urb(urb, port->serial->dev,
			usb_rcvbulkpipe(port->serial->dev,
					port->bulk_in_endpointAddress),
			urb->transfer_buffer, urb->transfer_buffer_length,
			omninet_read_bulk_callback, port);
L
Linus Torvalds 已提交
243 244
	result = usb_submit_urb(urb, GFP_ATOMIC);
	if (result)
A
Alan Cox 已提交
245 246
		err("%s - failed resubmitting read urb, error %d",
						__func__, result);
L
Linus Torvalds 已提交
247 248 249 250

	return;
}

A
Alan Cox 已提交
251 252
static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
					const unsigned char *buf, int count)
L
Linus Torvalds 已提交
253
{
A
Alan Cox 已提交
254 255
	struct usb_serial *serial = port->serial;
	struct usb_serial_port *wport = serial->port[1];
L
Linus Torvalds 已提交
256

A
Alan Cox 已提交
257 258 259
	struct omninet_data *od = usb_get_serial_port_data(port);
	struct omninet_header *header = (struct omninet_header *)
					wport->write_urb->transfer_buffer;
L
Linus Torvalds 已提交
260 261 262

	int			result;

263
	dbg("%s - port %d", __func__, port->number);
L
Linus Torvalds 已提交
264 265

	if (count == 0) {
266
		dbg("%s - write request of 0 bytes", __func__);
A
Alan Cox 已提交
267
		return 0;
L
Linus Torvalds 已提交
268
	}
269

270
	spin_lock_bh(&wport->lock);
271
	if (wport->write_urb_busy) {
272
		spin_unlock_bh(&wport->lock);
273
		dbg("%s - already writing", __func__);
274
		return 0;
L
Linus Torvalds 已提交
275
	}
276
	wport->write_urb_busy = 1;
277
	spin_unlock_bh(&wport->lock);
L
Linus Torvalds 已提交
278 279 280

	count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;

A
Alan Cox 已提交
281 282
	memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET,
								buf, count);
L
Linus Torvalds 已提交
283

A
Alan Cox 已提交
284 285
	usb_serial_debug_data(debug, &port->dev, __func__, count,
					wport->write_urb->transfer_buffer);
L
Linus Torvalds 已提交
286 287 288 289 290 291 292 293 294 295 296

	header->oh_seq 	= od->od_outseq++;
	header->oh_len 	= count;
	header->oh_xxx  = 0x03;
	header->oh_pad 	= 0x00;

	/* send the data out the bulk port, always 64 bytes */
	wport->write_urb->transfer_buffer_length = 64;

	wport->write_urb->dev = serial->dev;
	result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);
297
	if (result) {
298
		wport->write_urb_busy = 0;
A
Alan Cox 已提交
299 300
		err("%s - failed submitting write urb, error %d",
							__func__, result);
301
	} else
L
Linus Torvalds 已提交
302 303 304 305 306 307
		result = count;

	return result;
}


A
Alan Cox 已提交
308
static int omninet_write_room(struct tty_struct *tty)
L
Linus Torvalds 已提交
309
{
A
Alan Cox 已提交
310
	struct usb_serial_port *port = tty->driver_data;
L
Linus Torvalds 已提交
311 312 313
	struct usb_serial 	*serial = port->serial;
	struct usb_serial_port 	*wport 	= serial->port[1];

314
	int room = 0; /* Default: no room */
L
Linus Torvalds 已提交
315

316
	/* FIXME: no consistent locking for write_urb_busy */
317
	if (wport->write_urb_busy)
L
Linus Torvalds 已提交
318 319
		room = wport->bulk_out_size - OMNINET_HEADERLEN;

320
	dbg("%s - returns %d", __func__, room);
L
Linus Torvalds 已提交
321

A
Alan Cox 已提交
322
	return room;
L
Linus Torvalds 已提交
323 324
}

A
Alan Cox 已提交
325
static void omninet_write_bulk_callback(struct urb *urb)
L
Linus Torvalds 已提交
326
{
A
Alan Cox 已提交
327 328
/*	struct omninet_header	*header = (struct omninet_header  *)
						urb->transfer_buffer; */
329
	struct usb_serial_port 	*port   =  urb->context;
330
	int status = urb->status;
L
Linus Torvalds 已提交
331

332
	dbg("%s - port %0x\n", __func__, port->number);
L
Linus Torvalds 已提交
333

334
	port->write_urb_busy = 0;
335 336
	if (status) {
		dbg("%s - nonzero write bulk status received: %d",
337
		    __func__, status);
L
Linus Torvalds 已提交
338 339 340
		return;
	}

341
	usb_serial_port_softint(port);
L
Linus Torvalds 已提交
342 343 344
}


A
Alan Cox 已提交
345
static void omninet_shutdown(struct usb_serial *serial)
L
Linus Torvalds 已提交
346
{
347 348
	struct usb_serial_port *wport = serial->port[1];
	struct usb_serial_port *port = serial->port[0];
A
Alan Cox 已提交
349
	dbg("%s", __func__);
350 351 352

	usb_kill_urb(wport->write_urb);
	kfree(usb_get_serial_port_data(port));
L
Linus Torvalds 已提交
353 354 355
}


A
Alan Cox 已提交
356
static int __init omninet_init(void)
L
Linus Torvalds 已提交
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
{
	int retval;
	retval = usb_serial_register(&zyxel_omninet_device);
	if (retval)
		goto failed_usb_serial_register;
	retval = usb_register(&omninet_driver);
	if (retval)
		goto failed_usb_register;
	info(DRIVER_VERSION ":" DRIVER_DESC);
	return 0;
failed_usb_register:
	usb_serial_deregister(&zyxel_omninet_device);
failed_usb_serial_register:
	return retval;
}


A
Alan Cox 已提交
374
static void __exit omninet_exit(void)
L
Linus Torvalds 已提交
375
{
A
Alan Cox 已提交
376 377
	usb_deregister(&omninet_driver);
	usb_serial_deregister(&zyxel_omninet_device);
L
Linus Torvalds 已提交
378 379 380 381 382 383
}


module_init(omninet_init);
module_exit(omninet_exit);

A
Alan Cox 已提交
384 385
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
L
Linus Torvalds 已提交
386 387 388 389
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");