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 210 211
	int i;
	int result;

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 234 235
		for (i = 0; i < header->oh_len; i++)
			 tty_insert_flip_char(port->port.tty,
					data[OMNINET_DATAOFFSET + i], 0);
		tty_flip_buffer_push(port->port.tty);
L
Linus Torvalds 已提交
236 237 238
	}

	/* Continue trying to always read  */
A
Alan Cox 已提交
239 240 241 242 243
	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 已提交
244 245
	result = usb_submit_urb(urb, GFP_ATOMIC);
	if (result)
A
Alan Cox 已提交
246 247
		err("%s - failed resubmitting read urb, error %d",
						__func__, result);
L
Linus Torvalds 已提交
248 249 250 251

	return;
}

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

A
Alan Cox 已提交
258 259 260
	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 已提交
261 262 263

	int			result;

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

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

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

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

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

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

	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);
298
	if (result) {
299
		wport->write_urb_busy = 0;
A
Alan Cox 已提交
300 301
		err("%s - failed submitting write urb, error %d",
							__func__, result);
302
	} else
L
Linus Torvalds 已提交
303 304 305 306 307 308
		result = count;

	return result;
}


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

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

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

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

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

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

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

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

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


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

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


A
Alan Cox 已提交
357
static int __init omninet_init(void)
L
Linus Torvalds 已提交
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
{
	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 已提交
375
static void __exit omninet_exit(void)
L
Linus Torvalds 已提交
376
{
A
Alan Cox 已提交
377 378
	usb_deregister(&omninet_driver);
	usb_serial_deregister(&zyxel_omninet_device);
L
Linus Torvalds 已提交
379 380 381 382 383 384
}


module_init(omninet_init);
module_exit(omninet_exit);

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

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