Hello.
Here is the source code for a portable data collector Argox PT-12
driver. It's been tested and is in production now. I see a point in
including this to kernel. SF project name - argox-pt (awaiting
registration confirmation). You could find this source code there in a
couple of days.

Any comments and suggestions are welcome.

---

/*
 * USB CRD-10 driver - 1.0 for use with Argox PT-12.
 *
 * Copyright (C) 2006 Alexander Voronin ([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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * This driver is based on drivers/usb/usb-skeleton.c version 2.6.3
 * but has been rewritten in terms of simplication, as no locks are
 * needed anymore.
 */

#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/kref.h>
#include <asm/uaccess.h>
#include <linux/usb.h>


/* define vendor & device IDs */
#define USB_CRD_VENDOR_ID    0x1664
#define USB_CRD_PRODUCT_ID    0xd100

/* a list of devices that are supposed to work with this driver */
static struct usb_device_id crd_table [] = {
    { USB_DEVICE(USB_CRD_VENDOR_ID, USB_CRD_PRODUCT_ID) },
    { }                    /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, crd_table);


/* get a minor range for devices from usb maintainer */
#define USB_CRD_MINOR_BASE    192

/* our private defines. if this grows any larger, use your own .h file */
#define MAX_TRANSFER        ( PAGE_SIZE - 512 )
#define WRITES_IN_FLIGHT    8

/* structure to hold all device-specific stuff */
struct usb_crd {
    struct usb_device *    udev;            /* usb device for this device */
    struct usb_interface *    interface;        /* interface for this device */
    struct semaphore    limit_sem;        /* limiting the number of
writes in progress */
    unsigned char *        intr_in_buffer;        /* buffer to receive data */
    size_t            intr_in_size;        /* the size of the
receiving buffer */
    __u8            intr_in_endpointAddr;    /* the address of the int
in endpoint */
    __u8            intr_out_endpointAddr;    /* the address of the
int out endpoint */
    struct kref        kref;
};
#define to_crd_dev(d) container_of(d, struct usb_crd, kref)

static struct usb_driver crd_driver;

static void crd_delete(struct kref *kref)
{
    struct usb_crd *dev = to_crd_dev(kref);

    usb_put_dev(dev->udev);
    kfree (dev->intr_in_buffer);
    kfree (dev);
}

static int crd_open(struct inode *inode, struct file *file)
{
    struct usb_crd *dev;
    struct usb_interface *interface;
    int subminor;
    int retval = 0;

    subminor = iminor(inode);

    interface = usb_find_interface(&crd_driver, subminor);
    if (!interface) {
        err ("%s - error, can't find device for minor %d",
             __FUNCTION__, subminor);
        retval = -ENODEV;
        goto exit;
    }

    dev = usb_get_intfdata(interface);
    if (!dev) {
        retval = -ENODEV;
        goto exit;
    }

    /* increment usage count for device */
    kref_get(&dev->kref);

    /* save object in the file's private structure */
    file->private_data = dev;

exit:
    return retval;
}

static int crd_release(struct inode *inode, struct file *file)
{
    struct usb_crd *dev;

    dev = (struct usb_crd *)file->private_data;
    if (dev == NULL)
        return -ENODEV;

    /* decrement the count on device */
    kref_put(&dev->kref, crd_delete);
    return 0;
}

static ssize_t crd_read(struct file *file, char *buffer, size_t count,
loff_t *ppos)
{
    struct usb_crd *dev;
    int retval = 0;
    int bytes_read;

    dev = (struct usb_crd *)file->private_data;

    /* do a blocking bulk read to get data from the device */
    retval = usb_bulk_msg(dev->udev,
                  usb_rcvintpipe(dev->udev, dev->intr_in_endpointAddr),
                  dev->intr_in_buffer,
                  min(dev->intr_in_size, count),
                  &bytes_read, 10000);
    /* if successful, copy the data to userspace */
    if (!retval) {
        if (copy_to_user(buffer, dev->intr_in_buffer, bytes_read))
            retval = -EFAULT;
        else
            retval = bytes_read;
    }
        return retval;
}

static void crd_write_intr_callback(struct urb *urb, struct pt_regs *regs)
{
    struct usb_crd *dev;

    dev = (struct usb_crd *)urb->context;

    /* sync/async unlink faults aren't errors */
    if (urb->status &&
        !(urb->status == -ENOENT ||
          urb->status == -ECONNRESET ||
          urb->status == -ESHUTDOWN)) {
        dbg("%s - nonzero write int status received: %d",
            __FUNCTION__, urb->status);
    }

    /* dellocate buffer */
    usb_buffer_free(urb->dev, urb->transfer_buffer_length,
            urb->transfer_buffer, urb->transfer_dma);
    up(&dev->limit_sem);
}

static ssize_t crd_write(struct file *file, const char *user_buffer,
size_t count, loff_t *ppos)
{
    struct usb_crd *dev;
    int retval = 0;
    struct urb *urb = NULL;
    char *buf = NULL;
    size_t writesize = min(count, (size_t)MAX_TRANSFER);

    dev = (struct usb_crd *)file->private_data;

    /* verify that we actually have some data to write */
    if (count == 0)
        goto exit;

    /* limit the number of URBs in flight to stop a user from using up
all RAM */
    if (down_interruptible(&dev->limit_sem)) {
        retval = -ERESTARTSYS;
        goto exit;
    }

    /* create an urb and a buffer for it; copy all the data to the urb */
    urb = usb_alloc_urb(0, GFP_KERNEL);
    if (!urb) {
        retval = -ENOMEM;
        goto error;
    }

    buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
    if (!buf) {
        retval = -ENOMEM;
        goto error;
    }

    if (copy_from_user(buf, user_buffer, writesize)) {
        retval = -EFAULT;
        goto error;
    }

    /* initialize urb properly */
    usb_fill_int_urb(urb, dev->udev,
              usb_sndintpipe(dev->udev, dev->intr_out_endpointAddr),
              buf, writesize, crd_write_intr_callback, dev, 1);
    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    /* send data out the int port */
    retval = usb_submit_urb(urb, GFP_KERNEL);
    if (retval) {
        err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
        goto error;
    }

    /* release reference to this urb, the USB core will eventually
free it entirely */
    usb_free_urb(urb);

exit:
    return writesize;

error:
    usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
    usb_free_urb(urb);
    up(&dev->limit_sem);
    return retval;
}

static struct file_operations crd_fops = {
    .owner =    THIS_MODULE,
    .read =        crd_read,
    .write =    crd_write,
    .open =        crd_open,
    .release =    crd_release,
};

/*
 * usb class driver info in order to get a minor number from the usb core,
 * and to have the device registered with the driver core
 */
static struct usb_class_driver crd_class = {
    .name =        "crd%d",
    .fops =        &crd_fops,
    .minor_base =    USB_CRD_MINOR_BASE,
};

static int crd_probe(struct usb_interface *interface, const struct
usb_device_id *id)
{
    struct usb_crd *dev = NULL;
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    size_t buffer_size;
    int i;
    int retval = -ENOMEM;

    /* allocate memory for device state and initialize it */
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (dev == NULL) {
        err("Out of memory");
        goto error;
    }
    kref_init(&dev->kref);
    sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);

    dev->udev = usb_get_dev(interface_to_usbdev(interface));
    dev->interface = interface;

    /* set up the endpoint information */
    /* use only the first int-in and int-out endpoints */
    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

        if (!dev->intr_in_endpointAddr &&
            ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
                    == USB_DIR_IN) &&
            ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                    == USB_ENDPOINT_XFER_INT)) {
            /* we found a int in endpoint */
            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
            dev->intr_in_size = buffer_size;
            dev->intr_in_endpointAddr = endpoint->bEndpointAddress;
            dev->intr_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
            if (!dev->intr_in_buffer) {
                err("Could not allocate int_in_buffer");
                goto error;
            }
        }

        if (!dev->intr_out_endpointAddr &&
            ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
                    == USB_DIR_OUT) &&
            ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                    == USB_ENDPOINT_XFER_INT)) {
            /* we found a int out endpoint */
            dev->intr_out_endpointAddr = endpoint->bEndpointAddress;
        }
    }
    if (!(dev->intr_in_endpointAddr && dev->intr_out_endpointAddr)) {
        err("Could not find both int-in and int-out endpoints");
        goto error;
    }

    /* save data pointer in this interface device */
    usb_set_intfdata(interface, dev);

    /* we can register the device now, as it is ready */
    retval = usb_register_dev(interface, &crd_class);
    if (retval) {
        /* something prevented from registering this driver */
        err("Not able to get a minor for this device.");
        usb_set_intfdata(interface, NULL);
        goto error;
    }

    /* let the user know what node this device is now attached to */
    info("USB CRD-10 device now attached to USBCrd-%d", interface->minor);
    return 0;

error:
    if (dev)
        kref_put(&dev->kref, crd_delete);
    return retval;
}

static void crd_disconnect(struct usb_interface *interface)
{
    struct usb_crd *dev;
    int minor = interface->minor;

    /* prevent crd_open() from racing crd_disconnect() */
    lock_kernel();

    dev = usb_get_intfdata(interface);
    usb_set_intfdata(interface, NULL);

    /* give back the minor */
    usb_deregister_dev(interface, &crd_class);

    unlock_kernel();

    /* decrement usage count */
    kref_put(&dev->kref, crd_delete);

    info("USB CRD-10 #%d now disconnected", minor);
}

static struct usb_driver crd_driver = {
    .name =        "crd-10",
    .probe =    crd_probe,
    .disconnect =    crd_disconnect,
    .id_table =    crd_table,
};

static int __init usb_crd_init(void)
{
    int result;

    /* register this driver with USB subsystem */
    result = usb_register(&crd_driver);
    if (result)
        err("usb_register failed. Error number %d", result);

    return result;
}

static void __exit usb_crd_exit(void)
{
    /* unregister this driver with USB subsystem */
    usb_deregister(&crd_driver);
}

module_init (usb_crd_init);
module_exit (usb_crd_exit);

MODULE_LICENSE("GPL");

---

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to