Hi,

Here's a patch against 2.5.2-pre5 that adds the USB ipaq driver.
The driver was written by Ganesh Varadarajan.  A patch to add the driver
to the build and the Configure.help entry will be sent later.

thanks,

greg k-h



diff -Nru a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/serial/ipaq.c Mon Dec 31 10:37:52 2001
@@ -0,0 +1,525 @@
+/*
+ * USB Compaq iPAQ driver
+ *
+ *     Copyright (C) 2001
+ *         Ganesh Varadarajan <[EMAIL PROTECTED]>
+ *
+ *     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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#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"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Ganesh Varadarajan <[EMAIL PROTECTED]>"
+#define DRIVER_DESC "USB Compaq iPAQ driver"
+
+/* 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);
+static int ipaq_write_flush(struct usb_serial_port *port);
+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);
+
+
+static __devinitdata struct usb_device_id ipaq_id_table [] = {
+       { USB_DEVICE(IPAQ_VENDOR_ID, IPAQ_PRODUCT_ID) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, ipaq_id_table);
+
+/* All of the device info needed for the Compaq iPAQ */
+struct usb_serial_device_type ipaq_device = {
+       owner:                  THIS_MODULE,
+       name:                   "Compaq iPAQ",
+       id_table:               ipaq_id_table,
+       num_interrupt_in:       0,
+       num_bulk_in:            1,
+       num_bulk_out:           1,
+       num_ports:              1,
+       open:                   ipaq_open,
+       close:                  ipaq_close,
+       startup:                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,
+};
+
+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;
+
+       if (port_paranoia_check(port, __FUNCTION__)) {
+               return -ENODEV;
+       }
+       
+       dbg(__FUNCTION__ " - port %d", port->number);
+
+       down(&port->sem);
+       
+       ++port->open_count;
+       
+       if (port->open_count == 1) {
+               bytes_in = 0;
+               bytes_out = 0;
+               priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), 
+GFP_KERNEL);
+               if (priv == NULL) {
+                       err(__FUNCTION__ " - Out of memory");
+                       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;
+                       }
+                       pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL);
+                       if (pkt->data == NULL) {
+                               kfree(pkt);
+                               goto enomem;
+                       }
+                       pkt->len = 0;
+                       pkt->written = 0;
+                       INIT_LIST_HEAD(&pkt->list);
+                       list_add(&pkt->list, &priv->freelist);
+                       priv->free_len += PACKET_SIZE;
+               }
+
+               /*
+                * Force low latency on. This will immediately push data to the line
+                * discipline instead of queueing.
+                */
+
+               port->tty->low_latency = 1;
+
+               /*
+                * Lose the small buffers usbserial provides. Make larger ones.
+                */
+
+               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) {
+                       kfree(port->bulk_in_buffer);
+                       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 */
+               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,
+                             ipaq_read_bulk_callback, port);
+               result = usb_submit_urb(port->read_urb);
+               if (result) {
+                       err(__FUNCTION__ " - failed submitting read urb, error %d", 
+result);
+               }
+
+               /*
+                * Send out two control messages observed in win98 sniffs. Not sure 
+what
+                * they do.
+                */
+
+               result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 
+0x22, 0x21,
+                               0x1, 0, NULL, 0, 5 * HZ);
+               if (result < 0) {
+                       err(__FUNCTION__ " - failed doing control urb, error %d", 
+result);
+               }
+               result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 
+0x22, 0x21,
+                               0x1, 0, NULL, 0, 5 * HZ);
+               if (result < 0) {
+                       err(__FUNCTION__ " - failed doing control urb, error %d", 
+result);
+               }
+       }
+       
+       up(&port->sem);
+       
+       return result;
+
+enomem:
+       ipaq_destroy_lists(port);
+       kfree(priv);
+       err(__FUNCTION__ " - Out of memory");
+       return -ENOMEM;
+}
+
+
+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; 
+       }
+       
+       dbg(__FUNCTION__ " - port %d", port->number);
+                        
+       serial = get_usb_serial(port, __FUNCTION__);
+       if (!serial)
+               return;
+       
+       down (&port->sem);
+
+       --port->open_count;
+
+       if (port->open_count <= 0) {
+
+               /*
+                * shut down bulk read and write
+                */
+
+               usb_unlink_urb(port->write_urb);
+               usb_unlink_urb(port->read_urb);
+               ipaq_destroy_lists(port);
+               kfree(priv);
+               port->private = NULL;
+               port->open_count = 0;
+
+       }
+       up (&port->sem);
+
+       /* 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;
+
+       dbg(__FUNCTION__ " - port %d", port->number);
+
+       if (!serial) {
+               dbg(__FUNCTION__ " - bad serial pointer, exiting");
+               return;
+       }
+
+       if (urb->status) {
+               dbg(__FUNCTION__ " - nonzero read bulk status received: %d", 
+urb->status);
+               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  */
+       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,
+                     ipaq_read_bulk_callback, port);
+       result = usb_submit_urb(port->read_urb);
+       if (result)
+               err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+       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;
+
+       dbg(__FUNCTION__ " - port %d", port->number);
+
+       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) {
+               dbg(__FUNCTION__ " - we're stuffed");
+               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) {
+               dbg(__FUNCTION__ " - we're stuffed");
+               return -EAGAIN;
+       }
+
+       if (from_user) {
+               copy_from_user(pkt->data, buf, count);
+       } 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;
+               result = ipaq_write_flush(port);
+       }
+       spin_unlock_irqrestore(&write_list_lock, flags);
+       return result;
+}
+
+static int ipaq_write_flush(struct usb_serial_port *port)
+{
+       struct ipaq_private     *priv = (struct ipaq_private *)port->private;
+       struct usb_serial       *serial = port->serial;
+       int                     count, room, result;
+       struct ipaq_packet      *pkt;
+       struct urb              *urb = port->write_urb;
+       struct list_head        *tmp;
+
+       if (urb->status == -EINPROGRESS) {
+               /* Should never happen */
+               err(__FUNCTION__ " - flushing while urb is active !");
+               return -EAGAIN;
+       }
+       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) {
+                       list_del(&pkt->list);
+                       list_add(&pkt->list, &priv->freelist);
+                       priv->free_len += PACKET_SIZE;
+               }
+               if (room == 0) {
+                       break;
+               }
+       }
+
+       count = URBDATA_SIZE - room;
+       FILL_BULK_URB(port->write_urb, serial->dev, 
+                     usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+                     port->write_urb->transfer_buffer, count, 
+ipaq_write_bulk_callback,
+                     port);
+       result = usb_submit_urb(urb);
+       if (result) {
+               err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+       }
+       return result;
+}
+
+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;
+
+       if (port_paranoia_check (port, __FUNCTION__)) {
+               return;
+       }
+       
+       dbg(__FUNCTION__ " - port %d", port->number);
+       
+       if (urb->status) {
+               dbg(__FUNCTION__ " - nonzero write bulk status received: %d", 
+urb->status);
+       }
+
+       spin_lock_irqsave(&write_list_lock, flags);
+       if (!list_empty(&priv->queue)) {
+               ipaq_write_flush(port);
+       } else {
+               priv->active = 0;
+       }
+       spin_unlock_irqrestore(&write_list_lock, flags);
+       queue_task(&port->tqueue, &tq_immediate);
+       mark_bh(IMMEDIATE_BH);
+       
+       return;
+}
+
+static int ipaq_write_room(struct usb_serial_port *port)
+{
+       struct ipaq_private     *priv = (struct ipaq_private *)port->private;
+
+       dbg(__FUNCTION__ " - freelen %d", priv->free_len);
+       return priv->free_len;
+}
+
+static int ipaq_chars_in_buffer(struct usb_serial_port *port)
+{
+       struct ipaq_private     *priv = (struct ipaq_private *)port->private;
+
+       dbg(__FUNCTION__ " - queuelen %d", priv->queue_len);
+       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)
+{
+       dbg(__FUNCTION__);
+       usb_set_configuration(serial->dev, 1);
+       return 0;
+}
+
+static void ipaq_shutdown(struct usb_serial *serial)
+{
+       int i;
+
+       dbg (__FUNCTION__);
+
+       /* stop reads and writes on all ports */
+       for (i=0; i < serial->num_ports; ++i) {
+               while (serial->port[i].open_count > 0) {
+                       ipaq_close(&serial->port[i], NULL);
+               }
+       }
+}
+
+static int __init ipaq_init(void)
+{
+       usb_serial_register(&ipaq_device);
+       info(DRIVER_DESC " " DRIVER_VERSION);
+
+       return 0;
+}
+
+
+static void __exit ipaq_exit(void)
+{
+       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");
+
diff -Nru a/drivers/usb/serial/ipaq.h b/drivers/usb/serial/ipaq.h
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/serial/ipaq.h Mon Dec 31 10:37:52 2001
@@ -0,0 +1,60 @@
+/*
+ * USB Compaq iPAQ driver
+ *
+ *     Copyright (C) 2001
+ *         Ganesh Varadarajan <[EMAIL PROTECTED]>
+ *
+ *     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.
+ *
+ * 
+ */
+
+#ifndef __LINUX_USB_SERIAL_IPAQ_H
+#define __LINUX_USB_SERIAL_IPAQ_H
+
+
+#define IPAQ_VENDOR_ID                 0x049f
+#define IPAQ_PRODUCT_ID                        0x0003
+
+/*
+ * Since we can't queue our bulk write urbs (don't know why - it just
+ * doesn't work), we can send down only one write urb at a time. The simplistic
+ * approach taken by the generic usbserial driver will work, but it's not good
+ * for performance. Therefore, we buffer upto URBDATA_QUEUE_MAX bytes of write
+ * requests coming from the line discipline. This is done by chaining them
+ * in lists of struct ipaq_packet, each packet holding a maximum of
+ * PACKET_SIZE bytes.
+ *
+ * ipaq_write() can be called from bottom half context; hence we can't
+ * allocate memory for packets there. So we initialize a pool of packets at
+ * the first open and maintain a freelist.
+ *
+ * The value of PACKET_SIZE was empirically determined by
+ * checking the maximum write sizes sent down by the ppp ldisc.
+ * URBDATA_QUEUE_MAX is set to 64K, which is the maximum TCP window size
+ * supported by the iPAQ.
+ */
+
+struct ipaq_packet {
+       char                    *data;
+       size_t                  len;
+       size_t                  written;
+       struct list_head        list;
+};
+
+struct ipaq_private {
+       int                     active;
+       int                     queue_len;
+       int                     free_len;
+       struct list_head        queue;
+       struct list_head        freelist;
+};
+
+#define URBDATA_SIZE           4096
+#define URBDATA_QUEUE_MAX      (64 * 1024)
+#define PACKET_SIZE            256
+
+#endif


_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to