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

Reply via email to