Re: driver migration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Greg KH <gregkh@...> writes:

> 
> On Mon, Feb 15, 2016 at 11:30:43PM +0000, tilman wrote:
> > Dear all
> > 
> > a couple of years ago I wrote a driver for a serial dongle.
> > I did not add it to the linux source because the dongle requires a firmware
> > to be downloaded to the device (ezusb).
> > The manufacturer, IO-DATA, did not want me to use
> > their firmware.
> 
> Why did they not want you to use their firmware in their device?
They did not say. I would think that they feared that users would come to
them in case the driver does not work. For  the successor model, the offer a
linux driver however. 
I have disassembled the firmware however. If I can make some time, I can
rewrite the firmware which would avoid this issue. First step however is to
get the driver working again.


> 
> It's impossible to say without seeing the code, sorry.
Below, it comes...

Thanks
Tilman

/// usbrsa.c

/*
 * Driver for IO-Data's USB RSA serial dongle
 *      Copyright (C) 2012
 *         Tilman Glotzner(tilmanglotzner@xxxxxxxxx)
 *  
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *  
 *
 *
 *  USB-RSA by IO-DATA
 *  USB to serial convertor
 *  =======================
 *  
 *  Remark: The device uses an AN2131Q (Eazy USB) and a ST16C550(UART)
 *  
 *  End Points:
 *  ============
 *  
 *  End Point:	EP1OUT
 *  Direction:	Host to Device
 *  Type:		Bulk Transfer
 *  Size:		64 bytes
 *  Desc:		Data to be transmitted 
 *  Format:		64 bytes of data
 *  
 *  
 *  End Point:	EP2OUT
 *  Direction:	Host to Device
 *  Type:		Bulk Transfer
 *  Size:		3 bytes
 *  Desc:		Set baud rate counter and Line Control Register
 *  			of the ST16C550. Receiver Interrupts are turned ON,
 *  			i.e.  Modem status IR, RX line status IR, and 
 *  			RX holding register IR
 *  Format:
 *  	Byte0:	Low Byte of counter (DLL)
 *  	Byte1:	High Byte of counter (DLM)
 *  	Byte2:	Line Control Register (LCR)
 *  			- Bit 0: Word Length 0
 *  			- Bit 1: Word Length 1
 *  			- Bit 2: Stop Bits
 *  			- Bit 3: Parity Enable
 *  			- Bit 4: Parity Even
 *  			- Bit 5: Set Parity
 *  			- Bit 6: Set Break
 *  			- Bit 7: Counter Register Enable (needs to be set to '0')	
 *           
 *           
 *	End Point:	EP3OUT
 *  Direction:	Host to Device
 *  Type:		Bulk Transfer
 *  Size:		1 bytes
 *  Desc:		Set modem control register of the ST16C550
 *  Format:     
 *  	Byte0:	Modem Control Register
 *  			- Bit0: DTR
 *  			- Bit1: RTS
 *  			- Bit2: OP1
 *  			- Bit3: OP2
 *  			- Bit4: Diagnostics mode 
 *  			
 *  			
 *  End Point:	EP4OUT
 *  Direction:	Host to Device
 *  Type:		Bulk Transfer
 *  Size:		0 bytes
 *  Desc:		Reset EP1OUT and EP1IN (Tx/Rx pipes)
 *  Format:   	n.a.
 *  
 *  
 *  End Point:	EP5OUT
 *  Direction:	Host to Device
 *  Type:		Bulk Transfer
 *  Size:		3 bytes
 *  Desc:		Set baud rate counter and Line Control Register
 *  			of the ST16C550. Receiver Interrupts are turned OFF,
 *  			i.e.  Modem status IR, RX line status IR, and 
 *  			RX holding register IR
 *  Format:
 *  	Byte0:	Low Byte of counter (DLL)
 *  	Byte1:	High Byte of counter (DLM)
 *  	Byte2:	Line Control Register (LCR)
 *  			- Bit 0: Word Length 0
 *  			- Bit 1: Word Length 1
 *  			- Bit 2: Stop Bits
 *  			- Bit 3: Parity Enable
 *  			- Bit 4: Parity Even
 *  			- Bit 5: Set Parity
 *  			- Bit 6: Set Break
 *  			- Bit 7: Counter Register Enable (needs to be set to '0')	 
 *  			
 *  			
 *  End Point:	EP1IN
 *  Direction:	Device to Host 
 *  Type:		Bulk Transfer
 *  Size:		64 bytes
 *  Desc:		Data received by USB-RSA 
 *  Format:		64 bytes of data
 *  
 *  
 *  End Point:	EP2IN
 *  Direction:	Device to Host
 *  Type:		Bulk Transfer
 *  Size:		1 byte
 *  Desc:		Read Modem Status Register of ST16C550
 *  Format:     
 *  	Byte0:	Modem Status Register 
 *  			- Bit0: Delta CTS
 *  			- Bit1: Delta DSR
 *  			- Bit2: Delta RI
 *  			- Bit3: Delta CD
 *  			- Bit4: CTS
 *  			- Bit5: DSR
 *  			- Bit6: RI
 *  			- Bit7: CD
 *  			
 *  			
 *  End Point:	EP3IN
 *  Direction:	Device to Host
 *  Type:		Bulk Transfer
 *  Size:		4 bytes
 *  Desc:		Read RX/TX Pipe Status
 *  Format:   
 *  	Byte0:	Byte available in TX Buffer of USB-RSA (Low Byte)
 *  	Byte1:	Byte available in TX Buffer of USB-RSA (High Byte)	
 *  	Byte2:	Byte received and stored  in RX Buffer of USB-RSA (Low Byte)	
 *  	Byte3:	Byte received and stored  in RX Buffer of USB-RSA (High Byte)
 *  			
 *  	
 *  	todo: use of status urb information (status urb returns
tx_bytes_available on usbrsa) (done)
 *  		  debug: write blocks if more than 9 URBs (done)
 *  	      debug  rx pipe (first urb read is always empty)
 *  	      CTS, and DSR: do these need to be enabled OP1/OP2 ? (no)
 *  	      xon/xoff correctly implemented ? 
 *  	      write function to issue lcr und divisor urb (EPOUT2 and EPOUT5) +
waits for completion (done)
 *  	      reflect lcr, dll, dlm in usbrsa_port_private (done)
 *  	      send break
 *  	      buffering effect: when writing to USB-RSA not all characters are
transferred at once (done)
 *  	      Are parent_serial,parent_port in usbrsa_port_private really
necessary ? 
 *  	      
 *  	      
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.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/mutex.h>
#include <linux/uaccess.h>
#include <asm/termbits.h>
#include <linux/usb.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
#include <linux/usb/serial.h>
#include <linux/usb/ezusb.h>
#include <linux/firmware.h>
#include <linux/ihex.h>
#include <linux/usb/ezusb.h>
#include "usbrsa.h"



#define CONFIG_USB_SERIAL_DEBUG 1
#ifdef CONFIG_USB_SERIAL_DEBUG
	static int debug = 1;
#else
	static int debug = 0;
#endif

/*
 * Version Information
 */
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Tilman Glotzner <tilman.gloetzner@xxxxxxxxx>"
#define DRIVER_DESC "Driver for IODATA's USBRSA serial dongle"


#define IODATA_VENDOR_ID			0x4bb
#define IODATA_USBRSA_PREENUM_ID	0xa01
#define IODATA_USBRSA_ID			0xa02


#define COMMAND_TIMEOUT		(2*HZ)	/* 2 second timeout for a command */

/*
   Taken whiteheat driver as role model:
   ID tables for usb-rsa are unusual, because we want to different
   things for different versions of the device.  Eventually, this
   will be doable from a single table.  But, for now, we define two
   separate ID tables, and then a third table that combines them
   just for the purpose of exporting the autoloading information.
*/
static const struct usb_device_id id_table_std[] = {
        { USB_DEVICE(IODATA_VENDOR_ID, IODATA_USBRSA_ID) },
        { }                                             /* Terminating entry */
};

static const struct usb_device_id id_table_prerenumeration[] = {
        { USB_DEVICE(IODATA_VENDOR_ID, IODATA_USBRSA_PREENUM_ID) },
        { }                                             /* Terminating entry */
};

static const struct usb_device_id id_table_combined[] = {
        { USB_DEVICE(IODATA_VENDOR_ID, IODATA_USBRSA_ID) },
        { USB_DEVICE(IODATA_VENDOR_ID, IODATA_USBRSA_PREENUM_ID) },
        { }                                             /* Terminating entry */
};


MODULE_DEVICE_TABLE(usb, id_table_combined);





struct usbrsa_private {
	spinlock_t lock;
	u8 line_control;
	u8 line_status;

	u8 line_settings[7];
};


////////////////////////////////////////////////////////////////////////
//                   Preenumeration device 
////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
//                   Function Prototypes
////////////////////////////////////////////////////////////////////////
static int usbrsa_firmware_download(struct usb_serial *serial,
                                        const struct usb_device_id *id);
static int usbrsa_firmware_attach(struct usb_serial *serial);


////////////////////////////////////////////////////////////////////////
//                   Driver object declaration
////////////////////////////////////////////////////////////////////////
static struct usb_serial_driver usbrsa_preenum_device = {
        .driver = {
                .owner =        THIS_MODULE,
                .name =         "usbrsanofirm",
        }, 
        .description =          "IO-DATA - USB-RSA - (prerenumeration)",
       // .usb_driver =           &usbrsa_driver,
        .id_table =             id_table_prerenumeration,
        .num_ports =            1,
        .probe =                usbrsa_firmware_download,
        .attach =               usbrsa_firmware_attach,
};


////////////////////////////////////////////////////////////////////////
//                   Functions 
////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////
// Name: usbrsa_firmware_download
// Purpose: Downloads firmware to AN2134Q
//
//////////////////////////////////////////////////////////////////////
static int usbrsa_firmware_download(struct usb_serial *serial,
                                        const struct usb_device_id *id)
{
	int response;

	dev_dbg(&serial->dev->dev,"%s", __func__);
    response = ezusb_fx1_ihex_firmware_download(serial->dev, "usbrsa.fw");

    if (response >= 0)
    {
    	dev_dbg(&serial->dev->dev,"%s(): Firmware downloaded.",__func__);
    	// The USB-RSA does not reenumerate without toggeling the reset bit
    	// one more time
    	udelay(1000);
        response = ezusb_fx1_set_reset(serial->dev, 1);
        udelay(10000);
        if (response >= 0)
        {
        	response = ezusb_fx1_set_reset(serial->dev, 0);
        	if (response >= 0)
        	{
        		dev_dbg(&serial->dev->dev,"%s(): Device with new firmware
reset.",__func__);
        		return 0;
        	}
        }
        dev_err(&serial->dev->dev,"%s(): Status register of EZUSB not
accessible.",__func__);
    }
dev_err(&serial->dev->dev,"%s(): Firmware download or subsequent reset
failed",__func__);
return -ENOENT;

}

//////////////////////////////////////////////////////////////////////
// Name: usbrsa_firmware_attach
// Purpose: Prevents the driver from attaching, so that can attach
//          to the reenumerated device
//
//////////////////////////////////////////////////////////////////////
static int usbrsa_firmware_attach(struct usb_serial *serial)
{
        // We want this device to fail to have the driver 
        // assigned to the reenumerated device 
        return 1;
}


/***********************************************************************/

////////////////////////////////////////////////////////////////////////
//                  REENUMERATED DEVICE 
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
//                   Function Prototypes
////////////////////////////////////////////////////////////////////////
static int  usbrsa_attach(struct usb_serial *serial);
static void  usbrsa_release(struct usb_serial *serial);
static int usbrsa_probe(struct usb_serial_port *port);
static int usbrsa_remove(struct usb_serial_port *port);
static int  usbrsa_open(struct tty_struct *tty,
                        struct usb_serial_port *port);

static void usbrsa_close(struct usb_serial_port *port);
static void usbrsa_set_termios(struct tty_struct *tty,
                        struct usb_serial_port *port, struct ktermios *old);
static void usbrsa_init_termios(struct tty_struct *tty);
static int  usbrsa_write(struct tty_struct *tty,
                        struct usb_serial_port *port,
                        const unsigned char *buf, int count);
static int  usbrsa_ioctl(struct tty_struct *tty,
                        unsigned int cmd, unsigned long arg);
static int usbrsa_write_room(struct tty_struct *tty);
static int usbrsa_chars_in_buffer(struct tty_struct *tty);
static void usbrsa_unthrottle(struct tty_struct *tty);
static void usbrsa_throttle(struct tty_struct *tty); 
static int usbrsa_tiocmset(struct tty_struct *tty,
        unsigned int set, unsigned int clear);
static int usbrsa_tiocmget(struct tty_struct *tty);

////////////////////////////////////////////////////////////////////////
//              Completion Handler Prototypes
////////////////////////////////////////////////////////////////////////
static void usbrsa_baudrate_lcr_callback(struct urb *urb);
static void  usbrsa_reset_callback(struct urb *urb);
static void  usbrsa_status_callback(struct urb *urb);
static void  usbrsa_write_callback(struct urb *urb);
static void  usbrsa_read_callback(struct urb *urb);
static void  usbrsa_mcr_callback(struct urb *urb);
static void  usbrsa_msr_callback(struct urb *urb);

///l/////////////////////////////////////////////////////////////////////
//                   Driver object declaration
////////////////////////////////////////////////////////////////////////
static struct usb_serial_driver usbrsa_enumerated_device = 
{
        .driver = {
                .owner =        THIS_MODULE,
                .name =         "usbrsa",
        }, 
        .description =          "IO-DATA - USB-RSA",
       // .usb_driver =           &usbrsa_driver,
        .id_table =             id_table_std,
        .num_ports =            1,
        .attach =               usbrsa_attach,
        .release =				usbrsa_release,
		.port_probe =		    usbrsa_probe,
		.port_remove =	 		usbrsa_remove,
        .open =					usbrsa_open,
        .close = 				usbrsa_close,
        .write =				usbrsa_write,
        .write_bulk_callback =  usbrsa_write_callback,
        .read_bulk_callback =	usbrsa_read_callback,
        .set_termios =          usbrsa_set_termios,
        .tiocmset = 			usbrsa_tiocmset,
        .tiocmget = 			usbrsa_tiocmget,
     //   .init_termios =			usbrsa_init_termios,
        .ioctl =				usbrsa_ioctl,
        .write_room = 			usbrsa_write_room,
        .chars_in_buffer = 		usbrsa_chars_in_buffer,
        .throttle =				usbrsa_throttle,
        .unthrottle =			usbrsa_unthrottle
};



////////////////////////////////////////////////////////////////////////
//                   Function Prototypes of help functions 
////////////////////////////////////////////////////////////////////////
static int usbrsa_allocate_write_urbs(struct usbrsa_port_private *priv_data);
static int allocate_read_urbs(struct usbrsa_port_private *priv_data);
static int allocate_baudrate_lcr_urb(struct usbrsa_port_private *priv_data);
static int allocate_reset_urb(struct usbrsa_port_private *priv_data);
static int allocate_status_urb(struct usbrsa_port_private *priv_data);
static int allocate_mcr_urb(struct usbrsa_port_private *priv_data);
static int allocate_msr_urb(struct usbrsa_port_private *priv_data);

static void release_write_urbs(struct usbrsa_port_private *priv_data);
static void release_read_urbs(struct usbrsa_port_private *priv_data);
static void release_baudrate_lcr_urb(struct usbrsa_port_private *priv_data);
static void release_reset_urb(struct usbrsa_port_private *priv_data);
static void release_status_urb(struct usbrsa_port_private *priv_data);
static void release_mcr_urb(struct usbrsa_port_private *priv_data);
static void release_msr_urb(struct usbrsa_port_private *priv_data);



static int send_baudrate_lcr_register(unsigned int cflag, speed_t baud_rate,
			int endpoint, struct usb_serial_port *port);
static int send_mcr_register(struct usb_serial_port *port);
static int fetch_msr_register(struct usb_serial_port *port);
static int get_serial_info(struct usb_serial_port  *port,
                           struct serial_struct __user *retinfo);
static int send_reset(struct usb_serial_port *port);
static int wait_modem_info(struct usb_serial_port *port, unsigned int arg);

////////////////////////////////////////////////////////////////////////
//                   Functions 
////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////
// Name: usbrsa_attach
// Purpose: Creates private data structures of driver
//        
//
//////////////////////////////////////////////////////////////////////
static int  usbrsa_attach(struct usb_serial *serial)
{
	int 						i = 0;
	int							ret = 0;
	struct usb_serial_port*		serport;
	struct usbrsa_port_private*	priv = NULL;

	printk("%s start",__func__);
	dev_dbg(&serial->dev->dev,"%s", __func__);
	if (!serial)
	{
		dev_err(&serial->dev->dev,"%s(): Invalid handler", __func__);
		return -ENODEV;
	}
	
	for (i = 0; i < serial->num_ports; i++) 
	{
		serport = serial->port[i];
	
		// allocate struct for driver's private data
		priv = kmalloc(sizeof(struct usbrsa_port_private), GFP_KERNEL);
        if (priv == NULL) 
        {
                dev_err(&serport->dev,
                        "%s: Out of memory for port structures\n",
                        serial->type->description);
                goto out_no_private;
        }
        
        usb_set_serial_port_data(serial->port[i],priv);
       	priv->parent_serial  = serial;
        priv->parent_port    = serport;
        priv->read_running 	 = USBRSA_READ_STOP;
        priv->urb_pool_size  = NUM_URBS;
        priv->nofTxMaxBytes  = USBRSA_TX_MAX_BYTES;
        mutex_init(&priv->mutex);

        init_waitqueue_head(&priv->wait_flag);

        // allocate  urbs
        printk("%s about to enter 'usbrsa_allocate_write_urbs'",__func__);
        ret = usbrsa_allocate_write_urbs(priv);
        if (ret != 0)
        	goto out_no_alloc_write;
        printk("%s allocate_read_urbs",__func__);
        ret = allocate_read_urbs(priv);
        if (ret != 0)
         	goto out_no_alloc_read;
        printk("%s allocate_baudrate_lcr_urb",__func__);
        ret = allocate_baudrate_lcr_urb(priv);
        if (ret != 0)
         	goto out_no_alloc_baudrate_lcr;
        printk("%s allocate_reset_urb",__func__);
        ret = allocate_reset_urb(priv);
        if (ret != 0)
         	goto out_no_alloc_reset;
        printk("%s allocate_status_urb",__func__);
        ret = allocate_status_urb(priv);
        if (ret != 0)
         	goto out_no_alloc_status;
        printk("%s allocate_mcr_urb",__func__);
        ret = allocate_mcr_urb(priv);
        if (ret != 0)
        	goto out_no_alloc_mcr;
        ret = allocate_msr_urb(priv);
        if (ret != 0)
        	goto out_no_alloc_msr;

        printk("%s send_reset",__func__);
        // sent reset
        ret = send_reset(serport);
        if (ret != 0)
        {
    		dev_err(&serport->dev, "%s(): Failed to issue reset command. "
    				" Error=%d\n", __func__, ret);
    		ret = ENODEV;
    		goto out_no_device;
        }
        printk("%s end -- debug section",__func__);
        // debug

        for (i=0; i < 5 ; i++)
        {
    	priv->mcr &= ~(USBRSA_ST16C550_MCR_DTR );
    	ret = send_mcr_register(serport);
    	mdelay(200);
    	priv->mcr |= ~(USBRSA_ST16C550_MCR_DTR );
    	ret = send_mcr_register(serport);
    	mdelay(200);
    	priv->mcr &= ~(USBRSA_ST16C550_MCR_DTR );
    	ret = send_mcr_register(serport);
    	mdelay(200);
    	priv->mcr |= ~(USBRSA_ST16C550_MCR_DTR );
    	ret = send_mcr_register(serport);
    	mdelay(200);
    	priv->mcr &= ~(USBRSA_ST16C550_MCR_DTR );
    	ret = send_mcr_register(serport);
    	mdelay(200);
        }
    	//debug end
        printk("%s end",__func__);
        return 0; 
	}
	while (i >= 0)
	{
		priv = usb_get_serial_port_data(serial->port[i]);
		
		out_no_device:
			release_msr_urb(priv);
		out_no_alloc_msr:
			release_mcr_urb(priv);
		out_no_alloc_mcr:
			release_status_urb(priv);
		out_no_alloc_status:
			release_reset_urb(priv);
		out_no_alloc_reset:
			release_baudrate_lcr_urb(priv);
		out_no_alloc_baudrate_lcr:
			release_read_urbs(priv);
		out_no_alloc_read:
			release_write_urbs(priv);
		out_no_alloc_write:
			kfree(priv);
		out_no_private:
		i--;
	}
	printk("%s end error",__func__);
	return ret;
}

//////////////////////////////////////////////////////////////////////
// Name: usbrsa_release
// Purpose: Cleans up private data structures of driver
//        
//
//////////////////////////////////////////////////////////////////////
static void  usbrsa_release(struct usb_serial *serial)
{
	int 						i;
	struct usbrsa_port_private*	priv;

	printk("%s",__func__);
	dev_dbg(&serial->dev->dev,"%s()", __func__);
	
	
	for (i = 0; i < serial->num_ports; i++) 
	{

		priv = usb_get_serial_port_data(serial->port[i]);
		release_msr_urb(priv);
		release_mcr_urb(priv);
		release_status_urb(priv);
		release_reset_urb(priv);
		release_baudrate_lcr_urb(priv);
		release_write_urbs(priv);
		release_read_urbs(priv);
		kfree(priv);
	}
}

///////////////////////////////////////////////////////////////////////
// Name: usbrsa_probe
// Purpose:
//
//
//
//////////////////////////////////////////////////////////////////////
static int usbrsa_probe(struct usb_serial_port *port)
{
	printk("%s",__func__);
//	struct usbrsa_private *info;
//
//	info = kzalloc(sizeof(*info), GFP_KERNEL);
//	if (!info)
//		return -ENOMEM;
//
//	usb_set_serial_port_data(port, info);

	return 0;
}

///////////////////////////////////////////////////////////////////////
// Name: usbrsa_remove
// Purpose:
//
//
//
//////////////////////////////////////////////////////////////////////
static int usbrsa_remove(struct usb_serial_port *port)
{
	printk("%s",__func__);
//	struct usbrsa_private *info;
//
//	info = usb_get_serial_port_data(port);
//	kfree(info);

	return 0;
}


///////////////////////////////////////////////////////////////////////
// Name: usbrsa_open   
// Purpose: Open USB device. Sets termios, tty, and starts to sent 
//          status queries to EP3IN. 
//        
//
//////////////////////////////////////////////////////////////////////
static int  usbrsa_open(struct tty_struct *tty,
                        struct usb_serial_port *port)
{
	struct ktermios 			tmp_termios;
	int 						retval;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	unsigned long				flags;
	
	printk("%s",__func__);
	dev_dbg(&port->dev,"%s - port %d", __func__,port->port_number);

	
	// flush buffer of USB-RSA by sending reset 
	retval = send_reset(port);

	if (retval != 0)
	{
		dev_err(&port->dev, "%s(): usb_submit_urb() failed"
				" with error %d\n", __func__, retval);
		return ENODEV;
	}

	
	 if (tty)
	 {
		 // usbrsa_set_termios will sent to endpoint 2 of the USB-RSA thereby 
		 // turning on the RX interrupt 
		 usbrsa_set_termios(tty, port, &tmp_termios);
	 }
	 

	 // start reading from USB-RSA
	 spin_lock_irqsave(&priv->lock, flags);
	 if (!(test_bit(LOCK_STATUS,&priv->urb_lock)))
	 {
		 set_bit(LOCK_STATUS,&priv->urb_lock);
		 // enable continuous reading
		 priv->read_running = USBRSA_READ_RUNNING;
		 spin_unlock_irqrestore(&priv->lock, flags);
		 retval = usb_submit_urb(priv->status_urb,GFP_KERNEL);
	 }
	 else
	 {
		 spin_unlock_irqrestore(&priv->lock, flags);
	 }

	 dev_dbg(&port->dev,"%s - port %d: leaving", __func__, port->port_number);
	 printk("%s - port=%d",__func__,port->port_number);
	 return 0;
}

/////////////////////////////////////////////////////////////////////
// Name: usbrsa_close
// Purpose: closes USB-RSA device                       
//        
//
//////////////////////////////////////////////////////////////////////
static void usbrsa_close(struct usb_serial_port *port)
{
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	int 						retries = 3;
	unsigned long 				flags;
	int							retval;


	printk("%s",__func__);
	dev_dbg(&port->dev,"%s - port %d", __func__, port->port_number);
	
	// turn off  RX interrupt of USB-RSA by sending to EP5
	while(retries > 0)
	{
		// turn off RX interrupt of USB-RSA by sending to EP5.
		 retval = send_baudrate_lcr_register(priv->c_flag, priv->baudrate,
					 EP5OUT, port);
		 
		 if (retval != 0)
		 {
			 dev_err(&port->dev, "%s(): usb_submit_urb() failed"
					 " with error %d\n", __func__, retval);
			
		 }
		 retries--;
	}
	
	// disable continuous reading
	spin_lock_irqsave(&priv->lock, flags);
	priv->read_running = USBRSA_READ_STOP;
	spin_unlock_irqrestore(&priv->lock, flags);
	
	return;
}

/////////////////////////////////////////////////////////////////////
// Name: usbrsa_init_termios
// Purpose: Initialize termios of tty                       
//        
//
//////////////////////////////////////////////////////////////////////
static void usbrsa_init_termios(struct tty_struct *tty)
{

	dev_dbg(tty->dev,"%s()", __func__);

	tty->termios = tty_std_termios;
	tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
	tty->termios.c_ispeed = 9600;
	tty->termios.c_ospeed = 9600;
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_set_termios
// Purpose: Initialize USB-RSA with terminal settings                       
//        
//
//////////////////////////////////////////////////////////////////////
static void usbrsa_set_termios(struct tty_struct *tty,
        struct usb_serial_port *port, struct ktermios *old_termios)
{
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	unsigned int 				cflag;
	speed_t						baud_rate;
	int 						retval;
	int							retries = 3;
	
	dev_dbg(&port->dev,"%s - port %d", __func__,port->port_number);
	
	
	if (!tty) 
	{
		dev_err(&port->dev,"%s(): no tty structures", __func__);
		return;
	}
	
	if (I_IXOFF(tty) || I_IXON(tty)) 
	{
		priv->xon = START_CHAR(tty);
		priv->xoff = STOP_CHAR(tty);
		dev_dbg(&port->dev,"%s - XON = %2x, XOFF = %2x",
				__func__, priv->xon, priv->xoff );
	}
	cflag = tty->termios.c_cflag;
	priv->c_flag = cflag;
	/* get the baud rate wanted */
	baud_rate	= tty_get_baud_rate(tty);
	priv->baudrate = baud_rate;
	dev_dbg(&port->dev,"%s - baudrate = %u",__func__, baud_rate );
	
	 /* if baud rate is B0, drop DTR */
	 if ((baud_rate & CBAUD) == B0)
	 {
		priv->mcr &= ~(USBRSA_ST16C550_MCR_DTR );
		 retval = send_mcr_register(port);
		 if (retval != 0)
			 //return -ENODEV;
			 return;
	 }
	 
	 while(retries > 0)
	 {
		 retval = send_baudrate_lcr_register(cflag, baud_rate,
				 EP2OUT, port);

		 if (retval != 0)
		 {
			 dev_err(&port->dev, "%s(): send_baudrate_lcr_register() failed"
					 " with error %d\n", __func__, retval);
			 retries--;
		 }
		 else
		 {
			 // bail out
			 retries = 0;
		 }

	 }
}

/////////////////////////////////////////////////////////////////////
// Name: usbrsa_write
// Purpose: copies data from user space, packs it into urbs, and sents
//          urbs downstream to USB-RSA
//        
//
//////////////////////////////////////////////////////////////////////
static int  usbrsa_write(struct tty_struct *tty,
                        struct usb_serial_port *port,
                        const unsigned char *buf, int count)
{
	struct usb_serial*			serial = port->serial;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	unsigned long				flags;
	int 						retval;
	int							nof_bytes_to_be_sent;
	int 						bytes;   
	int							urb_index;
	int							sent = 0;
	struct urb*					urb;
	
	dev_dbg(&port->dev,"%s - port %d START", __func__,port->port_number);

	while (count > 0)
	{
		// transfer only as many bytes as USB-RSA can take
		nof_bytes_to_be_sent = (count > priv->nofTxBytesFree) ? 
				priv->nofTxBytesFree : count;
		dev_dbg(&port->dev,"%s - port %d; bytes=%d := count=%d : nofTxBytesFree=%d",
				__func__, port->port_number,
				nof_bytes_to_be_sent,count,
				priv->nofTxBytesFree);
		
		while (nof_bytes_to_be_sent > 0)
		{
			// check for empty urb in write pool
			spin_lock_irqsave(&priv->lock, flags);
			urb_index = 
					find_first_zero_bit(&priv->write_urb_pool_lock,
							priv->urb_pool_size);

			dev_dbg(&port->dev,"%s - poolsize  %d, urb_index %d\n",__func__,
priv->urb_pool_size,urb_index);
			if (urb_index >= priv->urb_pool_size)
			{
				// no more urbs available
				spin_unlock_irqrestore(&priv->lock, flags);
				goto out_write_no_urbs;
			}

			dev_dbg(&port->dev,"%s - port %d;URB allocated=%d",__func__,
port->port_number,urb_index);
			// reserve urb
			set_bit(urb_index, &priv->write_urb_pool_lock);
			urb = priv->write_urb_pool[urb_index];

			spin_unlock_irqrestore(&priv->lock, flags);

			// copy data from userspace into urb transfer buffer
			bytes = (nof_bytes_to_be_sent > port->bulk_out_size) ?
					port->bulk_out_size : nof_bytes_to_be_sent;
			memcpy(urb->transfer_buffer, buf + sent, bytes);

			dev_dbg(&port->dev,"%s - port %d;bytes in urb=%d",__func__,
port->port_number, bytes);
			usb_serial_debug_data( &port->dev,
					__func__, bytes, urb->transfer_buffer);
			urb->dev = serial->dev;
			urb->transfer_buffer_length = bytes;

			// sent urb to USB-RSA
			retval = usb_submit_urb(urb, GFP_ATOMIC);

			if (retval) 
			{
				// return urb to pool
				spin_lock_irqsave(&priv->lock, flags);
				clear_bit(urb_index, &priv->write_urb_pool_lock);
				spin_unlock_irqrestore(&priv->lock, flags);
				dev_err(&port->dev,
						"%s - failed submitting write urb, error %d\n",
						__func__, retval);
				sent = retval;

				// bail out
				goto out_write_submit_failed;
			} 
			else 
			{
				sent += bytes;
				count -= bytes;
				nof_bytes_to_be_sent -= bytes;
				dev_dbg(&port->dev,"%s - port %d;sent=%d;count=%d;
nof_bytes_to_be_sent=%d;bytes=%d",
						__func__, port->port_number, sent,count, nof_bytes_to_be_sent,
						bytes);
			} 
		}
	}
	out_write_no_urbs:
	out_write_submit_failed:
	dev_dbg(&port->dev,"%s() End - sent=%d\n",__func__,sent);
	return sent;
}

/////////////////////////////////////////////////////////////////////
// Name: usbrsa_ioctl
// Purpose: copies data from user space, packs it into urbs, and sents
//          urbs downstream to USB-RSA
//        
//
//////////////////////////////////////////////////////////////////////
static int  usbrsa_ioctl(struct tty_struct *tty,
                        unsigned int cmd, unsigned long arg)
{ 
  	  
	struct usb_serial_port *port = tty->driver_data;

	dev_dbg(&port->dev,"%s() cmd 0x%.4x", __func__, cmd);

	switch (cmd) 
	{	  
	case TIOCGSERIAL:
		return get_serial_info(port,
				(struct serial_struct __user *) arg);
		break;
	case TIOCMIWAIT:
		dev_dbg(&port->dev,"%s(): TIOCMIWAIT", __func__);
		return wait_modem_info(port, arg);
		break;
	case TCGETS:
		dev_dbg(&port->dev,"%s(): TCGETS -- not implemented", __func__);
		break;
	case TCSETS:
		dev_dbg(&port->dev,"%s(): TCSETS -- not implemented", __func__);
		break;
	case TCFLSH:
		dev_dbg(&port->dev,"%s(): TCFLSH -- not implemented", __func__);
		break;
	default:
		dev_dbg(&port->dev,"%s(): 0x%04x not supported", __func__, cmd);
		break;
	}
	return -ENOIOCTLCMD; 
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_write_room
// Purpose: Returns fill level of the tx queue. The tx queue consists
//          of 2 parts: A pool of URBs, and a buffer in the USB-RSA.
//			Mostly, the fill level of the pool will be returned.
//			Only if there is less memory available in the USB-RSA's
//			buffer than in the the pool, the fill level of the USB-RSA
//			will be returned to prevent clogging the tx queue.
//          
//        
//
//////////////////////////////////////////////////////////////////////
static int usbrsa_write_room(struct tty_struct *tty)
{
	struct usb_serial_port*		port = tty->driver_data;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	int 						room = 0;
	int 						i = 0;
	long						t;
	unsigned long 				flags;
	int							retval;
	
	dev_dbg(&port->dev,"%s - port %d", __func__,port->port_number);
	
	// if no current fill level information is available, issue status update
	if (priv->read_running != USBRSA_READ_RUNNING)
	{
		// start reading from USB-RSA
		spin_lock_irqsave(&priv->lock, flags);
		if (!(test_bit(LOCK_STATUS,&priv->urb_lock)))
		{
			set_bit(LOCK_STATUS,&priv->urb_lock);
			spin_unlock_irqrestore(&priv->lock, flags);
			retval = usb_submit_urb(priv->status_urb,GFP_KERNEL);
			if (retval != 0)
			{
				return -ENODEV;
			}
			/* wait for the command to complete 
			 * (waits, until timeout or condition==true) */
			t = wait_event_timeout(priv->wait_flag,
					(test_bit(LOCK_STATUS, &priv->urb_lock)==0), COMMAND_TIMEOUT);
			if (!t)
			{
				// fetching of msr 
				usb_kill_urb(priv->status_urb);
				dev_dbg(&port->dev,"%s - status urb timed out.", __func__);
				return -ETIMEDOUT;
				//goto send_reset_exit;
			}
		}
		else
		{
			spin_unlock_irqrestore(&priv->lock, flags);
		}
	}
	


	spin_lock_irqsave(&priv->lock, flags);
	for (i=0;i<priv->urb_pool_size;i++)
	{
		if (test_bit(i,&priv->write_urb_pool_lock) == 0)
		{
			room++;
		}
	}
	spin_unlock_irqrestore(&priv->lock, flags);
	
	//compute bytes  available in write URB pool
	room *= port->bulk_out_size;

	dev_dbg(&port->dev,"%s(): Pool=%d;usbrsa.tx=%d", __func__,
room,priv->nofTxBytesFree);
	// if usbrsa has fewer bytes available than URB pool return available bytes
in USBRSA
	room = (room > priv->nofTxBytesFree) ?  priv->nofTxBytesFree : room;

	dev_dbg(&port->dev,"%s(): %d", __func__, room);
	return room;
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_chars_in_buffer
// Purpose: Return the number of bytes still waiting to be sent
// 			stored in the USBRSA as well as in the buffer of the
//			driver (URB pool).
//			The tty layer thereby calls the close function only
//			after all bytes are sent.
//          
//        
//
// todo: When should the bytes remaining in the buffer, and
//////////////////////////////////////////////////////////////////////
static int usbrsa_chars_in_buffer(struct tty_struct *tty)
{
	struct usb_serial_port*		port = tty->driver_data;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	int 						chars = 0;
	int 						i = 0;
	unsigned long				flags;
	long						t;
	int							retval;
	
	dev_dbg(&port->dev,"%s - port %d", __func__,port->port_number);
	
	if (priv->read_running != USBRSA_READ_RUNNING)
	{
		// start reading from USB-RSA
		spin_lock_irqsave(&priv->lock, flags);
		if (!(test_bit(LOCK_STATUS,&priv->urb_lock)))
		{
			set_bit(LOCK_STATUS,&priv->urb_lock);
			spin_unlock_irqrestore(&priv->lock, flags);
			retval = usb_submit_urb(priv->status_urb,GFP_KERNEL);
			if (retval != 0)
			{
				chars =  -ENODEV;
				goto chars_in_buffer_exit;
			}
			/* wait for the command to complete 
			 * (waits, until timeout or condition==true) */
			t = wait_event_timeout(priv->wait_flag,
					(test_bit(LOCK_STATUS, &priv->urb_lock)==0), COMMAND_TIMEOUT);
			if (!t)
			{
				// fetching of msr 
				usb_kill_urb(priv->status_urb);
				dev_dbg(&port->dev,"%s - status urb timed out.", __func__);
				chars =  -ETIMEDOUT;
				goto chars_in_buffer_exit;
			}
		}
		else
		{
			spin_unlock_irqrestore(&priv->lock, flags);
		}
	}
	spin_lock_irqsave(&priv->lock, flags);

	//bytes in TX buffer of USBRSA
	chars = priv->nofTxMaxBytes - priv->nofTxBytesFree;

	// account for URBs in the pool and not yet transferred to the USB-RSA
	for (i=0;i<priv->urb_pool_size;i++)
	{
		if (test_bit(i,&priv->write_urb_pool_lock) != 0)
		{
			chars += priv->write_urb_pool[i]->transfer_buffer_length;
		}
	}

	spin_unlock_irqrestore(&priv->lock, flags);

	chars_in_buffer_exit:
	dev_dbg(&port->dev,"%s(): %d", __func__, chars);
	return chars;
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_throttle
// Purpose: 
//          
//        
//
/////////////////////////////////////////////////////////////////////
static void usbrsa_throttle(struct tty_struct *tty) 
{ 
	struct usb_serial_port*		port = tty->driver_data;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	int status;


	dev_dbg(&port->dev,"%s - port %d", __func__,port->port_number);


	/* if we are implementing XON/XOFF, send the stop character */
	if (I_IXOFF(tty)) 
	{
		unsigned char stop_char = STOP_CHAR(tty);
		status = usbrsa_write(tty, port, &stop_char, 1);
		if (status <= 0)
			return;
	}

	/* if we are implementing RTS/CTS, toggle that line */
	if (tty->termios.c_cflag & CRTSCTS)
	{
		priv->mcr &= ~USBRSA_ST16C550_MCR_RTS;
		status = send_mcr_register(port);
		if (status != 0)
			return;
	}
} 


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_unthrottle
// Purpose: 
//          
//        
//
/////////////////////////////////////////////////////////////////////
static void usbrsa_unthrottle(struct tty_struct *tty) 
{ 
	struct usb_serial_port*		port = tty->driver_data;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	int status;

	dev_dbg(&port->dev,"%s - port %d", __func__,port->port_number);

	/* if we are implementing XON/XOFF, send the start character */
	if (I_IXOFF(tty)) 
	{
		unsigned char start_char = START_CHAR(tty);
		status = usbrsa_write(tty, port, &start_char, 1);
		if (status <= 0)
			return;
	}
	/* if we are implementing RTS/CTS, toggle that line */
	if (tty->termios.c_cflag & CRTSCTS)
	{
		priv->mcr |= USBRSA_ST16C550_MCR_RTS;
		send_mcr_register(port);
	}
} 


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_tiocmset
// Purpose: 
//          
//        
//
/////////////////////////////////////////////////////////////////////
static int  usbrsa_tiocmset(struct tty_struct *tty,
        unsigned int set, unsigned int clear)
{ 
	struct usb_serial_port*		port = tty->driver_data;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	unsigned long 				flags;
	int							retval;
	
	if (port == NULL)
		return -ENODEV;

	dev_dbg(&port->dev,"%s - port %d", __func__,port->port_number);
	
	spin_lock_irqsave(&priv->lock, flags);
	
	if (set & TIOCM_RTS)
		priv->mcr |= USBRSA_ST16C550_MCR_RTS;
	if (set & TIOCM_DTR)
		priv->mcr |= USBRSA_ST16C550_MCR_DTR;
	if (set & TIOCM_LOOP)
	{
		priv->mcr |= USBRSA_ST16C550_MCR_DIAG;
		priv->lloop = 1;
	}

	if (clear & TIOCM_RTS)
		priv->mcr &= ~USBRSA_ST16C550_MCR_RTS;
	if (clear & TIOCM_DTR)
		priv->mcr &= ~USBRSA_ST16C550_MCR_DTR;
	if (clear & TIOCM_LOOP)
	{
		priv->mcr &= ~USBRSA_ST16C550_MCR_DIAG;
		priv->lloop = 0;
	}
	
	// bring OP1 and OP2 to defined states 
	priv->mcr &= ~USBRSA_ST16C550_MCR_OP1;  
	priv->mcr &= ~USBRSA_ST16C550_MCR_OP2;
	
	spin_unlock_irqrestore(&priv->lock, flags);


	retval = send_mcr_register(port);
	dev_dbg(&port->dev,"%s: %d=send_mcr_register",__func__,retval);
	return(retval);
} 


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_tiocmget
// Purpose: 
//          
//        
//
/////////////////////////////////////////////////////////////////////
static int  usbrsa_tiocmget(struct tty_struct *tty)
{ 
	struct usb_serial_port*		port = tty->driver_data;
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	int							retval;
	
	
	if (port == NULL)
		return -ENODEV;

	dev_dbg(&port->dev,"%s() - port %d",__func__, port->port_number);
	
	retval = fetch_msr_register(port);
	dev_dbg(&port->dev,"%s: %d=fetch_msr_register",__func__,retval);

	if (retval == 0)
	{
		retval = ((priv->mcr &  USBRSA_ST16C550_MCR_DTR) ? TIOCM_DTR : 0)
			| ((priv->mcr &  USBRSA_ST16C550_MCR_RTS) ? TIOCM_RTS : 0)
			| ((priv->mcr &  USBRSA_ST16C550_MCR_DIAG) ? TIOCM_LOOP : 0)
			| ((priv->msr &  USBRSA_ST16C550_MSR_CTS) ? TIOCM_CTS : 0)
			| ((priv->msr &  USBRSA_ST16C550_MSR_CD) ? TIOCM_CD : 0)
			| ((priv->msr &  USBRSA_ST16C550_MSR_RI) ? TIOCM_RI : 0)
			| ((priv->msr &  USBRSA_ST16C550_MSR_DSR) ? TIOCM_DSR : 0);

	}
	
	return(retval);
} 


////////////////////////////////////////////////////////////////////////
//                C O M P L E T I O N   H A N D L E R S
////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////
// Name: usbrsa_baudrate_lcr_callback
// Purpose:                     
//        
//
//////////////////////////////////////////////////////////////////////
static void usbrsa_baudrate_lcr_callback(struct urb *urb)
{
	struct usb_serial_port* 	port 	= urb->context;
	struct usbrsa_port_private*	priv 	= usb_get_serial_port_data(port);
	int 						status	= urb->status;
	unsigned long				flags;

	dev_dbg(&port->dev,"%s() - port = %d, ep =0x%x", __func__, port->port_number,
			priv->baudrate_lcr_urb->pipe);
	
	// unlock urb
	spin_lock_irqsave(&priv->lock, flags);
	clear_bit(LOCK_BAUDRATE_LCR,&priv->urb_lock);
	spin_unlock_irqrestore(&priv->lock, flags);
	
	if (status)
	{
		dev_dbg(&port->dev,"%s(): nonzero urb status: 0x%d", __func__, status);
		
	}
	wake_up(&priv->wait_flag);
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_reset_callback
// Purpose:                     
//        
//
//////////////////////////////////////////////////////////////////////
static void  usbrsa_reset_callback(struct urb *urb)
{
	struct usb_serial_port* 	port 	= urb->context;
	struct usbrsa_port_private*	priv 	= usb_get_serial_port_data(port);
	int 						status	= urb->status;
	unsigned long				flags;

	dev_dbg(&port->dev,"%s() - port = %d, ep =0x%x", __func__, port->port_number,
			priv->reset_urb->pipe);
	
	// unlock urb
	spin_lock_irqsave(&priv->lock, flags);
	clear_bit(LOCK_RESET,&priv->urb_lock);
	spin_unlock_irqrestore(&priv->lock, flags);
	
	if (status)
	{
		dev_dbg(&port->dev,"%s(): nonzero urb status: 0x%d", __func__,status);
	
	}
	wake_up(&priv->wait_flag);	
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_status_callback
// Purpose:                     
//        
//
//////////////////////////////////////////////////////////////////////
static void  usbrsa_status_callback(struct urb *urb)
{
	struct usb_serial_port* 	port 	= urb->context;
	struct usbrsa_port_private*	priv 	= usb_get_serial_port_data(port);
	int 						status	= urb->status;
	__u8*						ep3in   = urb->transfer_buffer;
	unsigned long				flags;
	int							retval;
	int 						read_urbs_needed = 0;
	int							urb_index;
	unsigned int				i;

//	dev_dbg(&port->dev,"%s() - port = %d, ep =0x%x", __func__, port->port_number,
//			priv->status_urb->pipe);

	
	spin_lock_irqsave(&priv->lock, flags);
	// read payload data of urb, i.e. bytes received and bytes 
	// available in tx queue of USB-RSA
	priv->nofTxBytesFree	= ep3in[0] + (256 * ep3in[1]);
	priv->nofRxBytesReceived= ep3in[2] + (256 * ep3in[3]);
	// unlock urb
	clear_bit(LOCK_STATUS,&priv->urb_lock);
	spin_unlock_irqrestore(&priv->lock, flags);

	
	
	// if coutinuous read enabled, either
	// -- issue next status request (USBRSA did not receive 
	//    anything)
	// -- issue a read urb (if rx queue is filled)
	if (priv->read_running == USBRSA_READ_RUNNING)
	{
		if(priv->nofRxBytesReceived != 0)
		{
			// USB-RSA received data 	

			// compute urbs needed
			read_urbs_needed  = priv->nofRxBytesReceived/port->bulk_in_size;
			if (priv->nofRxBytesReceived%port->bulk_in_size)
				read_urbs_needed++;
			dev_dbg(&port->dev,"%s(): EP1IN: rx=%d, urbs_needed=%d,size_of_urb=%d",
					__func__,
					priv->nofRxBytesReceived,
					read_urbs_needed,
					port->bulk_in_size);


			// issue read urbs
			// todo: issue more than one read urb. Will this work ?
			if (read_urbs_needed > priv->urb_pool_size)
				read_urbs_needed = priv->urb_pool_size;
			for(i=0; i < read_urbs_needed;i++ )
			{
				spin_lock_irqsave(&priv->lock, flags);
				urb_index = find_first_zero_bit(&priv->read_urb_pool_lock,
						priv->urb_pool_size);
				if (urb_index < priv->urb_pool_size)
				{
					set_bit(urb_index,&priv->read_urb_pool_lock);
					spin_unlock_irqrestore(&priv->lock, flags);

					// submit read
					dev_dbg(&port->dev,"%s(): submit read urb[%d]", __func__,urb_index );
					retval = usb_submit_urb(
							priv->read_urb_pool[urb_index],GFP_KERNEL);
					if (retval)
					{
						// return urb to read pool
						spin_lock_irqsave(&priv->lock, flags);
						clear_bit(urb_index,&priv->read_urb_pool_lock);
						spin_unlock_irqrestore(&priv->lock, flags);
						dev_dbg(&port->dev,"%s(): nonzero urb status: 0x%d", __func__, status);
						return;
					}
				}
				else
				{
					spin_unlock_irqrestore(&priv->lock, flags);
					i = read_urbs_needed;
				}
			}
		}
		spin_lock_irqsave(&priv->lock, flags);
		set_bit(LOCK_STATUS,&priv->urb_lock);
		spin_unlock_irqrestore(&priv->lock, flags);
		retval = usb_submit_urb(priv->status_urb,GFP_KERNEL);
		if (retval != 0)
		{
			dev_err(&port->dev, "%s(): usb_submit_urb() failed"
					" with error %d\n", __func__, retval);
			spin_lock_irqsave(&priv->lock, flags);
			clear_bit(LOCK_STATUS,&priv->urb_lock);
			spin_unlock_irqrestore(&priv->lock, flags);
		}
	}
	else
	{
		// reading from device ceased -- do nothing
		dev_dbg(&port->dev,"%s(): reading stopped: tx_free=%d;rx_recv=%d",
				__func__, priv->nofTxBytesFree,priv->nofRxBytesReceived);
	}
	if (status)
	{
		dev_dbg(&port->dev,"%s(): nonzero urb status: 0x%d", __func__, status);
		
	}
	if (priv->read_running == USBRSA_READ_RUNNING)
	{
		usb_serial_port_softint(port);
	}
	else
	{
		wake_up(&priv->wait_flag);	
	}
	return;
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_write_callback
// Purpose:                     
//        
//
//////////////////////////////////////////////////////////////////////
static void  usbrsa_write_callback(struct urb *urb)
{
	struct usb_serial_port* 	port 	= urb->context;
	struct usbrsa_port_private*	priv 	= usb_get_serial_port_data(port);
	int 						status	= urb->status;
	unsigned long				flags;
	int							urb_index;
	
	dev_dbg(&port->dev,"%s() - port = %d, ep =0x%x", __func__, port->port_number,
				priv->status_urb->pipe);
	
	usb_serial_debug_data( &port->dev, __func__,
				urb->actual_length, urb->transfer_buffer);
	// find urb in write pool
	for (urb_index=0; urb_index < priv->urb_pool_size;urb_index++)
	{
		if (urb == priv->write_urb_pool[urb_index])
		{
			// urb found; return urb to pool
			spin_lock_irqsave(&priv->lock, flags);
			clear_bit(urb_index, &priv->write_urb_pool_lock);
			spin_unlock_irqrestore(&priv->lock, flags);
			break;
		}
	}
	
	if (urb != priv->write_urb_pool[urb_index])
	{
		// urb not found -- this points to a bug
		dev_err(&port->dev, "%s(): urb not in pool",
				__func__);
	}
	dev_dbg(&port->dev,"%s(): Returned URB %d",__func__, urb_index);
	if (status)
	{
		dev_dbg(&port->dev,"%s(): nonzero urb status: 0x%d", __func__, status);
	}
	usb_serial_port_softint(port);
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_read_callback
// Purpose:                     
//        
//
//////////////////////////////////////////////////////////////////////
static void  usbrsa_read_callback(struct urb *urb)
{
	struct usb_serial_port* 	port 	= urb->context;
	struct usbrsa_port_private*	priv 	= usb_get_serial_port_data(port);
	struct tty_struct*			tty;
	int 						status	= urb->status;
	unsigned char*				data 	= urb->transfer_buffer;
	int							urb_index;
	unsigned long				flags;
	
	dev_dbg(&port->dev,"%s() - port = %d, ep=0x%x", __func__, port->port_number,
				priv->status_urb->pipe);
	usb_serial_debug_data( &port->dev, __func__,
			urb->actual_length, data);

	
	// find urb in read pool
	for (urb_index=0; urb_index < priv->urb_pool_size;urb_index++)
	{
		if (urb == priv->read_urb_pool[urb_index])
		{
			// urb found; 
		
			break;
		}
	}
	if (urb != priv->read_urb_pool[urb_index])
	{
		// urb not found -- this points to a bug
		dev_err(&port->dev, "%s(): urb not in pool",
				__func__);
		return;
	}
	dev_dbg(&port->dev,"%s() - port = %d: urb_index=%d",
			__func__, port->port_number,urb_index);

	tty = tty_port_tty_get(&port->port);
	if (tty != NULL && urb->actual_length > 0) {
		tty_insert_flip_string(tty->port, data, urb->actual_length);
		tty_flip_buffer_push(tty->port);
	}
	tty_kref_put(tty);

	// return urb to pool
	dev_dbg(&port->dev,"%s: return URB %d",__func__,urb_index);
	spin_lock_irqsave(&priv->lock, flags);
	clear_bit(urb_index, &priv->read_urb_pool_lock);
	spin_unlock_irqrestore(&priv->lock, flags);
	if (status)
	{
		dev_dbg(&port->dev,"%s: nonzero urb status: 0x%d", __func__, status);
	}
	usb_serial_port_softint(port);
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_mcr_callback
// Purpose:                     
//        
//
//////////////////////////////////////////////////////////////////////
static void usbrsa_mcr_callback(struct urb *urb)
{
	struct usb_serial_port* 	port 	= urb->context;
	struct usbrsa_port_private*	priv 	= usb_get_serial_port_data(port);
	int 						status	= urb->status;
	unsigned long				flags;
	
	dev_dbg(&port->dev,"%s() - port = %d, ep =0x%x, status=%d", __func__,
port->port_number,
					priv->status_urb->pipe,status);
	
	spin_lock_irqsave(&priv->lock, flags);
	clear_bit(LOCK_MCR,&priv->urb_lock);
	spin_unlock_irqrestore(&priv->lock, flags);

	
	if (status)
	{
		dev_dbg(&port->dev,"%s: nonzero urb status: 0x%d", __func__, status);
	}
	wake_up(&priv->wait_flag);		
}


/////////////////////////////////////////////////////////////////////
// Name: usbrsa_msr_callback
// Purpose:                     
//        
//
//////////////////////////////////////////////////////////////////////
static void usbrsa_msr_callback(struct urb *urb)
{
	struct usb_serial_port* 	port 	= urb->context;
	struct usbrsa_port_private*	priv 	= usb_get_serial_port_data(port);
	int 						status	= urb->status;
	unsigned long				flags;
	__u8						*msr;
	
	dev_dbg(&port->dev,"%s() - port = %d, ep =0x%x, status=%d", __func__,
port->port_number,
					priv->status_urb->pipe,status);
	
	spin_lock_irqsave(&priv->lock, flags);
	msr = urb->transfer_buffer;
	priv->msr = msr[0];
	clear_bit(LOCK_MSR,&priv->urb_lock);
	spin_unlock_irqrestore(&priv->lock, flags);

	dev_dbg(&port->dev,"%s() - msr =%02x",__func__,priv->msr);
	
	if (status)
	{
		dev_dbg(&port->dev,"%s: nonzero urb status: 0x%d", __func__, status);
	}
	wake_up(&priv->wait_flag);	
}


////////////////////////////////////////////////////////////////////////
//                  Help Functions 
////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// Name: usbrsa_allocate_write_urbs
// Purpose: Allocates and initializes pool of write urbs to EP1OUT
//        
//
//////////////////////////////////////////////////////////////////////
int usbrsa_allocate_write_urbs(struct usbrsa_port_private *priv_data)
{
	int i;
	unsigned long flags;
	
	printk("%s() Mark 1",__func__);
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	printk("%s() Mark 2",__func__);
	
	for(i=0;i<priv_data->urb_pool_size;i++)
	{
		printk("%s; i=%d",__func__,i);
		priv_data->write_urb_pool[i] = usb_alloc_urb(0,GFP_KERNEL);
		if (priv_data->write_urb_pool[i] == NULL)
		{
			goto out_alloc_write_urb_pool;
		}
		priv_data->write_urb_pool[i]->transfer_buffer =
				kmalloc(priv_data->parent_port->bulk_out_size,GFP_KERNEL);
		if (priv_data->write_urb_pool[i]->transfer_buffer == NULL)
		{
			goto out_alloc_write_urb_pool;
		}
		
		usb_fill_bulk_urb(priv_data->write_urb_pool[i], 
					priv_data->parent_serial->dev,
					usb_sndbulkpipe(priv_data->parent_serial->dev,EP1OUT),
					priv_data->write_urb_pool[i]->transfer_buffer, 
					priv_data->parent_port->bulk_out_size,
			        usbrsa_write_callback, priv_data->parent_port);
		
		printk("%s -- clear_bit",__func__);
		spin_lock_irqsave(&priv_data->lock,flags);
		clear_bit( i,&priv_data->write_urb_pool_lock);
		spin_unlock_irqrestore(&priv_data->lock,flags);
	}
	printk("%s end",__func__);
	return 0;
	
	out_alloc_write_urb_pool:
		while (i > 0)
		{
			if (priv_data->write_urb_pool[i] != NULL)
			{
				if (priv_data->write_urb_pool[i]->transfer_buffer != NULL)
				{
					kfree(priv_data->write_urb_pool[i]->transfer_buffer);
				}
				usb_free_urb(priv_data->write_urb_pool[i]);
			}
		}	
		printk("%s end error",__func__);
		return ENOMEM;
}

//////////////////////////////////////////////////////////////////////
// Name: allocate_read_urbs
// Purpose: Allocates and initializes pool of read urbs to EP1IN
//        
//
//////////////////////////////////////////////////////////////////////
int allocate_read_urbs(struct usbrsa_port_private *priv_data)
{
	int i;
	unsigned long flags;

	printk("%s start",__func__);
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);

	for(i=0;i<priv_data->urb_pool_size;i++)
	{
		priv_data->read_urb_pool[i] = usb_alloc_urb(0,GFP_KERNEL);
		if (priv_data->read_urb_pool[i] == NULL)
		{
			goto out_alloc_read_urb_pool;
		}
		priv_data->read_urb_pool[i]->transfer_buffer =
			kmalloc(priv_data->parent_port->bulk_in_size,GFP_KERNEL);
		if (priv_data->read_urb_pool[i]->transfer_buffer == NULL)
		{
			goto out_alloc_read_urb_pool;
		}
		
		usb_fill_bulk_urb(priv_data->read_urb_pool[i], 
					priv_data->parent_serial->dev,
					usb_rcvbulkpipe(priv_data->parent_serial->dev,EP1OUT),
					priv_data->read_urb_pool[i]->transfer_buffer, 
					priv_data->parent_port->bulk_in_size,
			        usbrsa_read_callback, priv_data->parent_port);
		
		spin_lock_irqsave(&priv_data->lock,flags);
		clear_bit( i,&priv_data->read_urb_pool_lock);
		spin_unlock_irqrestore(&priv_data->lock,flags);
	}
	printk("%s end",__func__);
	return 0;
	
	out_alloc_read_urb_pool:
		while (i > 0)
		{
			if (priv_data->read_urb_pool[i] != NULL)
			{
				if (priv_data->read_urb_pool[i]->transfer_buffer != NULL)
				{
					kfree(priv_data->read_urb_pool[i]->transfer_buffer);
				}
				usb_free_urb(priv_data->read_urb_pool[i]);
			}
		}	
		printk("%s end error",__func__);
		return ENOMEM;

}

//////////////////////////////////////////////////////////////////////
// Name: allocate_baudrate_lcr_urb
// Purpose: Allocates and initializes urb to set baudrate and LCR
//          to EP2OUT or EP5OUT
//        
//
//////////////////////////////////////////////////////////////////////
static int allocate_baudrate_lcr_urb(struct usbrsa_port_private *priv_data)
{
	unsigned long	flags;
	
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	
	priv_data->baudrate_lcr_urb=usb_alloc_urb(0,GFP_KERNEL);
	if (priv_data->baudrate_lcr_urb == NULL)
	{
		goto out_alloc_baudrate_lcr;
	}
	
	priv_data->baudrate_lcr_urb->transfer_buffer=kmalloc(EP3OUTLEN,GFP_KERNEL);
	if (priv_data->baudrate_lcr_urb->transfer_buffer == NULL)
	{
		goto out_alloc_baudrate_lcr;
	}
	
	// sent to EP2OUT by default thereby disabling  the rx handling of USB-RSA.
	// The USB-RSA does no longer process or forward the data it receives
	usb_fill_bulk_urb(priv_data->baudrate_lcr_urb, priv_data->parent_serial->dev,
			usb_sndbulkpipe(priv_data->parent_serial->dev,
			EP5OUT),
	        priv_data->baudrate_lcr_urb->transfer_buffer, EP5OUTLEN,
	        usbrsa_baudrate_lcr_callback, priv_data->parent_port);
	
	spin_lock_irqsave(&priv_data->lock,flags);
	clear_bit( LOCK_BAUDRATE_LCR,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	
	return 0;
	
out_alloc_baudrate_lcr:
	spin_lock_irqsave(&priv_data->lock,flags);
	set_bit( LOCK_BAUDRATE_LCR,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	dev_dbg(&(priv_data->parent_port->dev),"%s(): Could not allocate
memory",__func__);
	return ENOMEM;
}

//////////////////////////////////////////////////////////////////////
// Name: allocate_reset_urb
// Purpose: Allocates and initializes reset urb to EP4OUT
//        
//
//////////////////////////////////////////////////////////////////////
static int allocate_reset_urb(struct usbrsa_port_private *priv_data)
{
	unsigned long	flags;

	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	
	priv_data->reset_urb = usb_alloc_urb(0,GFP_KERNEL);
	if (priv_data->reset_urb == NULL)
	{
		goto out_alloc_reset;
	}
	
	priv_data->reset_urb->transfer_buffer = NULL;

	usb_fill_bulk_urb(priv_data->reset_urb, priv_data->parent_serial->dev,
			usb_sndbulkpipe(priv_data->parent_serial->dev,EP4OUT),
			priv_data->reset_urb->transfer_buffer, EP4OUTLEN,
	        usbrsa_reset_callback, priv_data->parent_port);
	
	spin_lock_irqsave(&priv_data->lock,flags);
	clear_bit( LOCK_RESET,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	
	return 0;
	
out_alloc_reset:
	spin_lock_irqsave(&priv_data->lock,flags);
	set_bit( LOCK_RESET,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	dev_dbg(&(priv_data->parent_port->dev),"%s(): Could not allocate
memory",__func__);
	return ENOMEM;
}

//////////////////////////////////////////////////////////////////////
// Name: allocate_status_urb
// Purpose: Allocates and initializes status urb from EP3IN
//        
//
//////////////////////////////////////////////////////////////////////
static int allocate_status_urb(struct usbrsa_port_private *priv_data)
{
	unsigned long	flags;
	
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	priv_data->status_urb = usb_alloc_urb(0,GFP_KERNEL);
	if (priv_data->status_urb == NULL)
	{
		goto out_alloc_status;
	}
	
	priv_data->status_urb->transfer_buffer=kmalloc(EP3INLEN,GFP_KERNEL);
	if (priv_data->status_urb->transfer_buffer == NULL)
	{
		goto out_alloc_status;
	}
		
	usb_fill_bulk_urb(priv_data->status_urb, priv_data->parent_serial->dev,
			usb_rcvbulkpipe(priv_data->parent_serial->dev,EP3IN),
			priv_data->status_urb->transfer_buffer, EP3INLEN,
	        usbrsa_status_callback, priv_data->parent_port);
	spin_lock_irqsave(&priv_data->lock,flags);
	clear_bit( LOCK_STATUS,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	
	return 0;
	
out_alloc_status:
	spin_lock_irqsave(&priv_data->lock,flags);
	set_bit( LOCK_STATUS,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	dev_dbg(&(priv_data->parent_port->dev),"%s(): Could not allocate
memory",__func__);
	return ENOMEM;
}


//////////////////////////////////////////////////////////////////////
// Name: allocate_mcr_urb
// Purpose: Allocates and initializes modem control register urb 
//			to EP3OUT        
//
//////////////////////////////////////////////////////////////////////
static int allocate_mcr_urb(struct usbrsa_port_private *priv_data)
{
	unsigned long	flags;
	
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	priv_data->mcr_urb = usb_alloc_urb(0,GFP_KERNEL);
	if (priv_data->mcr_urb == NULL)
	{
		goto out_alloc_mcr;
	}
	
	priv_data->mcr_urb->transfer_buffer=kmalloc(EP3OUTLEN,GFP_KERNEL);
	if (priv_data->mcr_urb->transfer_buffer == NULL)
	{
		goto out_alloc_mcr;
	}
		
	usb_fill_bulk_urb(priv_data->mcr_urb, priv_data->parent_serial->dev,
			usb_sndbulkpipe(priv_data->parent_serial->dev,EP3OUT),
			priv_data->mcr_urb->transfer_buffer, EP3OUTLEN,
	        usbrsa_mcr_callback, priv_data->parent_port);
	spin_lock_irqsave(&priv_data->lock,flags);
	clear_bit( LOCK_MCR,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	
	return 0;
	
out_alloc_mcr:
	spin_lock_irqsave(&priv_data->lock,flags);
	set_bit( LOCK_MCR,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	dev_dbg(&(priv_data->parent_port->dev),"%s(): Could not allocate
memory",__func__);
	return ENOMEM;
}


//////////////////////////////////////////////////////////////////////
// Name: allocate_msr_urb
// Purpose: Allocates and initializes modem status register urb 
//			to EP3IN        
//
//////////////////////////////////////////////////////////////////////
static int allocate_msr_urb(struct usbrsa_port_private *priv_data)
{
	unsigned long	flags;
	
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	priv_data->msr_urb = usb_alloc_urb(0,GFP_KERNEL);
	if (priv_data->msr_urb == NULL)
	{
		goto out_alloc_msr;
	}
	
	priv_data->msr_urb->transfer_buffer=kmalloc(EP3OUTLEN,GFP_KERNEL);
	if (priv_data->msr_urb->transfer_buffer == NULL)
	{
		goto out_alloc_msr;
	}
		
	usb_fill_bulk_urb(priv_data->msr_urb, priv_data->parent_serial->dev,
			usb_rcvbulkpipe(priv_data->parent_serial->dev,EP2IN),
			priv_data->msr_urb->transfer_buffer, EP2INLEN,
	        usbrsa_msr_callback, priv_data->parent_port);
	spin_lock_irqsave(&priv_data->lock,flags);
	clear_bit( LOCK_MSR,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	
	return 0;
	
out_alloc_msr:
	spin_lock_irqsave(&priv_data->lock,flags);
	set_bit( LOCK_MSR,&priv_data->urb_lock);
	spin_unlock_irqrestore(&priv_data->lock,flags);
	dev_dbg(&(priv_data->parent_port->dev),"%s(): Could not allocate
memory",__func__);
	return ENOMEM;
}


//////////////////////////////////////////////////////////////////////
// Name: release_write_urbs
// Purpose: Deallocate resource pool for EP1OUT
//        
//
//////////////////////////////////////////////////////////////////////
static void release_write_urbs(struct usbrsa_port_private *priv_data)
{
	unsigned long	flags;
	int 			i;

	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	for(i=0;i<priv_data->urb_pool_size;i++)
	{
		priv_data->write_urb_pool[i] = usb_alloc_urb(0,GFP_KERNEL);
		if (priv_data->write_urb_pool[i] != NULL)
		{
			if (priv_data->write_urb_pool[i]->transfer_buffer != NULL)
			{
				kfree(priv_data->write_urb_pool[i]->transfer_buffer);
			}
			usb_free_urb(priv_data->write_urb_pool[i]);
		}
		
		spin_lock_irqsave(&priv_data->lock,flags);
		set_bit( i,&priv_data->write_urb_pool_lock);
		spin_unlock_irqrestore(&priv_data->lock,flags);
	}
}

//////////////////////////////////////////////////////////////////////
// Name: release_read_urbs
// Purpose: Deallocate resource pool for EP1IN
//        
//
//////////////////////////////////////////////////////////////////////
static void release_read_urbs(struct usbrsa_port_private *priv_data)
{
	unsigned long	flags;
	int		i;

	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	for(i=0;i<priv_data->urb_pool_size;i++)
	{
		priv_data->read_urb_pool[i] = usb_alloc_urb(0,GFP_KERNEL);
		if (priv_data->read_urb_pool[i] != NULL)
		{
			if (priv_data->read_urb_pool[i]->transfer_buffer != NULL)
			{
				kfree(priv_data->read_urb_pool[i]->transfer_buffer);
			}
			usb_free_urb(priv_data->read_urb_pool[i]);
		}
		
		spin_lock_irqsave(&priv_data->lock,flags);
		set_bit( i,&priv_data->read_urb_pool_lock);
		spin_unlock_irqrestore(&priv_data->lock,flags);
	}

}

//////////////////////////////////////////////////////////////////////
// Name: release_baudrate_lcr_urb
// Purpose: Remove baudrate_lcr_urb 
//        
//
//////////////////////////////////////////////////////////////////////
static void release_baudrate_lcr_urb(struct usbrsa_port_private *priv_data)
{
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	if (priv_data->baudrate_lcr_urb != NULL)
	{
		if (priv_data->baudrate_lcr_urb->transfer_buffer != NULL)
		{
			kfree(priv_data->baudrate_lcr_urb->transfer_buffer);
		}
		usb_free_urb(priv_data->baudrate_lcr_urb);
		priv_data->baudrate_lcr_urb = NULL;
	}
}

//////////////////////////////////////////////////////////////////////
// Name: release_reset_urb
// Purpose: Remove release_reset_urb 
//        
//
//////////////////////////////////////////////////////////////////////
static void release_reset_urb(struct usbrsa_port_private *priv_data)
{
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	if (priv_data->reset_urb != NULL)
	{
		usb_free_urb(priv_data->reset_urb);
		priv_data->reset_urb = NULL;
	}
}

//////////////////////////////////////////////////////////////////////
// Name: release_status_urb
// Purpose: Remove release_status_urb 
//        
//
//////////////////////////////////////////////////////////////////////
static void release_status_urb(struct usbrsa_port_private *priv_data)
{
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	if (priv_data->status_urb != NULL)
	{
		if (priv_data->status_urb->transfer_buffer != NULL)
		{
			kfree(priv_data->status_urb->transfer_buffer);
		}
		usb_free_urb(priv_data->status_urb);
		priv_data->status_urb = NULL;
	}
	
}

//////////////////////////////////////////////////////////////////////
// Name: release_mcr_urb
// Purpose: Remove release_mcr_urb 
//        
//
//////////////////////////////////////////////////////////////////////
static void release_mcr_urb(struct usbrsa_port_private *priv_data)
{
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	if (priv_data->mcr_urb != NULL)
	{
		if (priv_data->mcr_urb->transfer_buffer != NULL)
		{
			kfree(priv_data->mcr_urb->transfer_buffer);
		}
		usb_free_urb(priv_data->mcr_urb);
		priv_data->mcr_urb = NULL;
	}
	
}


//////////////////////////////////////////////////////////////////////
// Name: release_msr_urb
// Purpose: deallocate msr_urb 
//        
//
//////////////////////////////////////////////////////////////////////
static void release_msr_urb(struct usbrsa_port_private *priv_data)
{
	dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);
	if (priv_data->msr_urb != NULL)
	{
		if (priv_data->msr_urb->transfer_buffer != NULL)
		{
			kfree(priv_data->msr_urb->transfer_buffer);
		}
		usb_free_urb(priv_data->msr_urb);
		priv_data->msr_urb = NULL;
	}
	
}
//////////////////////////////////////////////////////////////////////
// Name: prepare_baudrate_lcr_urb
// Purpose: Computes urb setting Line Control Regsister (LCR) and 
// 			divider register (DLL+DLM) to determine baud rate of 
//			ST16c550. The urb is sent to either EP2OUT and EP5OUT.
//			An urb to EP2OUT sets the registers of the USB-RSA and
//			enables receive interrupts. An urb to EP5OUT only sets 
//			the register. 
//
//
//			Format of EP2OUT/EP5OUT
//			Byte 0: DLL of ST16c550
//			Byte 1: DLM of ST16c550
// 			Byte 2: LCR of ST16c550
//
//////////////////////////////////////////////////////////////////////
static int send_baudrate_lcr_register(unsigned int cflag, speed_t baud_rate,
			int endpoint, struct usb_serial_port *port)
{
        struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
        unsigned long				flags;
        long						t;
        int 						retval;
    	__u8   						lcr 		= 0;
    	__u16						divisor  	= 0;
    	__u8*						buffer_ptr	= priv->baudrate_lcr_urb->transfer_buffer;

	dev_dbg(&port->dev,"%s() CFLAG=%u SPEED=%u ep=%d",__func__, cflag,
baud_rate,endpoint );
	// if word length is 5 bit, stop bit length is 1,5 bit times
	// else 2 bit times

	// restrict baud_rate to boundaries determined by 
	// USB-RSA hardware
	if (baud_rate < 75 )
		baud_rate = 75;
	if (baud_rate > 460800)
		baud_rate = 460800;
	
	
	divisor = USBRSA_ST16C550_CLOCK/(16 * baud_rate);
	priv->dll = divisor & 0xFF;
	priv->dlm = (divisor & 0xFF00) >> 8;

	

	switch (cflag & CSIZE) 
	{
		case CS5: lcr = USBRSA_ST16C550_LCR_CS5;   break;
		case CS6: lcr = USBRSA_ST16C550_LCR_CS6;   break;
		case CS7: lcr = USBRSA_ST16C550_LCR_CS7;   break;
		default:
		case CS8: lcr = USBRSA_ST16C550_LCR_CS8;   break;
	}

	/* determine the parity */
	if (cflag & PARENB)
		if (cflag & CMSPAR)
			if (cflag & PARODD)
				lcr |= USBRSA_ST16C550_LCR_MARK;
			else
				lcr |= USBRSA_ST16C550_LCR_SPACE;
		else
			if (cflag & PARODD)
				lcr |= USBRSA_ST16C550_LCR_ODD;
			else
				lcr |= USBRSA_ST16C550_LCR_EVEN;
	else
		lcr |= USBRSA_ST16C550_LCR_NONE;


   /* figure out the stop bits requested */
   if (cflag & CSTOPB)
	   lcr |= USBRSA_ST16C550_LCR_STOP2;
   else
	   lcr |= USBRSA_ST16C550_LCR_STOP1;
   
   // FW of USB-RSA expects bit 7 (Baud Rate Counter Latch) to be 0
   
   lcr &= 0x7F;
   priv->lcr = lcr;
   
   *buffer_ptr = priv->dll;
   buffer_ptr++;
   *buffer_ptr = priv->dlm;
   buffer_ptr++;
   *buffer_ptr = priv->lcr;
   
   
   dev_dbg(&port->dev,"%s()
ST16C550.DLL=%d;ST16C550.DLM=%d;ST16C550.LCR=%d", __func__,
priv->dll,priv->dlm,priv->lcr);
   
   spin_lock_irqsave(&priv->lock, flags);
   if (!(test_bit(LOCK_BAUDRATE_LCR,&priv->urb_lock)))
   {
	   set_bit(LOCK_BAUDRATE_LCR, &priv->urb_lock);
	   priv->baudrate_lcr_urb->pipe =
			   usb_sndbulkpipe(priv->parent_serial->dev,endpoint);
	   spin_unlock_irqrestore(&priv->lock, flags);

	   retval  = usb_submit_urb(priv->baudrate_lcr_urb, GFP_ATOMIC);
   }
   else
   {
	   // urb already in use
	  spin_unlock_irqrestore(&priv->lock, flags);
	  dev_dbg(&port->dev,"%s(): Cannot reserve baudrate_lcr_urb",__func__);
	  // fix me: return appropriate return code
	  retval = -1;
	  // end fix me
	  goto send_baudrate_lcr_exit;  
   }

   if (retval) 
   {
		 /* something bad happened, let's free up the urb */
		 dev_dbg(&port->dev,"%s(): Cannot submit urb: %d",__func__, retval);
		 goto send_baudrate_lcr_exit;
   }
           
   /* wait for the command to complete 
	* (waits, until timeout or condition==true) */
   t = wait_event_timeout(priv->wait_flag,
		(test_bit(LOCK_BAUDRATE_LCR, &priv->urb_lock)==0), COMMAND_TIMEOUT);
   
   if (!t)
   {
		// timeout. Dequeue urb
		usb_kill_urb(priv->baudrate_lcr_urb);
		dev_dbg(&port->dev,"%s - sending baudrate_lcr_urb timed out.", __func__);
		retval = -ETIMEDOUT;
		goto send_baudrate_lcr_exit;
   }              

   send_baudrate_lcr_exit:
   spin_lock_irqsave(&priv->lock, flags);
   clear_bit(LOCK_BAUDRATE_LCR, &priv->urb_lock);
   spin_unlock_irqrestore(&priv->lock, flags);
   dev_dbg(&port->dev,"%s() leaving",__func__);
   return retval;
}


//////////////////////////////////////////////////////////////////////
// Name: send_mcr_register
// Purpose: Sends MCR value in private data structure of device
//			to USBRSA 
//
//			Format of EP3OUT
//			USB Message Length: 1 Byte
//			Bit0: DTR
//			Bit1: RTS
//			Bit2: OP1
//			Bit3: OP2
//			Bit4: Diagnostics mode 
//
//////////////////////////////////////////////////////////////////////
static int send_mcr_register(struct usb_serial_port *port)
{
        struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
        unsigned long				flags;
        long						t;
        int retval;

        dev_dbg(&port->dev,"%s: 0x%02x", __func__, priv->mcr);


        spin_lock_irqsave(&priv->lock, flags);
		set_bit(LOCK_MCR, &priv->urb_lock);
		memcpy(priv->mcr_urb->transfer_buffer,&priv->mcr,1);
        spin_unlock_irqrestore(&priv->lock, flags);
        
        retval  = usb_submit_urb(priv->mcr_urb, GFP_ATOMIC);

        if (retval) 
        {
              /* something bad happened, let's free up the urb */
              dev_dbg(&port->dev,"%s(): Cannot submit urb: %d",__func__,
retval);
              goto send_mcr_exit;
        }
        
        /* wait for the command to complete 
         * (waits, until timeout or condition==true) */
        t = wait_event_timeout(priv->wait_flag,
        		(test_bit(LOCK_MCR, &priv->urb_lock)==0), COMMAND_TIMEOUT);
        if (!t)
        {
        	// fetching of msr 
        	usb_kill_urb(priv->mcr_urb);
        	dev_dbg(&port->dev,"%s - sending mcr timed out.", __func__);
        	retval = -ETIMEDOUT;
        	goto send_mcr_exit;
        }
             

        send_mcr_exit:
        spin_lock_irqsave(&priv->lock, flags);
        clear_bit(LOCK_MCR, &priv->urb_lock);
        spin_unlock_irqrestore(&priv->lock, flags);
        return retval;
}


//////////////////////////////////////////////////////////////////////
// Name: fetch_msr_register
// Purpose: Get MSR value from USBRSA and stores it in private data 
//          structure  
//
//			Format of EP2IN
//			USB Message Length: 1 Byte
//			Bit0: Delta CTS (1 indicates a change on the line)
//			Bit1: Delta DTS (1 indicates a change on the line)
//			Bit2: Delta RI (1 indicates a change on the line)
//			Bit3: Delta CD (1 indicates a change on the line)
//			Bit4: CTS
//			Bit5: DTS
//			Bit6: RI
//			Bit7: CD
//
//////////////////////////////////////////////////////////////////////
static int fetch_msr_register(struct usb_serial_port *port)
{
        struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
        unsigned long				flags;
        long						t;
        int retval;

        dev_dbg(&port->dev,"%s: 0x%02x", __func__, priv->msr);


        spin_lock_irqsave(&priv->lock, flags);
		set_bit(LOCK_MSR, &priv->urb_lock);
        spin_unlock_irqrestore(&priv->lock, flags);
        
        retval  = usb_submit_urb(priv->msr_urb, GFP_ATOMIC);

        if (retval) 
        {
              /* something bad happened, let's free up the urb */
              dev_dbg(&port->dev,"%s(): Cannot submit urb: %d",__func__,
retval);
              goto send_msr_exit;
        }
        
        /* wait for the command to complete 
         * (waits, until timeout or condition==true) */
        t = wait_event_timeout(priv->wait_flag,
        		(test_bit(LOCK_MSR, &priv->urb_lock)==0), COMMAND_TIMEOUT);
        if (!t)
        {
        	// fetching of msr 
        	usb_kill_urb(priv->msr_urb);
        	dev_dbg(&port->dev,"%s - fetching msr timed out.", __func__);
        	retval = -ETIMEDOUT;
        	goto send_msr_exit;
        }
             

        send_msr_exit:
        spin_lock_irqsave(&priv->lock, flags);
        clear_bit(LOCK_MSR, &priv->urb_lock);
        spin_unlock_irqrestore(&priv->lock, flags);
        dev_dbg(&port->dev,"%s: 0x%02x", __func__, priv->msr);
        return retval;
}


//////////////////////////////////////////////////////////////////////
// Name: send_reset
// Purpose: sent reset urb to USBRSA and wait for completion
//			
//
//////////////////////////////////////////////////////////////////////
static int send_reset(struct usb_serial_port *port)
{
	struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
	unsigned long				flags;
	long						t;
	int 						retval;

	dev_dbg(&port->dev,"%s: ", __func__);


	spin_lock_irqsave(&priv->lock, flags);
	set_bit(LOCK_RESET, &priv->urb_lock);
	spin_unlock_irqrestore(&priv->lock, flags);

	retval  = usb_submit_urb(priv->reset_urb, GFP_ATOMIC);

	if (retval) 
	{
		/* something bad happened, let's free up the urb */
		dev_dbg(&port->dev,"%s(): Cannot submit urb: %d",__func__, retval);
		goto send_reset_exit;
	}

	/* wait for the command to complete 
	 * (waits, until timeout or condition==true) */
	t = wait_event_timeout(priv->wait_flag,
			(test_bit(LOCK_RESET, &priv->urb_lock)==0), COMMAND_TIMEOUT);
	if (!t)
	{
		// fetching of msr 
		usb_kill_urb(priv->reset_urb);
		dev_dbg(&port->dev,"%s - reset timed out.", __func__);
		retval = -ETIMEDOUT;
		goto send_reset_exit;
	}


	send_reset_exit:
	spin_lock_irqsave(&priv->lock, flags);
	clear_bit(LOCK_RESET, &priv->urb_lock);
	spin_unlock_irqrestore(&priv->lock, flags);
	return retval;
	
	
}


//////////////////////////////////////////////////////////////////////
// Name: wait_modem_info
// Purpose: waits until a line reflected in the MSR (modem status 
// 			register) changes. Lines that can be tested are:
//			- CTS
//			- DSR
// 			- RI
// 			- CD
//			
//
//////////////////////////////////////////////////////////////////////
static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
{
        struct usbrsa_port_private*	priv = usb_get_serial_port_data(port);
        unsigned long 				flags;
        unsigned int 				prev, status;
        unsigned int 				changed;
        int 						retval;

        spin_lock_irqsave(&priv->lock, flags);
        
        retval = fetch_msr_register(port);
        if (retval != 0)
        {
        	goto bailout_wait_modem_info;
        }
        spin_lock_irqsave(&priv->lock, flags);
        prev = priv->msr;
        spin_unlock_irqrestore(&priv->lock, flags);

        while (1) 
        {
        	retval = fetch_msr_register(port);
        	if (retval != 0)
        	{
        		goto bailout_wait_modem_info;
        	}
        	spin_lock_irqsave(&priv->lock, flags);
        	status = priv->msr;
        	spin_unlock_irqrestore(&priv->lock, flags);
        	
        	if (signal_pending(current))
        		return -ERESTARTSYS;

        	spin_lock_irqsave(&priv->lock, flags);
        	changed = prev ^ status;
        	prev = status;
        	spin_unlock_irqrestore(&priv->lock, flags);
        	
        	if (((arg & TIOCM_RNG) && (changed & USBRSA_ST16C550_MSR_RI)) ||
        			((arg & TIOCM_DSR) && (changed & USBRSA_ST16C550_MSR_DSR)) ||
        			((arg & TIOCM_CD)  && (changed & USBRSA_ST16C550_MSR_CD)) ||
        			((arg & TIOCM_CTS) && (changed & USBRSA_ST16C550_MSR_CTS)))
        		return 0;
        	
        }

        bailout_wait_modem_info:
        /* NOTREACHED */
        return retval;
}


//////////////////////////////////////////////////////////////////////
// Name: get_serial_info
// Purpose: fills a struct containing information about the serial
//			port.
//			
//
//////////////////////////////////////////////////////////////////////
static int get_serial_info(struct usb_serial_port  *port,
                           struct serial_struct __user *retinfo)
{
	struct usbrsa_port_private *priv = usb_get_serial_port_data(port);
	struct serial_struct tmp;

	if (!retinfo)
		return -EFAULT;

	memset(&tmp, 0, sizeof(tmp));

	tmp.type                = PORT_16550A;
	tmp.line                = port->minor;
	tmp.port                = port->port_number;
	tmp.irq                 = 0;
	tmp.flags               = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
	tmp.xmit_fifo_size      = priv->urb_pool_size * port->bulk_out_size;
	tmp.baud_base           = 9600;
	tmp.close_delay         = 5*HZ;
	tmp.closing_wait        = 30*HZ;

	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
		return -EFAULT;
	return 0;
}




static struct usb_serial_driver * const serial_drivers[] =
{
		&usbrsa_preenum_device,&usbrsa_enumerated_device, NULL
};

module_usb_serial_driver(serial_drivers, id_table_combined);


//request_module("ezusb");
//request_module("usbserial");

MODULE_FIRMWARE("usbrsa.fw");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");


module_param(debug, int , S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debug");


// usbrsa.h
/*
 * Driver for IO-Data's USB RSA serial dongle
 *
 *      Copyright (C) 2012
 *          Tilman Glotzner <tilmanglotzner@xxxxxxxxxxx>
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *
 */

#ifndef __LINUX_USB_SERIAL_USBRSA_H
#define __LINUX_USB_SERIAL_USBRSA_H

#include <linux/tty.h>

#define FALSE				0
#define TRUE				1


#define USBRSA_ST16C550_CLOCK 			7730000

#define USBRSA_ST16C550_LCR_CS5 		0x00
#define USBRSA_ST16C550_LCR_CS6 		0x01
#define USBRSA_ST16C550_LCR_CS7 		0x02
#define USBRSA_ST16C550_LCR_CS8 		0x03
#define USBRSA_ST16C550_LCR_STOP1 		0x00
// if word length is 5 bit, stop bit length is 1,5 bit times
// else 2 bit times
#define USBRSA_ST16C550_LCR_STOP2 		0x04  

#define USBRSA_ST16C550_LCR_MARK		0x28
#define USBRSA_ST16C550_LCR_SPACE		0x38
#define USBRSA_ST16C550_LCR_ODD 		0x08
#define USBRSA_ST16C550_LCR_EVEN		0x18
#define USBRSA_ST16C550_LCR_NONE		0x00

#define USBRSA_ST16C550_LCR_BREAK		0x40
#define USBRSA_ST16C550_LCR_BREAKOFF 	0x00

#define USBRSA_ST16C550_MCR_DTR			0x01
#define USBRSA_ST16C550_MCR_RTS			0x02
#define USBRSA_ST16C550_MCR_OP1			0x04 // 1=enable CTS; 0=tie CTS to 1
(deactivate hardware flow control)
#define USBRSA_ST16C550_MCR_OP2			0x08
#define USBRSA_ST16C550_MCR_DIAG		0x10
#define USBRSA_ST16C550_MSR_CTS			0x10
#define USBRSA_ST16C550_MSR_DSR			0x20
#define USBRSA_ST16C550_MSR_RI			0x40
#define USBRSA_ST16C550_MSR_CD			0x80
#define USBRSA_ST16C550_MSR_CTS_CHD		0x01
#define USBRSA_ST16C550_MSR_DSR_CHD		0x02
#define USBRSA_ST16C550_MSR_RI_CHD		0x04
#define USBRSA_ST16C550_MSR_CD_CHD		0x08




#define NUM_URBS 10 // shall not bigger than nof bits of an unsigned long
#define USBRSA_READ_RUNNING 			0x1
#define USBRSA_READ_STOP    			0x0

// Tx buffer of USBRSA can takes 4096 bytes at most
#define USBRSA_TX_MAX_BYTES				4096

#define LOCK_RESET			0
#define LOCK_STATUS			1
#define LOCK_BAUDRATE_LCR	2
#define LOCK_MCR			4	
#define LOCK_MSR			5

#define EP1OUT				1
#define EP1IN				1

#define EP2OUT				2
#define EP2OUTLEN			3
#define EP3OUT				3
#define EP3OUTLEN			1
#define EP4OUT				4
#define EP4OUTLEN			0
#define EP5OUT				5
#define EP5OUTLEN			3

#define EP2IN				2
#define EP2INLEN			1
#define EP3IN				3
#define EP3INLEN			4



struct usbrsa_port_private
{
	struct usb_serial* 		parent_serial;
	struct usb_serial_port*	parent_port;
	spinlock_t            	lock;
	wait_queue_head_t     	wait_flag; /* for handling sleeping while waiting
for a command to finish */
	__u8   					lcr; 	/* USBRSA.ST16C550's Line Control Register */
	__u8   					dll;    /* USBRSA.ST16C550's Divisor Latch LSB Register */
	__u8   					dlm;    /* USBRSA.ST16C550's Divisor Latch MSB Register */
	__u8  					mcr;    /* USBRSA.ST16C550's Modem Control Register */
	__u8   					msr;    /* USBRSA.ST16C550's Modem Status Register */


	speed_t    				baudrate;	      	//baud rate of serial port
	unsigned int			c_flag;				// port settings
	// pointers to ep1in_buffer and ep1_out buffer are stored in the
usb_serial_port struct
	struct urb*				write_urb_pool[NUM_URBS];
	unsigned long       	write_urb_pool_lock;
	struct urb*				read_urb_pool[NUM_URBS];
	unsigned long         	read_urb_pool_lock;
	__u8					urb_pool_size;

	unsigned long			urb_lock;			// bit 0: lock reset_urb
	// bit 1: lock status_urb
	// bit 2; lock baudrate_lcr_urb
	// bit 3; lock mcr_urb
	// bit 4; lock msr_urb
	struct urb*         	reset_urb;			// goes out to EP4
	struct urb*		  		status_urb;			// comes in from EP3
	struct urb*		  		baudrate_lcr_urb;	// goes to EP2OUT or EP5OUT
	struct urb*         	mcr_urb;			// urb to modem control register (EP3OUT)
	struct urb*         	msr_urb;			// urb from modem status register (EP2IN)

	__u8					read_running;
	unsigned int 			nofRxBytesReceived;
	unsigned int   			nofTxBytesFree;
	unsigned int			nofTxMaxBytes;

	__u8					xoff;		/* XOFF byte value, default 0x13 */
	__u8					xon;		/* XON byte value, default 0x11 */
	__u8					lloop;		/* local loopback 0 or 1, default 0 */
	struct mutex		mutex;
} __attribute__ ((packed)); ;


#endif








--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux