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(tilmanglotz...@gmail.com)
 *  
 *
 *      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.gloetz...@gmail.com>"
#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 <tilmanglotz...@hotmail.com>
 *
 *      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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to