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