Hello,

I have attached a patch to the 2.6.9 kernel containing a driver for
Garmin GPS devices (www.garmin.com). I have only tried it with a GPS18
so far and I
would invite others with different devices to join me in development
and testing.

I would like ideas on the following:

Handling interrupt in data: it comes fast and I must buffer it up
so that the user has a chance to get the data out. Is there a better
way to ensure that packets won't be lost?

Reading data: Right now read (bulk in) and ioctl (interrupt in) rely
on the user providing a sufficiently large buffer to handle the max
packet size.

I can provide some more detailed information to anyone interested in helping.

Thanks,
Marty Boos

cd /usr/src/linux/drivers/usb; patch -p0 -b < patchfile

-- /home/marty/src/linux-2.6.9_tp/drivers/usb/Makefile  2004-11-02
23:21:18.000000000 +0000
+++ Makefile    2004-11-02 23:58:50.822733552 +0000
@@ -67,3 +67,4 @@
 obj-$(CONFIG_USB_TIGL)         += misc/
 obj-$(CONFIG_USB_USS720)       += misc/
 obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
+obj-$(CONFIG_USB_GARMIN)       += misc/
--- /home/marty/src/linux-2.6.9_tp/drivers/usb/misc/Makefile    2004-11-02
23:21:12.000000000 +0000
+++ ./misc/Makefile     2004-11-02 23:58:50.839730968 +0000
@@ -16,3 +16,4 @@
 obj-$(CONFIG_USB_TIGL)         += tiglusb.o
 obj-$(CONFIG_USB_USS720)       += uss720.o
 obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
+obj-$(CONFIG_USB_GARMIN)       += garmin_usb.o
--- /home/marty/src/linux-2.6.9_tp/drivers/usb/misc/Kconfig     2004-11-02
23:21:16.000000000 +0000
+++ ./misc/Kconfig      2004-11-02 23:58:50.838731120 +0000
@@ -157,3 +157,11 @@
          See <http://www.linux-usb.org/usbtest/> for more information,
          including sample test device firmware and "how to use it".
 
+config USB_GARMIN
+       tristate "Garmin GPS USB support"
+       depends on USB && EXPERIMENTAL
+       help
+         Say Y if you want to connect to a Garmin GPS.
+
+         To compile this driver as a module, choose M here: the
+         module will be called garmin_usb.
--- /home/marty/src/linux-2.6.9_tp/drivers/usb/misc/garmin_usb.c        1970-01-01
00:00:00.000000000 +0000
+++ ./misc/garmin_usb.c 2004-11-01 23:24:28.000000000 +0000
@@ -0,0 +1,916 @@
+/*
+*   Garmin device USB driver.
+*   Copyright (C) 2004 Martin Boos ([EMAIL PROTECTED])
+*
+*   Based on USB Skeleton driver - 1.1
+*   Copyright (C) 2001-2003 Greg Kroah-Hartman ([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, version 2.
+*
+*   History:
+*       2004-10-24     Initial version.
+*   
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+#include "garmin_usb.h"
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG
__FILE__ ": " format "\n" , ## arg); } while (0)
+
+/* Version Information */
+#define DRIVER_VERSION "v0.01"
+#define DRIVER_AUTHOR "Martin Boos [EMAIL PROTECTED]"
+#define DRIVER_DESC "Garmin device USB driver"
+
+/* Module parameters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug printing enabled");
+
+#define II_BUFFER_COUNT        16
+#define next( i, max ) ( (((i)+1)>=(max)) ? (0) : ((i)+1) )
+
+/* Define these values to match your devices */
+#define GARMIN_VENDOR_ID       0x091e
+#define GARMIN_PRODUCT_ID      3
+
+/* table of devices that work with this driver */
+static struct usb_device_id garmin_table[] = {
+       {USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_PRODUCT_ID)},
+       {}                      /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, garmin_table);
+
+struct buffer {
+       unsigned char *data;    /* the data section                             */
+       size_t size;                /* size of the data section             */
+       size_t count;               /* amount of data in the buffer */
+};
+
+/* Get a minor range for your devices from the usb maintainer */
+#define GARMIN_USB_MINOR_BASE  192
+
+/* Structure to hold all of our device specific stuff */
+struct garmin_dev {
+       struct usb_device *udev;                /* save off the usb device pointer */
+       struct usb_interface *interface;        /* the interface for this device */
+       unsigned char minor;                /* the starting minor number for
this device */
+       unsigned char num_ports;                /* the number of ports this device has 
*/
+
+       struct urb *ii_urb;                     /* URB to receive interrupt in
transfers */
+       struct buffer ii_buffer;                /* holds interrupt urb transfer data */
+       struct usb_endpoint_descriptor *ii_endpoint;
+       struct buffer ii_data_buffer[II_BUFFER_COUNT];
+                                        /* data transfer buffers not
attached to urb */
+                                        /* to hold interrupt transfers */
+       spinlock_t ii_data_lock;                /* protect ii data buffer */
+       int ii_data_head;                       /* head of buffer */
+       int ii_data_tail;                       /* tail of buffer */
+       int ii_data_full;                       /* buffer has wrapped */
+       atomic_t ii_has_data;               /* indicates data added to bi_buffer */
+       wait_queue_head_t ii_wait_queue;        /* waits on ii_has_data */
+
+       struct buffer bi_buffer;                /* the buffer to receive data */
+       struct usb_endpoint_descriptor *bi_endpoint;
+
+       struct buffer bo_buffer;                /* the buffer to send data */
+       struct urb *bo_urb;                     /* the urb used to send data */
+       struct usb_endpoint_descriptor *bo_endpoint;
+       atomic_t bo_busy;                       /* true iff bo urb is busy */
+       wait_queue_head_t bo_wait_queue;        /* waits on the bo_busy flag */
+
+       int open;                                   /* if the port is open or not */
+       int present;                            /* if the device is not disconnected */
+       struct semaphore sem;               /* locks this structure */
+};
+
+/* prevent races between open() and disconnect() */
+static DECLARE_MUTEX(disconnect_sem);
+
+/* local function prototypes */
+static ssize_t garmin_read(struct file *file, char *buffer, size_t
count, loff_t * ppos);
+static ssize_t garmin_write(struct file *file, const char *buffer,
size_t count, loff_t * ppos);
+static int garmin_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
+static int garmin_open(struct inode *inode, struct file *file);
+static int garmin_release(struct inode *inode, struct file *file);
+
+static int garmin_probe(struct usb_interface *interface, const struct
usb_device_id *id);
+static void garmin_disconnect(struct usb_interface *interface);
+
+static void garmin_write_bulk_callback(struct urb *urb, struct pt_regs *regs);
+static void garmin_ii_callback(struct urb *urb, struct pt_regs *regs);
+
+static int buffer_alloc(struct buffer *buf, size_t size);
+static void buffer_free(struct buffer *buf);
+
+/*
+ * File operations needed when we register this driver.
+ * This assumes that this driver NEEDS file operations,
+ * of course, which means that the driver is expected
+ * to have a node in the /dev directory. If the USB
+ * device were for a network interface then the driver
+ * would use "struct net_driver" instead, and a serial
+ * device would use "struct tty_driver".
+ */
+static struct file_operations garmin_fops = {
+       /*
+        * The owner field is part of the module-locking
+        * mechanism. The idea is that the kernel knows
+        * which module to increment the use-counter of
+        * BEFORE it calls the device's open() function.
+        * This also means that the kernel can decrement
+        * the use-counter again before calling release()
+        * or should the open() function fail.
+        */
+       .owner = THIS_MODULE,
+
+       .read = garmin_read,
+       .write = garmin_write,
+       .ioctl = garmin_ioctl,
+       .open = garmin_open,
+       .release = garmin_release,
+};
+
+/* 
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver garmin_class = {
+       .name = "usb/garmin%d",
+       .fops = &garmin_fops,
+       .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
+       .minor_base = GARMIN_USB_MINOR_BASE,
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver garmin_driver = {
+       .owner = THIS_MODULE,
+       .name = "garmin_usb",
+       .probe = garmin_probe,
+       .disconnect = garmin_disconnect,
+       .id_table = garmin_table,
+};
+
+/**
+ *     usb_garmin_debug_data
+ */
+static inline void usb_garmin_debug_data(const char *function, int size,
+                                        const unsigned char *data)
+{
+       int i;
+
+       if (!debug)
+               return;
+
+       printk(KERN_DEBUG __FILE__ ": %s - length = %d, data = ",
+              function, size);
+       for (i = 0; i < size; ++i) {
+               printk("%.2x ", data[i]);
+       }
+       printk("\n");
+}
+
+/**
+ *     garmin_delete
+ */
+static inline void garmin_delete(struct garmin_dev *dev)
+{
+       int i;
+
+       dbg("%s", __FUNCTION__);
+       buffer_free(&dev->bi_buffer);
+       buffer_free(&dev->ii_buffer);
+       for (i = 0; i < II_BUFFER_COUNT; i++) {
+               buffer_free(&dev->ii_data_buffer[i]);
+       }
+
+       usb_free_urb(dev->ii_urb);
+
+       usb_buffer_free(dev->udev, dev->bo_buffer.size,
+                       dev->bo_buffer.data, dev->bo_urb->transfer_dma);
+       usb_free_urb(dev->bo_urb);
+
+       kfree(dev);
+}
+
+/**
+ *     garmin_open
+ */
+static int garmin_open(struct inode *inode, struct file *file)
+{
+       struct garmin_dev *dev = NULL;
+       struct usb_interface *interface;
+       int minor;
+       int retval = 0;
+
+       dbg("%s", __FUNCTION__);
+
+       minor = iminor(inode);
+
+       /* prevent disconnects */
+       down(&disconnect_sem);
+
+       interface = usb_find_interface(&garmin_driver, minor);
+       if (!interface) {
+               err("%s - error, can't find device for minor %d",
+                   __FUNCTION__, minor);
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       dbg("found interface minor %d", minor);
+
+       dev = usb_get_intfdata(interface);
+       if (!dev) {
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       /* lock this device */
+       down(&dev->sem);
+
+       /* increment our usage count for the driver */
+       ++dev->open;
+
+       /* save our object in the file's private structure */
+       file->private_data = dev;
+    
+    /* TODO: should I move the initialization of buffers and wait queues here
+             but leave the allocation in the probe function so that
you can open/release
+             the device file multiple times and get a clean start each time */
+    atomic_set(&dev->ii_has_data, 0);
+       dev->ii_data_head = 0;
+       dev->ii_data_tail = 0;
+       dev->ii_data_full = 0;
+
+       /* unlock this device */
+       up(&dev->sem);
+
+exit_no_device:
+       up(&disconnect_sem);
+       return retval;
+}
+
+/**
+ *     garmin_release
+ */
+static int garmin_release(struct inode *inode, struct file *file)
+{
+       struct garmin_dev *dev;
+       int retval = 0;
+
+       dev = (struct garmin_dev *)file->private_data;
+       if (dev == NULL) {
+               dbg("%s - object is NULL", __FUNCTION__);
+               return -ENODEV;
+       }
+
+       dbg("%s - minor %d", __FUNCTION__, dev->minor);
+
+       /* lock our device */
+       down(&dev->sem);
+
+       if (dev->open <= 0) {
+               dbg("%s - device not opened", __FUNCTION__);
+               retval = -ENODEV;
+               goto exit_not_opened;
+       }
+
+       /* wait for any bulk writes that might be going on to finish up */
+       retval =
+           wait_event_interruptible(dev->bo_wait_queue,
+                                    !atomic_read(&dev->bo_busy));
+
+       /* just in case someone was waiting and they can get out before we close */
+       wake_up_interruptible(&dev->ii_wait_queue);
+
+       --dev->open;
+
+       if (!dev->present && !dev->open) {
+               /* the device was unplugged before the file was released */
+               dbg("%s - unplugged before file released", __FUNCTION__);
+               up(&dev->sem);
+               garmin_delete(dev);
+               return 0;
+       }
+
+exit_not_opened:
+       up(&dev->sem);
+
+       return retval;
+}
+
+/**
+ *     garmin_read
+ */
+static ssize_t garmin_read(struct file *file, char *buffer, size_t count,
+                          loff_t * ppos)
+{
+       struct garmin_dev *dev;
+       int retval = 0;
+
+       dev = (struct garmin_dev *)file->private_data;
+
+       dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count);
+
+       /* lock this object */
+       down(&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (!dev->present) {
+               up(&dev->sem);
+               return -ENODEV;
+       }
+
+       /* do a blocking bulk read to get data from the device, timeout = 3 seconds */
+       retval = usb_bulk_msg(dev->udev,
+                    usb_rcvbulkpipe(dev->udev,
dev->bi_endpoint->bEndpointAddress),
+                    dev->bi_buffer.data, 
+                    min(dev->bi_buffer.size, count), 
+                    &count, 
+                    3 * HZ);
+
+       /* if the read was successful, copy the data to userspace */
+       if (!retval) {
+               if (copy_to_user(buffer, dev->bi_buffer.data, count))
+                       retval = -EFAULT;
+               else
+                       retval = count;
+       }
+
+       /* unlock the device */
+       up(&dev->sem);
+       return retval;
+}
+
+/**
+ *     garmin_write
+ */
+static ssize_t garmin_write(struct file *file, const char *buffer,
size_t count,
+                           loff_t * ppos)
+{
+       struct garmin_dev *dev;
+       ssize_t bytes_written = 0;
+       int retval = 0;
+
+       dev = (struct garmin_dev *)file->private_data;
+
+       dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count);
+       down(&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (!dev->present) {
+               dbg("write no dev present");
+               retval = -ENODEV;
+               goto exit;
+       }
+
+       /* verify that we actually have some data to write */
+       if (count == 0) {
+               dbg("%s - write request of 0 bytes", __FUNCTION__);
+               goto exit;
+       }
+
+       /* wait for a previous write to finish up; we don't use a timeout
+        * and so a nonresponsive device can delay us indefinitely.
+        */
+
+       retval =
+           wait_event_interruptible(dev->bo_wait_queue,
+                                    !atomic_read(&dev->bo_busy));
+       if (retval){
+        if( retval == -ERESTARTSYS) {
+            dbg("I've got an ERESTARTSYS");
+        }
+        goto exit;
+       }
+
+       /* we can only write as much as our buffer will hold */
+       bytes_written = min(dev->bo_buffer.size, count);
+
+       /* copy the data from userspace into our transfer buffer;
+        * this is the only copy required.
+        */
+       if (copy_from_user(dev->bo_urb->transfer_buffer, buffer, bytes_written)) {
+               retval = -EFAULT;
+               goto exit;
+       }
+
+       //usb_garmin_debug_data(__FUNCTION__, bytes_written,
+       //                    dev->bo_urb->transfer_buffer);
+
+       /* this urb was already set up, except for the write size */
+       dev->bo_urb->transfer_buffer_length = bytes_written;
+
+       /* send the data out the bulk port */
+       atomic_set(&dev->bo_busy, 1);
+       retval = usb_submit_urb(dev->bo_urb, GFP_KERNEL);
+       if (retval) {
+               atomic_set(&dev->bo_busy, 0);
+               err("%s - failed submitting write urb, error %d",
+                   __FUNCTION__, retval);
+       } else {
+               retval = bytes_written;
+       }
+
+exit:
+       up(&dev->sem);
+
+       return retval;
+}
+
+/**
+ *     garmin_ioctl
+ */
+static int garmin_ioctl(struct inode *inode, struct file *file,
+                       unsigned int cmd, unsigned long arg)
+{
+       struct garmin_dev *dev;
+       int ret;
+       int err;
+
+       dev = (struct garmin_dev *)file->private_data;
+       ret = -ENOTTY;
+       err = 0;
+
+       /* lock this object */
+       down(&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (!dev->present) {
+               dbg("device not present");
+               up(&dev->sem);
+               return -ENODEV;
+       }
+
+       /* check for valid ioctl command */
+       if ((_IOC_TYPE(cmd) == GARMIN_USB_IOCTL_MAGIC)
+           && (_IOC_NR(cmd) <= GARMIN_USB_IOCTL_MAXNR)) {
+               ret = -EFAULT;
+               switch (cmd) {
+               case GARMIN_USB_IOCTL_INTERRUPT_SIZE:
+                       {
+                __u32 size = dev->ii_endpoint->wMaxPacketSize;
+                if(put_user(size, (__u32 *) arg) == 0) {
+                    ret = sizeof(size);
+                               }
+                       }
+                       break;
+
+               case GARMIN_USB_IOCTL_BULK_SIZE:
+                       {
+                               __u32 size = dev->bi_endpoint->wMaxPacketSize;
+                               if (put_user(size, (__u32 *) arg) == 0) {
+                                       ret = sizeof(size);
+                               }
+                       }
+                       break;
+
+               case GARMIN_USB_IOCTL_INTERRUPT_IN:
+                       {
+                               /* read the interrupt pipe, user buffer must be
+                                * at least the size of interrupt packets
+                                */
+                               unsigned long flags;
+                               struct buffer *buf;
+
+                               //dbg("IOCTL read interrupt has data? %d",
+                               //    atomic_read(&dev->ii_has_data));
+
+                               /* If there is no data available, wait for some.
+                                * If ret != 0 then we received a signal instead of 
data
+                                */
+                               ret =
+                                   wait_event_interruptible(dev->ii_wait_queue,
+                                                            
atomic_read(&dev->ii_has_data));
+                               if (ret) {
+                                       goto exit_ioctl;
+                               }
+
+                               /* Finally got some data. Copy the packet
+                                * over to userland. Manage the circular 
+                                * interrupt data buffers.
+                                */
+                               spin_lock_irqsave(&dev->ii_data_lock, flags);
+
+                               buf = &dev->ii_data_buffer[dev->ii_data_tail];
+                               ret = buf->count;
+                               copy_to_user((void *)arg, buf->data, ret);
+                               dev->ii_data_tail =
+                                   next(dev->ii_data_tail, II_BUFFER_COUNT);
+                               dev->ii_data_full = 0;
+
+                               spin_unlock_irqrestore(&dev->ii_data_lock, flags);
+
+                               atomic_dec(&dev->ii_has_data);
+
+                               //if (ret > 0) {
+                               //      usb_garmin_debug_data(__FUNCTION__, ret, 
buf->data);
+                               //}
+                       }
+                       break;
+               default:
+                       err("Unsupported IOCTL %d", cmd);
+               };
+       }
+exit_ioctl:
+       /* unlock the device */
+       up(&dev->sem);
+       return ret;
+}
+
+/**
+ *     garmin_write_bulk_callback
+ */
+static void garmin_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct garmin_dev *dev = (struct garmin_dev *)urb->context;
+
+       dbg("%s - minor %d", __FUNCTION__, dev->minor);
+
+       /* sync/async unlink faults aren't errors */
+       if (urb->status && !(urb->status == -ENOENT ||
+                            urb->status == -ECONNRESET)) {
+               dbg("%s - nonzero status received: %d",
+                   __FUNCTION__, urb->status);
+       }
+
+       /* notify anyone waiting that the write has finished */
+       atomic_set(&dev->bo_busy, 0);
+       wake_up_interruptible(&dev->bo_wait_queue);
+}
+
+/**
+ *     garmin_ii_callback
+ *      Interrupt context callback for interrupt in pipe.
+ */
+static void garmin_ii_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct garmin_dev *dev = (struct garmin_dev *)urb->context;
+       struct buffer *buf;
+
+       switch (urb->status) {
+       case -EPIPE:
+               dbg("interrupt pipe stalled");
+               break;
+       case -EILSEQ:
+               dbg("crc mismatch");
+               break;
+       case -EPROTO:
+               dbg("really messed up");
+               break;
+       case -EINPROGRESS:
+               dbg("In progress at callback");
+               break;
+       case -EREMOTEIO:
+               dbg("data did not fill specified buffer");
+               break;
+       case -ETIMEDOUT:
+               dbg("NAK");
+               break;
+       case -ENODEV:
+               dbg("Device Removed");
+               break;
+       case -ENOENT:
+       case -ECONNRESET:
+               dbg("Connection async unlinked");
+               break;
+       }
+
+       if (urb->status != 0) {
+               dbg("DROPPED INTERRUPT PACKET, len %d, status %d",
+                   urb->actual_length, urb->status);
+               goto exit_buffer_full;
+       }
+
+       spin_lock(&dev->ii_data_lock);
+
+       /* out of interrupt buffers to fill, this will probably spell
trouble for any client of this driver */
+       if (dev->ii_data_full) {
+               dbg("all interrupt in data buffers are full");
+               spin_unlock(&dev->ii_data_lock);
+               goto exit_buffer_full;
+       }
+
+       /* copy urb data to an interrupt data buffer */
+       buf = &dev->ii_data_buffer[dev->ii_data_head];
+       memcpy(buf->data, urb->transfer_buffer, urb->actual_length);
+       buf->count = urb->actual_length;
+
+       /* point to the next buffer */
+       dev->ii_data_head = next(dev->ii_data_head, II_BUFFER_COUNT);
+       if (dev->ii_data_head == dev->ii_data_tail) {
+               dev->ii_data_full = 1;
+       }
+
+       atomic_inc(&dev->ii_has_data);
+       spin_unlock(&dev->ii_data_lock);
+
+       //dbg("head %d, tail %d, hd %d", dev->ii_data_head, dev->ii_data_tail,
+       //   atomic_read(&dev->ii_has_data));
+
+       wake_up_interruptible(&dev->ii_wait_queue);
+
+exit_buffer_full:
+       /* resubmit urb to get more interrupt data */
+       if (usb_submit_urb(dev->ii_urb, GFP_ATOMIC) < 0) {
+               dbg("interrupt urb submit failed");
+       }
+}
+
+/**
+ *     garmin_probe
+ *
+ *     Called by the usb core when a new device is connected that it thinks
+ *     this driver might be interested in.
+ */
+static int garmin_probe(struct usb_interface *interface,
+                       const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct garmin_dev *dev = NULL;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int i;
+       int j;
+       int retval = -ENOMEM;
+
+       /* See if the device offered us matches what we can accept */
+       if ((udev->descriptor.idVendor != GARMIN_VENDOR_ID)
+           || (udev->descriptor.idProduct != GARMIN_PRODUCT_ID)) {
+               return -ENODEV;
+       }
+
+       /* allocate memory for our device state and initialize it */
+       dev = kmalloc(sizeof(struct garmin_dev), GFP_KERNEL);
+       if (dev == NULL) {
+               err("Out of memory");
+               goto error;
+       }
+       memset(dev, 0x00, sizeof(*dev));
+
+       init_MUTEX(&dev->sem);
+       dev->udev = udev;
+       dev->interface = interface;
+
+       /* set up the endpoint information */
+       /* use only the first  endpoints found */
+       iface_desc = &interface->altsetting[0];
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               /* bulk in endpoints */
+               if (dev->bi_endpoint == NULL &&
+                   ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 
USB_DIR_IN)
+                   && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_BULK)) {
+                       /* we found a bulk in endpoint */
+                       dev->bi_endpoint = endpoint;
+                       if (!buffer_alloc
+                           (&dev->bi_buffer,
+                            dev->bi_endpoint->wMaxPacketSize)) {
+                               err("Couldn't allocate bi_buffer");
+                               goto error;
+                       }
+                       dbg("found bulk in");
+               }
+               /* bulk out endpoint */
+               if (dev->bo_endpoint == NULL &&
+                   ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 
USB_DIR_OUT)
+                   && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_BULK)) {
+                       /* we found a bulk out endpoint */
+                       /* a probe() may sleep and has no restrictions on memory 
allocations */
+                       dev->bo_urb = usb_alloc_urb(0, GFP_KERNEL);
+                       if (!dev->bo_urb) {
+                               err("No free urbs available");
+                               goto error;
+                       }
+
+                       dev->bo_endpoint = endpoint;
+                       dev->bo_urb->transfer_flags =
+                           (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK | 
URB_ZERO_PACKET);
+                       dev->bo_buffer.size = dev->bo_endpoint->wMaxPacketSize;
+                       dev->bo_buffer.data = usb_buffer_alloc(udev,
+                                                              dev->bo_buffer.
+                                                              size, GFP_KERNEL,
+                                                              
&dev->bo_urb->transfer_dma);
+                       if (dev->bo_buffer.data == NULL) {
+                               err("Couldn't allocate bo_buffer");
+                               goto error;
+                       }
+                       usb_fill_bulk_urb
+                           (dev->bo_urb,
+                            udev,
+                            usb_sndbulkpipe(udev, dev->bo_endpoint->bEndpointAddress),
+                             dev->bo_buffer.data, 
+                             dev->bo_buffer.size,
+                             garmin_write_bulk_callback, 
+                            dev);
+
+                       dbg("found bulk out");
+               }
+               /* interrupt in endpoint */             
+               if ((dev->ii_endpoint == NULL) &&
+                   ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 
USB_DIR_IN)
+                   && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT)) {
+                       dev->ii_urb = usb_alloc_urb(0, GFP_KERNEL);
+                       if (!dev->ii_urb) {
+                               err("No free urbs available");
+                               goto error;
+                       }
+
+                       dev->ii_endpoint = endpoint;
+                       if (!buffer_alloc(&dev->ii_buffer, 
dev->ii_endpoint->wMaxPacketSize)) {
+                               err("Couldn't allocate interrupt in buffer");
+                               goto error;
+                       }
+                       for (j = 0; j < II_BUFFER_COUNT; j++) {
+                               if (!buffer_alloc
+                                   (&dev->ii_data_buffer[j],
+                                    dev->ii_endpoint->wMaxPacketSize)) {
+                                       err("couldn't allocate ii_data_buffers");
+                                       goto error;
+                               }
+                       }
+                       dbg("found interrupt in");
+               }
+
+       }
+       if (!(dev->bi_endpoint && dev->bo_endpoint && dev->ii_endpoint)) {
+               err("Couldn't find all the endpoints");
+               //retval = ERROR??
+               goto error;
+       }
+
+       init_waitqueue_head(&dev->bo_wait_queue);
+
+       /* setup the urb that will get interrupt in data */
+       usb_fill_int_urb
+           (dev->ii_urb,
+            dev->udev,
+            usb_rcvintpipe(dev->udev, dev->ii_endpoint->bEndpointAddress),
+            dev->ii_buffer.data,
+            dev->ii_buffer.size,
+            garmin_ii_callback, dev, dev->ii_endpoint->bInterval);
+
+       spin_lock_init(&dev->ii_data_lock);
+       init_waitqueue_head(&dev->ii_wait_queue);
+       atomic_set(&dev->ii_has_data, 0);
+       dev->ii_data_head = 0;
+       dev->ii_data_tail = 0;
+       dev->ii_data_full = 0;
+
+       /* allow device read, write and ioctl */
+       dev->present = 1;
+
+       /* we can register the device now, as it is ready */
+       usb_set_intfdata(interface, dev);
+       retval = usb_register_dev(interface, &garmin_class);
+       if (retval) {
+               /* something prevented us from registering this driver */
+               err("Not able to get a minor for this device.");
+               usb_set_intfdata(interface, NULL);
+               goto error;
+       }
+
+       dev->minor = interface->minor;
+
+       if (usb_submit_urb(dev->ii_urb, GFP_KERNEL)) {
+               dbg("%s - int urb submit failed", __FUNCTION__);
+               goto error;
+       }
+
+       /* let the user know what node this device is now attached to */
+       info("Garmin USB device now attached to minor %d", dev->minor);
+       return 0;
+
+error:
+       garmin_delete(dev);
+       return retval;
+}
+
+/**
+ *     garmin_disconnect
+ *
+ *     Called by the usb core when the device is removed from the system.
+ *
+ *     This routine guarantees that the driver will not submit any more urbs
+ *     by clearing dev->udev.  It is also supposed to terminate any currently
+ *     active urbs.  Unfortunately, usb_bulk_msg(), used in garmin_read(), does
+ *     not provide any way to do this.  But at least we can cancel an active
+ *     write.
+ */
+static void garmin_disconnect(struct usb_interface *interface)
+{
+       struct garmin_dev *dev;
+       int minor;
+
+       /* prevent races with open() */
+       down(&disconnect_sem);
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       down(&dev->sem);
+
+       minor = dev->minor;
+
+       /* give back our minor */
+       usb_deregister_dev(interface, &garmin_class);
+
+       /* terminate an ongoing write */
+       if (atomic_read(&dev->bo_busy)) {
+               usb_unlink_urb(dev->bo_urb);
+               wait_event_interruptible(dev->bo_wait_queue,
+                                        !atomic_read(&dev->bo_busy));
+       }
+
+       /*I am assuming that I can always unlink ii_urb since it is
+          always submitted unless I am in the interrupt in completion
+          handler. */
+       usb_unlink_urb(dev->ii_urb);
+
+       /* prevent device read, write and ioctl */
+       dev->present = 0;
+
+       up(&dev->sem);
+
+       /* if the device is opened, garmin_release will clean this up */
+       if (!dev->open)
+               garmin_delete(dev);
+
+       up(&disconnect_sem);
+
+       info("garmin usb device on minor %d now disconnected", minor);
+}
+
+/* Local buffer functions */
+
+static int buffer_alloc(struct buffer *buf, size_t size)
+{
+       buf->data = kmalloc(size, GFP_KERNEL);
+       if (buf->data == NULL)
+               return 0;
+       buf->size = size;
+       buf->count = 0;
+       return 1;
+}
+
+static void buffer_free(struct buffer *buf)
+{
+       kfree(buf->data);
+}
+
+/**
+ *     usb_garmin_init
+ */
+static int __init usb_garmin_init(void)
+{
+       int result;
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&garmin_driver);
+       if (result) {
+               err("usb_register failed. Error number %d", result);
+               return result;
+       }
+
+       info(DRIVER_DESC " " DRIVER_VERSION " Inited");
+       return 0;
+}
+
+/**
+ *     usb_garmin_exit
+ */
+static void __exit usb_garmin_exit(void)
+{
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&garmin_driver);
+       info(DRIVER_DESC " " DRIVER_VERSION " Exited");
+}
+
+module_init(usb_garmin_init);
+module_exit(usb_garmin_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
--- /home/marty/src/linux-2.6.9_tp/drivers/usb/misc/garmin_usb.h        1970-01-01
00:00:00.000000000 +0000
+++ ./misc/garmin_usb.h 2004-11-01 21:57:04.000000000 +0000
@@ -0,0 +1,31 @@
+/*
+*   garmin_usb.h
+*   Garmin device USB driver.
+*   Copyright (C) 2004 Martin Boos ([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, version 2.
+*
+*   History:
+*       2004-10-24     Initial version.
+*   
+*/
+#ifndef GARMIN_USB_H
+#define GARMIN_USB_H
+
+#include <linux/ioctl.h>
+
+#define GARMIN_USB_IOCTL_MAGIC 'G'
+
+#define GARMIN_USB_IOCTL_BULK_SIZE     _IOR( GARMIN_USB_IOCTL_MAGIC, 1, int )
+/*
+the size for interrupt in uses int as a dummy value, 
+use IOCTL_INTERRUPT_SIZE to find out the
+maximum number of bytes it will return.
+ */
+#define GARMIN_USB_IOCTL_INTERRUPT_IN   _IOR( GARMIN_USB_IOCTL_MAGIC, 2, int )
+#define GARMIN_USB_IOCTL_INTERRUPT_SIZE _IOR( GARMIN_USB_IOCTL_MAGIC, 3, int )
+#define GARMIN_USB_IOCTL_MAXNR 3 
+
+#endif


-------------------------------------------------------
This SF.Net email is sponsored by:
Sybase ASE Linux Express Edition - download now for FREE
LinuxWorld Reader's Choice Award Winner for best database on Linux.
http://ads.osdn.com/?ad_id=5588&alloc_id=12065&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to