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
