Hello,

I am working on a linux driver for the Mixman dm2 (
http://mixman.com/products/dm2.html ), and I'm having a bit of an
issue.
The device has a bunch of buttons and doo-dads (including some LEDs),
and communicates with the computer via an interrupt (for the status of
all the buttons and doo-dads) and a bulk transfer (for setting the
LEDs).
The interrupt works fine on the two computers I've tried it on, and
the driver happily writes the data out to the proc file that I've set
up. The bulk transfer, on the other hand, doesn't work on my main
computer, returning a -EINVAL (-22) error somewhere in the
submit_bulk_urb function. The callback is never called, so it is
failing somewhere before the urb is submitted.

My motherboard is an asus K8V-Deluxe SE (
http://usa.asus.com/products4.aspx?modelmenu=2&model=790&l1=3&l2=14&l3=67
), and has a VIA  K8T800 chipset, VT82xxxxx UHCI.
Anyway, setting the LEDs worked on the default Ubuntu 2.6.13 kernel on
my tablet computer, but in my desktop, running Gentoo with my
self-built 2.6.14 kernel, it doesn't work. The device works under
Windows on my computer (with the provided drivers), and doesn't work
on my 32-bit Gentoo install, so I'm guessing it has something to do
with either my driver, the way Linux works with my hardware, or a bug
in the device that is worked around in the Windows driver (which is
pretty much still "the way Linux works with my hardware").
The one thing about the device that I also think might cause the
problem is that it uses a bulk transfer, even though it is a low-speed
device (which according to the USB spec, is not supposed to use bulk
transfers).

So long story medium-length, I was hoping somebody could help me
figure out what's going wrong, whether it's in my code or in uhci-hcd
or my code, or neither somehow.

So I've slapped my code at the end of this email. The error takes
place in the call to submit_bulk_urb call in the set_leds function.
Can anybody help me figure this out?
--
Paul Bonser

--------------------dm2.c---------------------------
/*
    dm2 linux driver
    Copyright (C) 2005  Paul Bonser <[EMAIL PROTECTED]>

    GPL goes here (taken out to reduce size)

*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/delay.h>

#define VENDOR_ID  0x0665
#define PRODUCT_ID 0x0301

struct dm2 {
        struct usb_device *udev;
        u8 buttons1;
        u8 buttons2;
        u8 buttons3;
        u8 buttons4;
        u8 slider;
        u8  joyx;
        u8  joyy;
        u8 ring1;
        u8 ring2;
        u8 extra;
        u32 leds;
        u8 bulk_out_endpointAddr;
        u8 int_in_endpointAddr;
        u8 *int_in_buffer;
        u8 int_in_buffer_size;
        u8 *bulk_out_buffer;
        struct urb *int_in_urb;
        struct urb *bulk_out_urb;
        int ticks;
};

struct input_packet {/* This is it as far as I can tell */
        u8 buttons1;
        u8 buttons2;
        u8 buttons3;
        u8 buttons4;
        u8 pad1;
        u8 xpos;
        u8 ypos;
        u8 slider;
        u8 ring1;
        u8 ring2;
} __attribute__((packed));

static void int_done(struct urb *urb, struct pt_regs *regs)
{
        
        struct dm2 *ddev = urb->context;
        struct input_packet *in = urb->transfer_buffer;
        int retval;

        ddev->ticks++;
        
        switch (urb->status) {
                case 0: break; /* no errors */
                case -ECONNRESET:
                case -ENOENT:
                case -ESHUTDOWN:
                        dbg("urb shutting down with status %x\n", urb->status);
                        return;
                default:
                        dbg("nonzero urb status received: %x\n", urb->status);
                        goto exit;
        }

        ddev->buttons1 = in->buttons1;
        ddev->buttons2 = in->buttons2;
        ddev->buttons3 = in->buttons3;
        ddev->buttons4 = in->buttons4;
        ddev->extra  = in->pad1; /* this may be extra, but I better keep it
just in case*/
        ddev->joyx = in->xpos;
        ddev->joyy = in->ypos;
        ddev->slider = in->slider;
        ddev->ring1 = in->ring1;
        ddev->ring2 = in->ring2;

exit:
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval)
                dev_err(&urb->dev->dev,
                                "Error %d submitting interrupt urb\n",
                                retval);
}

static int do_init(struct dm2 *ddev)
{
        int retval;
        
        ddev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!ddev->int_in_urb)  {
                dev_err(&ddev->udev->dev, "No free urbs available\n");
                return 1;
        }
        usb_fill_int_urb(ddev->int_in_urb, ddev->udev,
                        usb_rcvintpipe(ddev->udev, ddev->int_in_endpointAddr),
                        ddev->int_in_buffer, ddev->int_in_buffer_size,
                        int_done, ddev, 10);
        retval = usb_submit_urb(ddev->int_in_urb, GFP_KERNEL);
        if (retval)
                dev_err(&ddev->udev->dev, "Error %d submitting interrupt urb\n",
                                retval);

        return retval;
}


static void set_leds_done(struct urb *urb, struct pt_regs *regs)
{
        struct dm2 *ddev = urb->context;
        
        switch (urb->status)    {
                case 0: break;
                case -ECONNRESET:
                case -ENOENT:
                case -ESHUTDOWN:
                        dbg("urb shutting down with status %x\n",urb->status);
                        return;
                default:
                        dbg("nonzero urb status received: %x\n", urb->status);
                        return;
        }
        /* save the state for status info if it was successful */
        ((char*)&ddev->leds)[0] = ((char*)urb->transfer_buffer)[0];
        ((char*)&ddev->leds)[1] = ((char*)urb->transfer_buffer)[1];
        ((char*)&ddev->leds)[2] = ((char*)urb->transfer_buffer)[2];
        ((char*)&ddev->leds)[3] = ((char*)urb->transfer_buffer)[3];
}

/******************Error happens in here*******************/
static int set_leds(struct dm2 *ddev, u32 leds)
{
        int retval;

        ddev->bulk_out_buffer[0] = ~(u8)(leds>>24);
        ddev->bulk_out_buffer[1] = ~(u8)(leds>>16);
        ddev->bulk_out_buffer[2] = ~(u8)(leds>>8);
        ddev->bulk_out_buffer[3] = ~(u8)(leds);
        
        ddev->bulk_out_urb = usb_alloc_urb(0, GFP_KERNEL);
        usb_fill_bulk_urb(ddev->bulk_out_urb, ddev->udev,
                        usb_sndbulkpipe(ddev->udev, 
ddev->bulk_out_endpointAddr),
                        ddev->bulk_out_buffer, 4, set_leds_done, ddev);

        retval = usb_submit_urb(ddev->bulk_out_urb, GFP_KERNEL);
                        
        if (retval)
                dev_err(&ddev->udev->dev, "Error %d submitting bulk message\n",
                                retval);
        
        return retval;
}

static ssize_t show_status(struct device *dev,
                struct device_attribute *attr,
                char *buf)
{
        int r1, r2;
        struct usb_interface *intf = to_usb_interface(dev);
        struct dm2 *ddev = usb_get_intfdata(intf);

        /* convert to the range of -128 to 127*/
        r1 = ddev->ring1 > 127 ? 256-ddev->ring1 : ddev->ring1==0 ? 
0:-ddev->ring1;
        r2 = ddev->ring2 > 127 ? 256-ddev->ring2 : ddev->ring2==0 ? 
0:-ddev->ring2;
        
        return sprintf(buf, "%x %x %x %x\n%x %x\n%x  %x\n%i %i\n%x\n%d\n",
                        ddev->buttons1, ddev->buttons2, ddev->buttons3, 
ddev->buttons4,
                        ddev->slider, ddev->extra,
                        ddev->joyx, ddev->joyy,
                        r1, r2,
                        ddev->leds, ddev->ticks);
}
static DEVICE_ATTR(dm2, S_IRUGO, show_status, NULL);

static struct usb_device_id id_table [] = {
        { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
        { },
};
MODULE_DEVICE_TABLE(usb, id_table);

static int dm2_probe(struct usb_interface *interface,
                     const struct usb_device_id *id)
{
        struct usb_device *udev = interface_to_usbdev(interface);
        struct dm2 *ddev;

        struct usb_endpoint_descriptor *endpoint;
        struct usb_host_interface *iface_desc;
        int buffer_size;
        int i;
        
        ddev = kmalloc(sizeof(struct dm2), GFP_KERNEL);
        if (ddev == NULL) {
                dev_err(&interface->dev, "Out of memory\n");
                return -ENOMEM;
        }
        memset(ddev, 0x00, sizeof(*ddev));

        ddev->udev = usb_get_dev(udev);

        usb_set_intfdata(interface, ddev);

        device_create_file(&interface->dev, &dev_attr_dm2);

        /* set up endpoints */
        iface_desc = interface->cur_altsetting;
        for (i=0; i < iface_desc->desc.bNumEndpoints; i++) {
                endpoint = &iface_desc->endpoint[i].desc;

                if ((endpoint->bEndpointAddress & USB_DIR_IN) &&
                    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                          == USB_ENDPOINT_XFER_INT)) {
                        /* int endpoint found */
                        buffer_size = 10;
                        ddev->int_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
                        ddev->int_in_endpointAddr = endpoint->bEndpointAddress;
                        ddev->int_in_buffer_size = buffer_size;
                        if (!ddev->int_in_buffer) {
                                dev_err(&interface->dev,
                                        "Could not allocate int_in_buffer\n");
                                return 1;
                        }
                }

                if (!(endpoint->bEndpointAddress & USB_DIR_IN) &&
                    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                                == USB_ENDPOINT_XFER_BULK)) {
                        ddev->bulk_out_endpointAddr = 
endpoint->bEndpointAddress;
                        ddev->bulk_out_buffer = kmalloc(4, GFP_KERNEL);

                }
        }
        if (!(ddev->int_in_endpointAddr && ddev->bulk_out_endpointAddr)) {
                dev_err(&interface->dev,
                        "Could not find int-in and bulk-out endpoints\n");
                return 1;
        }
        
        dev_info(&interface->dev, "dm2 device now attached\n");
        printk(KERN_INFO "Found int endpoint %x and bulk endpoint %x\n",
                        ddev->int_in_endpointAddr, ddev->bulk_out_endpointAddr);
        
        do_init(ddev); /* Start the interrupts going */
        
        set_leds(ddev, 0xaaaa0000); /* turn every other one on  */
        mdelay(50); /* yadda yadda */
        set_leds(ddev, 0x55550000); /* turn every OTHER other one on  */
        return 0;
}

static void dm2_disconnect(struct usb_interface *interface)
{
        struct dm2 *ddev;

        device_remove_file(&interface->dev, &dev_attr_dm2);
        ddev = usb_get_intfdata(interface);
        usb_set_intfdata(interface, NULL);

        usb_put_dev(ddev->udev);
        kfree(ddev);
        dev_info(&interface->dev, "dm2 now disconnected\n");
}

static struct usb_driver dm2_driver = {
        .owner =        THIS_MODULE,
        .name =         "dm2",
        .probe =        dm2_probe,
        .disconnect =   dm2_disconnect,
        .id_table =     id_table,
};

static int __init dm2_init(void)
{
        return usb_register(&dm2_driver);
}
static void __exit dm2_exit(void)
{
        usb_deregister(&dm2_driver);
}

module_init(dm2_init);
module_exit(dm2_exit);

MODULE_AUTHOR("Paul Bonser");
MODULE_DESCRIPTION("dm2 Driver");
MODULE_LICENSE("GPL");


-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_idv37&alloc_id865&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