omninet.c 10.3 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
	od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL);
	if (!od) {
157 158
		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n",
			__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
	tty_port_tty_set(&wport->port, tty);
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
	if (result)
186 187 188
		dev_err(&port->dev,
			"%s - failed submitting read urb, error %d\n",
			__func__, result);
L
Linus Torvalds 已提交
189 190 191
	return result;
}

A
Alan Cox 已提交
192
static void omninet_close(struct tty_struct *tty,
A
Alan Cox 已提交
193
			struct usb_serial_port *port, struct file *filp)
L
Linus Torvalds 已提交
194
{
195
	dbg("%s - port %d", __func__, port->number);
L
Linus Torvalds 已提交
196 197 198 199 200 201 202 203
	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 已提交
204
static void omninet_read_bulk_callback(struct urb *urb)
L
Linus Torvalds 已提交
205
{
206
	struct usb_serial_port 	*port 	= urb->context;
L
Linus Torvalds 已提交
207 208
	unsigned char 		*data 	= urb->transfer_buffer;
	struct omninet_header 	*header = (struct omninet_header *) &data[0];
209
	int status = urb->status;
L
Linus Torvalds 已提交
210
	int result;
A
Alan Cox 已提交
211
	int i;
L
Linus Torvalds 已提交
212

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

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

A
Alan Cox 已提交
221
	if (debug && header->oh_xxx != 0x30) {
L
Linus Torvalds 已提交
222
		if (urb->actual_length) {
A
Alan Cox 已提交
223 224 225 226 227 228
			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 已提交
229 230 231 232
		}
	}

	if (urb->actual_length && header->oh_len) {
A
Alan Cox 已提交
233 234 235 236 237
		struct tty_struct *tty = tty_port_tty_get(&port->port);
		tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET,
							header->oh_len);
		tty_flip_buffer_push(tty);
		tty_kref_put(tty);
L
Linus Torvalds 已提交
238 239 240
	}

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

	return;
}

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

A
Alan Cox 已提交
261 262 263
	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 已提交
264 265 266

	int			result;

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

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

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

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

A
Alan Cox 已提交
285 286
	memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET,
								buf, count);
L
Linus Torvalds 已提交
287

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

	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);
301
	if (result) {
302
		wport->write_urb_busy = 0;
303 304 305
		dev_err(&port->dev,
			"%s - failed submitting write urb, error %d\n",
			__func__, result);
306
	} else
L
Linus Torvalds 已提交
307 308 309 310 311 312
		result = count;

	return result;
}


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

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

321
	/* FIXME: no consistent locking for write_urb_busy */
322
	if (wport->write_urb_busy)
L
Linus Torvalds 已提交
323 324
		room = wport->bulk_out_size - OMNINET_HEADERLEN;

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

A
Alan Cox 已提交
327
	return room;
L
Linus Torvalds 已提交
328 329
}

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

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

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

346
	usb_serial_port_softint(port);
L
Linus Torvalds 已提交
347 348 349
}


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

	usb_kill_urb(wport->write_urb);
	kfree(usb_get_serial_port_data(port));
L
Linus Torvalds 已提交
358 359 360
}


A
Alan Cox 已提交
361
static int __init omninet_init(void)
L
Linus Torvalds 已提交
362 363 364 365 366 367 368 369
{
	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;
370 371
	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
	       DRIVER_DESC "\n");
L
Linus Torvalds 已提交
372 373 374 375 376 377 378 379
	return 0;
failed_usb_register:
	usb_serial_deregister(&zyxel_omninet_device);
failed_usb_serial_register:
	return retval;
}


A
Alan Cox 已提交
380
static void __exit omninet_exit(void)
L
Linus Torvalds 已提交
381
{
A
Alan Cox 已提交
382 383
	usb_deregister(&omninet_driver);
	usb_serial_deregister(&zyxel_omninet_device);
L
Linus Torvalds 已提交
384 385 386 387 388 389
}


module_init(omninet_init);
module_exit(omninet_exit);

A
Alan Cox 已提交
390 391
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
L
Linus Torvalds 已提交
392 393 394 395
MODULE_LICENSE("GPL");

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