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