ipaq.c 14.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3
/*
 * USB Compaq iPAQ driver
 *
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
4
 *	Copyright (C) 2001 - 2002
Linus Torvalds's avatar
Linus Torvalds committed
5 6 7 8 9 10 11
 *	    Ganesh Varadarajan <ganesh@veritas.com>
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
12 13 14 15
 * (26/7/2002) ganesh
 * 	Fixed up broken error handling in ipaq_open. Retry the "kickstart"
 * 	packet much harder - this drastically reduces connection failures.
 *
16 17 18 19
 * (30/4/2002) ganesh
 * 	Added support for the Casio EM500. Completely untested. Thanks
 * 	to info from Nathan <wfilardo@fuse.net>
 *
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
20 21 22 23
 * (19/3/2002) ganesh
 *	Don't submit urbs while holding spinlocks. Not strictly necessary
 *	in 2.5.x.
 *
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
24 25 26 27 28 29 30 31 32 33
 * (8/3/2002) ganesh
 * 	The ipaq sometimes emits a '\0' before the CLIENT string. At this
 * 	point of time, the ppp ldisc is not yet attached to the tty, so
 * 	n_tty echoes "^ " to the ipaq, which messes up the chat. In 2.5.6-pre2
 * 	this causes a panic because echo_char() tries to sleep in interrupt
 * 	context.
 * 	The fix is to tell the upper layers that this is a raw device so that
 * 	echoing is suppressed. Thanks to Lyle Lindholm for a detailed bug
 * 	report.
 *
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
34 35 36
 * (25/2/2002) ganesh
 * 	Added support for the HP Jornada 548 and 568. Completely untested.
 * 	Thanks to info from Heath Robinson and Arieh Davidoff.
Linus Torvalds's avatar
Linus Torvalds committed
37 38 39 40 41 42 43 44 45 46 47 48
 */

#include <linux/config.h>
#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>
49
#include <asm/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52 53 54 55 56 57 58 59 60
#include <linux/usb.h>

#ifdef CONFIG_USB_SERIAL_DEBUG
	static int debug = 1;
#else
	static int debug = 0;
#endif

#include "usb-serial.h"
#include "ipaq.h"

61 62
#define KP_RETRIES	100

Linus Torvalds's avatar
Linus Torvalds committed
63 64 65
/*
 * Version Information
 */
66
#define DRIVER_VERSION "v0.3"
Linus Torvalds's avatar
Linus Torvalds committed
67
#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
68
#define DRIVER_DESC "USB Compaq iPAQ, HP Jornada, Casio EM500 driver"
Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72 73 74 75 76 77 78

/* Function prototypes for an ipaq */
static int  ipaq_open (struct usb_serial_port *port, struct file *filp);
static void ipaq_close (struct usb_serial_port *port, struct file *filp);
static int  ipaq_startup (struct usb_serial *serial);
static void ipaq_shutdown (struct usb_serial *serial);
static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
		       int count);
static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
			   int count);
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
79
static void ipaq_write_gather(struct usb_serial_port *port);
Linus Torvalds's avatar
Linus Torvalds committed
80 81 82 83 84 85 86
static void ipaq_read_bulk_callback (struct urb *urb);
static void ipaq_write_bulk_callback(struct urb *urb);
static int ipaq_write_room(struct usb_serial_port *port);
static int ipaq_chars_in_buffer(struct usb_serial_port *port);
static void ipaq_destroy_lists(struct usb_serial_port *port);


87
static struct usb_device_id ipaq_id_table [] = {
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
88 89 90
	{ USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_IPAQ_ID) },
	{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_548_ID) },
	{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_568_ID) },
91
	{ USB_DEVICE(CASIO_VENDOR_ID, CASIO_EM500_ID) },
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94 95 96
	{ }					/* Terminating entry */
};

MODULE_DEVICE_TABLE (usb, ipaq_id_table);

97 98 99 100 101 102 103 104
static struct usb_driver ipaq_driver = {
	.name =		"ipaq",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	ipaq_id_table,
};


Linus Torvalds's avatar
Linus Torvalds committed
105 106
/* All of the device info needed for the Compaq iPAQ */
struct usb_serial_device_type ipaq_device = {
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
	.owner =		THIS_MODULE,
	.name =			"Compaq iPAQ",
	.id_table =		ipaq_id_table,
	.num_interrupt_in =	NUM_DONT_CARE,
	.num_bulk_in =		1,
	.num_bulk_out =		1,
	.num_ports =		1,
	.open =			ipaq_open,
	.close =		ipaq_close,
	.attach =		ipaq_startup,
	.shutdown =		ipaq_shutdown,
	.write =		ipaq_write,
	.write_room =		ipaq_write_room,
	.chars_in_buffer =	ipaq_chars_in_buffer,
	.read_bulk_callback =	ipaq_read_bulk_callback,
	.write_bulk_callback =	ipaq_write_bulk_callback,
Linus Torvalds's avatar
Linus Torvalds committed
123 124 125 126 127 128 129 130 131 132 133 134
};

static spinlock_t	write_list_lock;
static int		bytes_in;
static int		bytes_out;

static int ipaq_open(struct usb_serial_port *port, struct file *filp)
{
	struct usb_serial	*serial = port->serial;
	struct ipaq_private	*priv;
	struct ipaq_packet	*pkt;
	int			i, result = 0;
135
	int			retries = KP_RETRIES;
Linus Torvalds's avatar
Linus Torvalds committed
136 137 138 139 140

	if (port_paranoia_check(port, __FUNCTION__)) {
		return -ENODEV;
	}
	
141
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
142

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
143 144 145 146
	bytes_in = 0;
	bytes_out = 0;
	priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);
	if (priv == NULL) {
147
		err("%s - Out of memory", __FUNCTION__);
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
148 149 150 151 152 153 154 155 156 157 158 159
		return -ENOMEM;
	}
	port->private = (void *)priv;
	priv->active = 0;
	priv->queue_len = 0;
	INIT_LIST_HEAD(&priv->queue);
	INIT_LIST_HEAD(&priv->freelist);

	for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) {
		pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL);
		if (pkt == NULL) {
			goto enomem;
Linus Torvalds's avatar
Linus Torvalds committed
160
		}
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
161 162 163 164
		pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL);
		if (pkt->data == NULL) {
			kfree(pkt);
			goto enomem;
Linus Torvalds's avatar
Linus Torvalds committed
165
		}
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
166 167 168 169 170 171
		pkt->len = 0;
		pkt->written = 0;
		INIT_LIST_HEAD(&pkt->list);
		list_add(&pkt->list, &priv->freelist);
		priv->free_len += PACKET_SIZE;
	}
Linus Torvalds's avatar
Linus Torvalds committed
172

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
173 174 175 176
	/*
	 * Force low latency on. This will immediately push data to the line
	 * discipline instead of queueing.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
177

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
178 179 180
	port->tty->low_latency = 1;
	port->tty->raw = 1;
	port->tty->real_raw = 1;
Linus Torvalds's avatar
Linus Torvalds committed
181

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
182 183 184
	/*
	 * Lose the small buffers usbserial provides. Make larger ones.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
185

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
186 187 188 189 190 191 192 193
	kfree(port->bulk_in_buffer);
	kfree(port->bulk_out_buffer);
	port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
	if (port->bulk_in_buffer == NULL) {
		goto enomem;
	}
	port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
	if (port->bulk_out_buffer == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
194
		kfree(port->bulk_in_buffer);
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
195 196 197 198 199 200 201 202
		goto enomem;
	}
	port->read_urb->transfer_buffer = port->bulk_in_buffer;
	port->write_urb->transfer_buffer = port->bulk_out_buffer;
	port->read_urb->transfer_buffer_length = URBDATA_SIZE;
	port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE;
	
	/* Start reading from the device */
203
	usb_fill_bulk_urb(port->read_urb, serial->dev, 
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
204 205 206 207 208
		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
		      ipaq_read_bulk_callback, port);
	result = usb_submit_urb(port->read_urb, GFP_KERNEL);
	if (result) {
209
		err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
210
		goto error;
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
211
	}
Linus Torvalds's avatar
Linus Torvalds committed
212

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
213
	/*
214 215 216 217 218
	 * Send out control message observed in win98 sniffs. Not sure what
	 * it does, but from empirical observations, it seems that the device
	 * will start the chat sequence once one of these messages gets
	 * through. Since this has a reasonably high failure rate, we retry
	 * several times.
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
219
	 */
Linus Torvalds's avatar
Linus Torvalds committed
220

221 222 223 224 225 226 227
	while (retries--) {
		result = usb_control_msg(serial->dev,
				usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
				0x1, 0, NULL, 0, HZ / 10 + 1);
		if (result == 0) {
			return 0;
		}
Linus Torvalds's avatar
Linus Torvalds committed
228
	}
229
	err("%s - failed doing control urb, error %d", __FUNCTION__, result);
230
	goto error;
Linus Torvalds's avatar
Linus Torvalds committed
231 232

enomem:
233
	result = -ENOMEM;
234
	err("%s - Out of memory", __FUNCTION__);
235
error:
Linus Torvalds's avatar
Linus Torvalds committed
236 237
	ipaq_destroy_lists(port);
	kfree(priv);
238
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
239 240 241 242 243 244 245 246 247 248 249 250
}


static void ipaq_close(struct usb_serial_port *port, struct file *filp)
{
	struct usb_serial	*serial;
	struct ipaq_private	*priv = port->private;

	if (port_paranoia_check(port, __FUNCTION__)) {
		return; 
	}
	
251
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
252 253 254 255 256
			 
	serial = get_usb_serial(port, __FUNCTION__);
	if (!serial)
		return;

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
257 258 259
	/*
	 * shut down bulk read and write
	 */
Linus Torvalds's avatar
Linus Torvalds committed
260

Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
261 262 263 264 265
	usb_unlink_urb(port->write_urb);
	usb_unlink_urb(port->read_urb);
	ipaq_destroy_lists(port);
	kfree(priv);
	port->private = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281

	/* Uncomment the following line if you want to see some statistics in your syslog */
	/* info ("Bytes In = %d  Bytes Out = %d", bytes_in, bytes_out); */
}

static void ipaq_read_bulk_callback(struct urb *urb)
{
	struct usb_serial_port	*port = (struct usb_serial_port *)urb->context;
	struct usb_serial	*serial = get_usb_serial (port, __FUNCTION__);
	struct tty_struct	*tty;
	unsigned char		*data = urb->transfer_buffer;
	int			i, result;

	if (port_paranoia_check(port, __FUNCTION__))
		return;

282
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
283 284

	if (!serial) {
285
		dbg("%s - bad serial pointer, exiting", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
286 287 288 289
		return;
	}

	if (urb->status) {
290
		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
Linus Torvalds's avatar
Linus Torvalds committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
		return;
	}

	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);

	tty = port->tty;
	if (urb->actual_length) {
		for (i = 0; i < urb->actual_length ; ++i) {
			/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
				tty_flip_buffer_push(tty);
			}
			/* this doesn't actually push the data through unless tty->low_latency is set */
			tty_insert_flip_char(tty, data[i], 0);
		}
		tty_flip_buffer_push(tty);
		bytes_in += urb->actual_length;
	}

	/* Continue trying to always read  */
311
	usb_fill_bulk_urb(port->read_urb, serial->dev, 
Linus Torvalds's avatar
Linus Torvalds committed
312 313 314
		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
		      ipaq_read_bulk_callback, port);
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
315
	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
316
	if (result)
317
		err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
Linus Torvalds's avatar
Linus Torvalds committed
318 319 320 321 322 323 324 325 326 327
	return;
}

static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
		       int count)
{
	const unsigned char	*current_position = buf;
	int			bytes_sent = 0;
	int			transfer_size;

328
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354

	usb_serial_debug_data(__FILE__, __FUNCTION__, count, buf);
	
	while (count > 0) {
		transfer_size = min(count, PACKET_SIZE);
		if (ipaq_write_bulk(port, from_user, current_position, transfer_size)) {
			break;
		}
		current_position += transfer_size;
		bytes_sent += transfer_size;
		count -= transfer_size;
		bytes_out += transfer_size;
	}

	return bytes_sent;
} 

static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
			   int count)
{
	struct ipaq_private	*priv = port->private;
	struct ipaq_packet	*pkt = NULL;
	int			result = 0;
	unsigned long		flags;

	if (priv->free_len <= 0) {
355
		dbg("%s - we're stuffed", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
356 357 358 359 360 361 362 363 364 365 366
		return -EAGAIN;
	}

	spin_lock_irqsave(&write_list_lock, flags);
	if (!list_empty(&priv->freelist)) {
		pkt = list_entry(priv->freelist.next, struct ipaq_packet, list);
		list_del(&pkt->list);
		priv->free_len -= PACKET_SIZE;
	}
	spin_unlock_irqrestore(&write_list_lock, flags);
	if (pkt == NULL) {
367
		dbg("%s - we're stuffed", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
368 369 370 371
		return -EAGAIN;
	}

	if (from_user) {
Arnaldo Carvalho de Melo's avatar
Arnaldo Carvalho de Melo committed
372 373
		if (copy_from_user(pkt->data, buf, count))
			return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
374 375 376 377 378 379 380 381 382 383 384 385
	} else {
		memcpy(pkt->data, buf, count);
	}
	usb_serial_debug_data(__FILE__, __FUNCTION__, count, pkt->data);

	pkt->len = count;
	pkt->written = 0;
	spin_lock_irqsave(&write_list_lock, flags);
	list_add_tail(&pkt->list, &priv->queue);
	priv->queue_len += count;
	if (priv->active == 0) {
		priv->active = 1;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
386 387 388 389
		ipaq_write_gather(port);
		spin_unlock_irqrestore(&write_list_lock, flags);
		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
		if (result) {
390
			err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
391 392 393
		}
	} else {
		spin_unlock_irqrestore(&write_list_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
394 395 396 397
	}
	return result;
}

Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
398
static void ipaq_write_gather(struct usb_serial_port *port)
Linus Torvalds's avatar
Linus Torvalds committed
399 400 401
{
	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
	struct usb_serial	*serial = port->serial;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
402
	int			count, room;
Linus Torvalds's avatar
Linus Torvalds committed
403 404 405 406 407 408
	struct ipaq_packet	*pkt;
	struct urb		*urb = port->write_urb;
	struct list_head	*tmp;

	if (urb->status == -EINPROGRESS) {
		/* Should never happen */
409
		err("%s - flushing while urb is active !", __FUNCTION__);
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
410
		return;
Linus Torvalds's avatar
Linus Torvalds committed
411 412 413 414 415 416 417 418 419 420 421 422
	}
	room = URBDATA_SIZE;
	for (tmp = priv->queue.next; tmp != &priv->queue;) {
		pkt = list_entry(tmp, struct ipaq_packet, list);
		tmp = tmp->next;
		count = min(room, (int)(pkt->len - pkt->written));
		memcpy(urb->transfer_buffer + (URBDATA_SIZE - room),
		       pkt->data + pkt->written, count);
		room -= count;
		pkt->written += count;
		priv->queue_len -= count;
		if (pkt->written == pkt->len) {
423
			list_move(&pkt->list, &priv->freelist);
Linus Torvalds's avatar
Linus Torvalds committed
424 425 426 427 428 429 430 431
			priv->free_len += PACKET_SIZE;
		}
		if (room == 0) {
			break;
		}
	}

	count = URBDATA_SIZE - room;
432
	usb_fill_bulk_urb(port->write_urb, serial->dev, 
Linus Torvalds's avatar
Linus Torvalds committed
433 434 435
		      usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
		      port->write_urb->transfer_buffer, count, ipaq_write_bulk_callback,
		      port);
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
436
	return;
Linus Torvalds's avatar
Linus Torvalds committed
437 438 439 440 441 442 443
}

static void ipaq_write_bulk_callback(struct urb *urb)
{
	struct usb_serial_port	*port = (struct usb_serial_port *)urb->context;
	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
	unsigned long		flags;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
444
	int			result;
Linus Torvalds's avatar
Linus Torvalds committed
445 446 447 448 449

	if (port_paranoia_check (port, __FUNCTION__)) {
		return;
	}
	
450
	dbg("%s - port %d", __FUNCTION__, port->number);
Linus Torvalds's avatar
Linus Torvalds committed
451 452
	
	if (urb->status) {
453
		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
Linus Torvalds's avatar
Linus Torvalds committed
454 455 456 457
	}

	spin_lock_irqsave(&write_list_lock, flags);
	if (!list_empty(&priv->queue)) {
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
458 459 460 461
		ipaq_write_gather(port);
		spin_unlock_irqrestore(&write_list_lock, flags);
		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
		if (result) {
462
			err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
463
		}
Linus Torvalds's avatar
Linus Torvalds committed
464 465
	} else {
		priv->active = 0;
Ganesh Varadarajan's avatar
Ganesh Varadarajan committed
466
		spin_unlock_irqrestore(&write_list_lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
467
	}
468

Ingo Molnar's avatar
Ingo Molnar committed
469
	schedule_work(&port->work);
Linus Torvalds's avatar
Linus Torvalds committed
470 471 472 473 474 475
}

static int ipaq_write_room(struct usb_serial_port *port)
{
	struct ipaq_private	*priv = (struct ipaq_private *)port->private;

476
	dbg("%s - freelen %d", __FUNCTION__, priv->free_len);
Linus Torvalds's avatar
Linus Torvalds committed
477 478 479 480 481 482 483
	return priv->free_len;
}

static int ipaq_chars_in_buffer(struct usb_serial_port *port)
{
	struct ipaq_private	*priv = (struct ipaq_private *)port->private;

484
	dbg("%s - queuelen %d", __FUNCTION__, priv->queue_len);
Linus Torvalds's avatar
Linus Torvalds committed
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
	return priv->queue_len;
}

static void ipaq_destroy_lists(struct usb_serial_port *port)
{
	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
	struct list_head	*tmp;
	struct ipaq_packet	*pkt;

	for (tmp = priv->queue.next; tmp != &priv->queue;) {
		pkt = list_entry(tmp, struct ipaq_packet, list);
		tmp = tmp->next;
		kfree(pkt->data);
		kfree(pkt);
	}
	for (tmp = priv->freelist.next; tmp != &priv->freelist;) {
		pkt = list_entry(tmp, struct ipaq_packet, list);
		tmp = tmp->next;
		kfree(pkt->data);
		kfree(pkt);
	}
	return;
}


static int ipaq_startup(struct usb_serial *serial)
{
512
	dbg("%s", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
513 514 515 516 517 518
	usb_set_configuration(serial->dev, 1);
	return 0;
}

static void ipaq_shutdown(struct usb_serial *serial)
{
519
	dbg("%s", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
520 521 522 523
}

static int __init ipaq_init(void)
{
524
	spin_lock_init(&write_list_lock);
Linus Torvalds's avatar
Linus Torvalds committed
525
	usb_serial_register(&ipaq_device);
526
	usb_register(&ipaq_driver);
Linus Torvalds's avatar
Linus Torvalds committed
527 528 529 530 531 532 533 534
	info(DRIVER_DESC " " DRIVER_VERSION);

	return 0;
}


static void __exit ipaq_exit(void)
{
535
	usb_deregister(&ipaq_driver);
Linus Torvalds's avatar
Linus Torvalds committed
536 537 538 539 540 541 542 543 544 545 546 547 548 549
	usb_serial_deregister(&ipaq_device);
}


module_init(ipaq_init);
module_exit(ipaq_exit);

MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");

MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");