Hi all, I'm developing a driver for our USB smart card reader. We use a custom made USB-serial converter with interrupt endpoints.
In the open() method I want to start reading. Therefore I'm sending an INT URB. But the system blocks after the usb_submit_urb() call. What I am missing? I attached the source and /proc/bus/usb/devices Kernel 2.4.18-3 usb-ohci Best Regards Thomas
#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> #include <linux/errno.h> #include <linux/poll.h> #include <linux/init.h> #include <linux/ioctl.h> #include <linux/slab.h> #include <linux/fcntl.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/usb.h> #include "SerialUSBConverterRequests.h" #include "usb-debug.c" #ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; #else static int debug; #endif #include "usb-serial.h" /* * Version Information */ #define DRIVER_VERSION "v0.01" #define DRIVER_AUTHOR "Thomas Wahrenbruch" #define DRIVER_DESC "KOBIL USB Card Terminal Driver (experimental)" #define KOBIL_VENDOR_ID 0x0D46 #define KOBIL_PRODUCT_ID 0x2012 #define KOBIL_USBTWIN_PRODUCT_ID 0x0078 /* Function prototypes */ static int kobil_startup (struct usb_serial *serial); static void kobil_shutdown (struct usb_serial *serial); static int kobil_open (struct usb_serial_port *port, struct file *filp); static void kobil_close (struct usb_serial_port *port, struct file *filp); static int kobil_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); static int kobil_write_room(struct usb_serial_port *port); static int kobil_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); static void kobil_set_termios(struct usb_serial_port *port, struct termios * old); static void kobil_read_int_callback( struct urb *urb ); //static void kobil_write_bulk_callback (struct urb *urb); static void kobil_complete( struct urb *purb ); static __devinitdata struct usb_device_id id_table [] = { { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_PRODUCT_ID) }, { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_USBTWIN_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, id_table); struct usb_serial_device_type kobil_device = { name: "KOBIL USB card reader (experimental)", id_table: id_table, needs_interrupt_in: MUST_HAVE, needs_bulk_in: MUST_HAVE_NOT, needs_bulk_out: MUST_HAVE_NOT, num_interrupt_in: NUM_DONT_CARE, num_bulk_in: 0,//NUM_DONT_CARE, num_bulk_out: 0,//NUM_DONT_CARE, num_ports: 1, startup: kobil_startup, shutdown: kobil_shutdown, ioctl: kobil_ioctl, set_termios: kobil_set_termios, open: kobil_open, close: kobil_close, write: kobil_write, write_room: kobil_write_room, read_int_callback: kobil_read_int_callback, }; struct kobil_private { int write_int_endpoint_address; int read_int_endpoint_address; unsigned char wrbuf[64]; }; static int kobil_startup (struct usb_serial *serial) { int i; struct kobil_private *priv; struct usb_device *pdev; struct usb_config_descriptor *actconfig; struct usb_interface *interface; struct usb_interface_descriptor *altsetting; struct usb_endpoint_descriptor *endpoint; printk(KERN_DEBUG "kobil_startup\n" ); init_waitqueue_head(&serial->port->write_wait); serial->port->private = kmalloc(sizeof(struct kobil_private), GFP_KERNEL); if (!serial->port->private){ return (-1); /* error */ } priv = (struct kobil_private *) serial->port->private; // search for the neccessary endpoints pdev = serial->dev; actconfig = pdev->actconfig; interface = actconfig->interface; altsetting = interface->altsetting; endpoint = altsetting->endpoint; for (i = 0; i < altsetting->bNumEndpoints; i++) { usb_show_endpoint_descriptor( endpoint ); endpoint = &altsetting->endpoint[i]; if (((endpoint->bEndpointAddress & 0x80) == 0x00) && ((endpoint->bmAttributes & 3) == 0x03)) { // found interrupt out endpoint printk(KERN_DEBUG "Found interrupt out endpoint. Address: %d\n", endpoint->bEndpointAddress); priv->write_int_endpoint_address = endpoint->bEndpointAddress; } if (((endpoint->bEndpointAddress & 0x80) == 0x80) && ((endpoint->bmAttributes & 3) == 0x03)) { // found interrupt out endpoint printk(KERN_DEBUG "Found interrupt in endpoint. Address: %d\n", endpoint->bEndpointAddress); priv->read_int_endpoint_address = endpoint->bEndpointAddress; } } return( 0 ); } static void kobil_shutdown (struct usb_serial *serial) { int i; //dbg (__FUNCTION__); printk(KERN_DEBUG "kobil_shutdown\n" ); for (i=0; i < serial->num_ports; ++i) { while (serial->port[i].open_count > 0) { kobil_close (&serial->port[i], NULL); } /* My special items, the standard routines free my urbs */ if (serial->port[i].private) kfree(serial->port[i].private); } } static int kobil_open (struct usb_serial_port *port, struct file *filp) { int i, result; struct kobil_private *priv; unsigned char *transfer_buffer; int transfer_buffer_length = 8; purb_t kobil_urb; printk(KERN_DEBUG "kobil_open\n"); if (port_paranoia_check (port, __FUNCTION__)) return -ENODEV; //MOD_INC_USE_COUNT; down (&port->sem); ++(port->open_count); if (!port->active) { port->active = 1; /* force low_latency on so that our tty_push actually forces * the data through, otherwise it is scheduled, and with high * data rates (like with OHCI) data can get lost. */ port->tty->low_latency = 1; // shutdown any bulk reads that might be going on usb_unlink_urb (port->write_urb); usb_unlink_urb (port->read_urb); usb_unlink_urb (port->interrupt_in_urb); } up (&port->sem); // allocate urb kobil_urb = usb_alloc_urb(0); if (!kobil_urb) { printk(KERN_DEBUG "usb_alloc_urb failed"); return (-1); } // allocate memory for transfer buffer transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length * sizeof(char), GFP_KERNEL); if (! transfer_buffer){ return (-1); } else { for (i = 0; i < transfer_buffer_length; i++) { transfer_buffer[i] = 0; } } // init device result = usb_control_msg( port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0 ), 0x10, // request 0xC2, // request type SUSBCR_MSC_GetHWVersion, // value 0, // index transfer_buffer, transfer_buffer_length, 5 // timeout ); printk(KERN_DEBUG "kobil usb_submit_urb( CONTROL_URB ) returns with: %i", result); printk(KERN_DEBUG "Transfer Buffer: "); for (i = 0; i < 8; i++) { printk(KERN_DEBUG "%02X ", (short)(transfer_buffer[i] & 0x00FF) ); } printk(KERN_DEBUG "\n"); //----------------------------------------------------------------- result = usb_control_msg( port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0 ), 0x10, // request 0xC2, // request type SUSBCR_MSC_GetFWVersion, // value 0, // index transfer_buffer, transfer_buffer_length, 5 // timeout ); printk(KERN_DEBUG "kobil usb_submit_urb( CONTROL_URB ) returns with: %i", result); printk(KERN_DEBUG "Transfer Buffer: "); for (i = 0; i < 8; i++) { printk(KERN_DEBUG "%02X ", (short)(transfer_buffer[i] & 0x00FF) ); } printk(KERN_DEBUG "\n"); priv = (struct kobil_private *) port->private; FILL_INT_URB( kobil_urb, port->serial->dev, usb_sndbulkpipe( port->serial->dev, priv->read_int_endpoint_address), transfer_buffer, transfer_buffer_length, kobil_read_int_callback, // completion callback function port, //context 10 //interval ); // start reading usb_dump_urb( kobil_urb ); result = usb_submit_urb( kobil_urb ); if (result){ err("kobil usb_submit_urb( READ_INTERRUPT_URB ) failed. result = %i", result); } else { printk(KERN_DEBUG "kobil usb_submit_urb(READ_INTERRUPT_URB) succeeded\n"); } kfree(transfer_buffer); //usb_free_urb(kobil_urb); return (0); } static void kobil_close (struct usb_serial_port *port, struct file *filp) { dbg(__FUNCTION__ " - port %d", port->number); printk(KERN_DEBUG "kobil_close\n"); --port->open_count; if (port->open_count <= 0) { usb_unlink_urb (port->write_urb); usb_unlink_urb (port->read_urb); usb_unlink_urb (port->interrupt_in_urb); port->active = 0; port->open_count = 0; } printk(KERN_DEBUG "kobil_close (9)\n"); return; //MOD_DEC_USE_COUNT; } static void kobil_read_int_callback( struct urb *purb ) { int result; struct usb_serial_port *port = (struct usb_serial_port *) purb->context; printk(KERN_DEBUG "kobil_read_int_callback\n"); port->read_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb); if( result ) { printk(KERN_DEBUG "submit interrupt_in_urb failed\n"); } else { printk(KERN_DEBUG "submit interrupt_in_urb successful\n"); } wake_up_interruptible(&port->write_wait); } static void kobil_complete( struct urb *purb ) { char* p_dummy; int i; struct usb_serial_port *port; printk(KERN_DEBUG "kobil_complete (1)_\n"); usb_dump_urb( purb ); p_dummy = purb->transfer_buffer; printk(KERN_DEBUG "Transfer Buffer: "); for (i = 0; i < 8; i++) { printk(KERN_DEBUG "%02X ", (short)( p_dummy[i] & 0x00FF) ); } printk(KERN_DEBUG "\n"); /*if (purb->status) { purb->status = 0; return; }*/ port = (struct usb_serial_port *) purb->context; return; } static int kobil_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { int i; int result = 0; unsigned char *transfer_buffer; int transfer_buffer_length = 8; purb_t kobil_urb; struct kobil_private * k_priv; printk(KERN_DEBUG "kobil_write (1)\n"); for (i = 0; i < count; i++) { printk(KERN_DEBUG "%02X ", (short)(buf[i] & 0x00FF) ); } printk(KERN_DEBUG "\n"); // allocate memory for transfer buffer transfer_buffer = (unsigned char *) kmalloc(transfer_buffer_length * sizeof(char), GFP_KERNEL); if (! transfer_buffer){ return (-1); } else { for (i = 0; i < transfer_buffer_length; i++) { transfer_buffer[i] = 0xFF; } } k_priv = (struct kobil_private *) port->private; // allocate urb kobil_urb = usb_alloc_urb(0); if (!kobil_urb) { printk(KERN_DEBUG "usb_alloc_urb failed"); return (-1); } // do some paranoia checking if (port_paranoia_check (port, __FUNCTION__)){ printk(KERN_DEBUG "PORT PARANOIA CHECK FAILED!!!\n"); } if (serial_paranoia_check(port->serial, __FUNCTION__)){ printk(KERN_DEBUG "SERIAL PARANOIA CHECK FAILED!!!\n"); } //----------------------------------------------------------------- transfer_buffer[0] = 0x01; transfer_buffer[1] = 0x00; transfer_buffer[2] = 0x01; FILL_BULK_URB( kobil_urb, port->serial->dev, usb_sndbulkpipe( port->serial->dev, k_priv->write_int_endpoint_address), transfer_buffer, 3, //transfer_buffer_length, kobil_complete, // completion callback function port //context //0 //10 //interval ); usb_dump_urb( kobil_urb ); result = usb_submit_urb( kobil_urb ); if (result){ err("kobil usb_submit_urb( INTERRUPT_URB ) failed. result = %i", result); } else { printk(KERN_DEBUG "kobil usb_submit_urb(INTERRUPT_URB) succeeded\n"); } kfree(transfer_buffer); //usb_free_urb(kobil_urb); return count; } static int kobil_write_room (struct usb_serial_port *port) { printk(KERN_DEBUG "kobil_write_room\n"); dbg(__FUNCTION__ " - port %d", port->number); return (8); } static int kobil_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) { printk(KERN_DEBUG "kobil_ioctl: "); //dbg(__FUNCTION__ " - port %d", port->number); switch (cmd) { case TCGETS: // 0x5401 printk(KERN_DEBUG "TCGESTS received\n"); break; case TCSETS: // 0x5402 printk(KERN_DEBUG "TCSESTS received\n"); break; case TCFLSH: // 0x540B printk(KERN_DEBUG "TCFLUSH received\n"); break; case TIOCMGET: // 0x5415 printk(KERN_DEBUG "TIOCMGET received\n"); break; case TIOCMSET: // 0x5418 printk(KERN_DEBUG "TIOCMSET received\n"); break; case 0x77: printk(KERN_DEBUG "DECREMENT_MODULES_USE_COUNT\n"); MOD_DEC_USE_COUNT; break; default: return ( 0 ); } return 0; } // maybe not neccessary ?!? static void kobil_set_termios(struct usb_serial_port *port, struct termios * old) { printk(KERN_DEBUG "kobil_set_termios\n"); } static int __init kobil_init (void) { printk(KERN_DEBUG "kobil_init\n"); usb_serial_register (&kobil_device); info(DRIVER_VERSION " " DRIVER_AUTHOR); info(DRIVER_DESC); return 0; } static void __exit kobil_exit (void) { printk(KERN_DEBUG "kobil_exit\n"); usb_serial_deregister (&kobil_device); } module_init(kobil_init); module_exit(kobil_exit); MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE( "GPL" ); MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not");
T: Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0 D: Ver= 1.10 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0000 ProdID=0000 Rev= 0.00 S: Product=USB OHCI Root Hub S: SerialNumber=e0874000 C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 2 Ivl=255ms T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 4 B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0 D: Ver= 1.10 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0000 ProdID=0000 Rev= 0.00 S: Product=USB OHCI Root Hub S: SerialNumber=e0872000 C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 2 Ivl=255ms T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=1.5 MxCh= 0 D: Ver= 1.10 Cls=ff(vend.) Sub=ff Prot=ff MxPS= 8 #Cfgs= 1 P: Vendor=0d46 ProdID=0078 Rev= 2.01 S: Manufacturer=KOBIL S: Product=USB-Twin C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=150mA I: If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) E: Ad=01(O) Atr=03(Int.) MxPS= 8 Ivl= 8ms E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl= 8ms