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