Re: driver migration

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

 



Hi Greg

Kind of Driver: Driver for a dongle usb-> serial 
Manufacturer: Io-Data
Device: USB-RSA (based on EZUSB-CHip)
Goal: Port the driver that was working on kernel version 3.17 to current
kernel version
Problem Description:
- The driver initializes properly, and downloads the firmware.
- When issuing echo "0123456789" > /dev/ttyUSB0, the data is sent downstream
to the device. echo then blocks for 30 seconds. usbrsa_close is then called.
With it, also echo terminates (return code = 0)
- When issuing stty -F /dev/ttyUSB0 300, the command is apparently executed.
stty however blocks (as it seems indefinitely). usbrsa_close is only called
once I cancel stty with CTRL-C

Remark: From what I can see, the driver does not block within the driver
code but somewhere else.

For log and source code, please look below (As you suggested, I scanned the
source with checkpatch.pl. I have removed all errors). 

gkh> Why not post the updated
gkh> code so that everyone can see it and possibly help out with it? 
No issue with that -- just wanted to save everybody from utterly long postings.


=====

command: echo "0123456789" > /dev/ttyUSB0
SYSLOG:

[105791.355511] tty ttyUSB0: usbrsa_init_termios()
[105791.355528] usbrsa_open; priv=ffff880307620e00
[105791.355533] usbrsa ttyUSB0: usbrsa_open - port 0
[105791.355536] usbrsa ttyUSB0: usbrsa_set_termios - port 0
[105791.355539] usbrsa ttyUSB0: usbrsa_set_termios - baudrate = 300
[105791.355543] usbrsa ttyUSB0: send_baudrate_lcr_register() CFLAG=1207
SPEED=300 ep=2
[105791.355547] usbrsa ttyUSB0: send_baudrate_lcr_register()
ST16C550.DLL=74;ST16C550.DLM=6;ST16C550.LCR=3
[105791.355615] usbrsa ttyUSB0: usbrsa_baudrate_lcr_callback() - port = 0,
ep=0xc0010f00
[105791.355656] usbrsa ttyUSB0: send_baudrate_lcr_register() leaving
[105791.355669] usbrsa ttyUSB0: usbrsa_open - port 0: leaving
[105791.355712] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint
[105791.355724] usbrsa_write; private struct =ffff880307620e00
[105791.355729] usbrsa ttyUSB0: usbrsa_write - port 0; bytes=11 := count=11
: nofTxBytesFree=4063
[105791.355734] usbrsa ttyUSB0: usbrsa_write - poolsize  10, urb_index 0
[105791.355739] usbrsa ttyUSB0: usbrsa_write - port 0;URB allocated=0
[105791.355744] usbrsa ttyUSB0: usbrsa_write - port 0;bytes in urb=11
[105791.355748] usbrsa ttyUSB0: usbrsa_write - length = 11, data = 30 31 32
33 34 35 36 37 38 39 0a
[105791.355753] usbrsa ttyUSB0: usbrsa_write - port 0;sent=11;count=0;
nof_bytes_to_be_sent=0;bytes=11
[105791.355756] usbrsa ttyUSB0: usbrsa_write() End - sent=11
[105791.355801] usbrsa ttyUSB0: usbrsa_write_callback() - port = 0, ep
=0xc0018f80
[105791.355809] usbrsa ttyUSB0: usbrsa_write_callback - length = 11, data =
30 31 32 33 34 35 36 37 38 39 0a
[105791.355817] usbrsa ttyUSB0: usbrsa_write_callback(): Returned URB 0
[105791.355958] usbrsa_status_callback: nofTxBytesFree=4052,nofRxBytesReceived=0

... (here the blocking of "echo" starts)

[105793.353318] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint
[105795.353432] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint

... (repeats every 2 seconds for around 30 seconds)

usbrsa_status_callback(): usb_serial_port_softint
[105821.354309] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint
[105821.766319] usbrsa_close - start
[105821.766328] usbrsa ttyUSB0: usbrsa_close - port 0 start
[105821.766334] usbrsa ttyUSB0: send_baudrate_lcr_register() CFLAG=1207
SPEED=300 ep=5
[105821.766338] usbrsa ttyUSB0: send_baudrate_lcr_register()
ST16C550.DLL=74;ST16C550.DLM=6;ST16C550.LCR=3
[105821.766380] usbrsa ttyUSB0: usbrsa_baudrate_lcr_callback() - port = 0,
ep=0xc0028f00
[105821.766392] usbrsa ttyUSB0: send_baudrate_lcr_register() leaving
[105821.766395] usbrsa ttyUSB0: usbrsa_close - #retries=3
[105821.766397] usbrsa_close - end
[105821.766399] usbrsa ttyUSB0: usbrsa_close - end 
[105821.766416] usbrsa ttyUSB0: usbrsa_status_callback(): wake_up

... (echo terminates).....

command: stty -F /dev/ttyUSB0 300
SYSLOG:

[106202.139819] usbrsa_open; priv=ffff880307620e00
[106202.139824] usbrsa ttyUSB0: usbrsa_open - port 0
[106202.139827] usbrsa ttyUSB0: usbrsa_set_termios - port 0
[106202.139830] usbrsa ttyUSB0: usbrsa_set_termios - baudrate = 300
[106202.139834] usbrsa ttyUSB0: send_baudrate_lcr_register() CFLAG=1207
SPEED=300 ep=2
[106202.139838] usbrsa ttyUSB0: send_baudrate_lcr_register()
ST16C550.DLL=74;ST16C550.DLM=6;ST16C550.LCR=3
[106202.139924] usbrsa ttyUSB0: usbrsa_baudrate_lcr_callback() - port = 0,
ep=0xc0010f00
[106202.139949] usbrsa ttyUSB0: send_baudrate_lcr_register() leaving
[106202.139958] usbrsa ttyUSB0: usbrsa_open - port 0: leaving
[106202.139978] usbrsa ttyUSB0: usbrsa_ioctl() cmd 0x5401
[106202.139982] usbrsa ttyUSB0: usbrsa_ioctl(): TCGETS -- not implemented
[106202.139986] usbrsa ttyUSB0: usbrsa_ioctl() leaving
[106202.139996] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint
[106202.140016] usbrsa ttyUSB0: usbrsa_ioctl() cmd 0x5401
[106202.140021] usbrsa ttyUSB0: usbrsa_ioctl(): TCGETS -- not implemented
[106202.140025] usbrsa ttyUSB0: usbrsa_ioctl() leaving
[106202.140030] usbrsa ttyUSB0: usbrsa_ioctl() cmd 0x5403
[106202.140035] usbrsa ttyUSB0: usbrsa_ioctl(): 0x5403 not supported
[106202.140038] usbrsa ttyUSB0: usbrsa_ioctl() leaving
[106204.139545] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint

.... (stty waits blocked on the command line) ....

[106206.139617] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint

... (repeats every 2 seconds until CTRL-C  is sent to the stty command)


[106428.147311] usbrsa ttyUSB0: usbrsa_status_callback():
usb_serial_port_softint
[106428.665722] usbrsa_close - start
[106428.665733] usbrsa ttyUSB0: usbrsa_close - port 0 start
[106428.665740] usbrsa ttyUSB0: send_baudrate_lcr_register() CFLAG=1207
SPEED=300 ep=5
[106428.665744] usbrsa ttyUSB0: send_baudrate_lcr_register()
ST16C550.DLL=74;ST16C550.DLM=6;ST16C550.LCR=3
[106428.665782] usbrsa ttyUSB0: usbrsa_baudrate_lcr_callback() - port = 0,
ep=0xc0028f00
[106428.665794] usbrsa ttyUSB0: send_baudrate_lcr_register() leaving
[106428.665797] usbrsa ttyUSB0: usbrsa_close - #retries=3
[106428.665799] usbrsa_close - end
[106428.665801] usbrsa ttyUSB0: usbrsa_close - end 
[106428.665824] usbrsa ttyUSB0: usbrsa_status_callback(): wake_up

... (stty returns to command line. return code = 130)




SOURCE CODE:

/*
 * Driver for IO-Data's USB RSA serial dongle
 *      Copyright (C) 2012
 *         Tilman Glotzner(tilmanglotzner@xxxxxxxxx)
 *
 *
 * * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 *
 *
 *  USB-RSA by IO-DATA
 *  USB to serial converter
 *  =======================
 *
 *  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?
 *	Load kernel modules on which this driver depends.
 *
 */

#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
	int debug = 1;
#else
	int debug = 0;
#endif

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


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


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

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

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

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


MODULE_DEVICE_TABLE(usb, id_table_combined);







/***********************************************************************
*                   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)",
		.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
		 */
		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);

/***********************************************************************
*                   Driver object declaration
***********************************************************************/
static struct usb_serial_driver usbrsa_enumerated_device = {
		.driver = {
				.owner =        THIS_MODULE,
				.name =         "usbrsa",
		},
		.description =          "IO-DATA - USB-RSA",
		.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:
*
*
***********************************************************************/
static int  usbrsa_attach(struct usb_serial *serial)
{
	int							ret = 0;

	printk(KERN_DEBUG "%s start", __func__);
	dev_dbg(&serial->dev->dev, "%s", __func__);

	return ret;
}

/***********************************************************************
* Name: usbrsa_release
* Purpose:
*
*
***********************************************************************/
static void  usbrsa_release(struct usb_serial *serial)
{

	printk("%s: start: numberOfPorts=%d", __func__, serial->num_ports);
	dev_dbg(&serial->dev->dev, "%s()", __func__);

}

/***********************************************************************
* Name: usbrsa_probe
* Purpose: Creates private data structures of driver
*
*
*
***********************************************************************/
static int usbrsa_probe(struct usb_serial_port *port)
{
	int							ret = 0;
	struct usbrsa_port_private *priv = NULL;


	printk("%s", __func__);
/*	struct usbrsa_private *info;
*
*	info = kzalloc(sizeof(*info), GFP_KERNEL);
*	if (!info)
*		return -ENOMEM;
*
*	usb_set_serial_port_data(port, info);
*/


	dev_dbg(&port->dev, "%s", __func__);
	if (!port) {
		dev_err(&port->dev, "%s(): Invalid handler", __func__);
		return -ENODEV;
	}


	/* allocate struct for driver's private data */
	priv = kmalloc(sizeof(struct usbrsa_port_private), GFP_KERNEL);
	if (priv == NULL) {
		dev_err(&port->dev,
				"%s: Out of memory for port structures\n",
				__func__);
		goto out_no_private;
	}


	priv->parent_serial	= port->serial;
	priv->parent_port	= port;
	priv->read_running	= USBRSA_READ_STOP;
	priv->urb_pool_size	= NUM_URBS;
	priv->nofTxMaxBytes	= USBRSA_TX_MAX_BYTES;
	mutex_init(&priv->mutex);
	priv->debugTimeStamp = jiffies;

	spin_lock_init(&priv->lock);
	init_waitqueue_head(&priv->wait_flag);
	printk("%s: private struct at %p\n", __func__, priv);
	usb_set_serial_port_data(port, priv);
	printk("%s: private struct at %p\n", __func__,
			usb_get_serial_port_data(port));


	/* allocate  urbs */
	printk("%s about to enter 'usbrsa_allocate_write_urbs'\n", __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\n", __func__);
	ret = allocate_baudrate_lcr_urb(priv);
	if (ret != 0)
		goto out_no_alloc_baudrate_lcr;
	printk("%s allocate_reset_urb\n", __func__);
	ret = allocate_reset_urb(priv);
	if (ret != 0)
		goto out_no_alloc_reset;
	printk("%s allocate_status_urb\n", __func__);
	ret = allocate_status_urb(priv);
	if (ret != 0)
		goto out_no_alloc_status;
	printk("%s allocate_mcr_urb\n", __func__);
	ret = allocate_mcr_urb(priv);
	if (ret != 0)
		goto out_no_alloc_mcr;
	printk("%s allocate_msr_urb\n", __func__);
	ret = allocate_msr_urb(priv);
	if (ret != 0)
		goto out_no_alloc_msr;

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

	printk("%s end", __func__);
	return 0;

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:

	printk("%s end error", __func__);
	return ret;
}

/***********************************************************************
* Name: usbrsa_remove
* Purpose: Cleans up private data structures of driver
*
*
*
***********************************************************************/
static int usbrsa_remove(struct usb_serial_port *port)
{

	struct usbrsa_port_private	*priv;
	struct usb_serial_port		*serport;

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


	printk("%s: Mark1\n", __func__);
	serport = port;
	priv = usb_get_serial_port_data(serport);
	printk("%s: Mark2\n", __func__);
	printk("%s: private struct at %p\n", __func__, priv);
	release_msr_urb(priv);
	printk("%s: Mark2b ", __func__);
	release_mcr_urb(priv);
	printk("%s: Mark2c ", __func__);
	release_status_urb(priv);
	printk("%s: Mark2d ", __func__);
	release_reset_urb(priv);
	printk("%s: Mark2a ", __func__);
	release_baudrate_lcr_urb(priv);
	release_read_urbs(priv);
	printk("%s: Mark3", __func__);
	release_write_urbs(priv);
	printk("%s: Mark4", __func__);
	kfree(priv);
	printk("%s: Mark5", __func__);

	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 = 0;
	struct usbrsa_port_private	*priv = usb_get_serial_port_data(port);
	unsigned long				flags;

	printk("%s; priv=%p\n", __func__, priv);
	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);
	 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 - start", __func__);
	dev_dbg(&port->dev, "%s - port %d start", __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);
		} else {
			dev_dbg(&port->dev, "%s - #retries=%d", __func__, retries);
			break;
		}
		retries--;
	}

	/* disable continuous reading */
	spin_lock_irqsave(&priv->lock, flags);
	priv->read_running = USBRSA_READ_STOP;
	spin_unlock_irqrestore(&priv->lock, flags);

	printk("%s - end", __func__);
	dev_dbg(&port->dev, "%s - end ", __func__);
}

/***********************************************************************
* 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;
	}

	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);
	printk("%s; private struct =%p\n", __func__, priv);


	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);
	usb_serial_port_softint(port);
	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;
	}
	dev_dbg(&port->dev, "%s() leaving", __func__);
	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.
*
*
*
*
***********************************************************************/
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%x", __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%x", __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 ((priv->debugNofTxBytesFree != priv->nofTxBytesFree) ||
			(priv->debugRxBytesReceived !=	priv->nofRxBytesReceived)) {
		printk("%s: nofTxBytesFree=%d,nofRxBytesReceived=%d\n",
				__func__, priv->nofTxBytesFree, priv->nofRxBytesReceived);
	}
	priv->debugNofTxBytesFree =  priv->nofTxBytesFree;
	priv->debugRxBytesReceived = priv->nofRxBytesReceived;

	/* 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 */
			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%x", __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%x", __func__, status);
	}
	if (priv->read_running == USBRSA_READ_RUNNING) {
		if ((jiffies - priv->debugTimeStamp)/HZ  > 1 )	{
			dev_dbg(&port->dev,"%s(): usb_serial_port_softint", __func__);
			 priv->debugTimeStamp = jiffies;
		}
		usb_serial_port_softint(port);
	} else {
		dev_dbg(&port->dev, "%s(): wake_up", __func__);
		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%x", __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%x", __func__, status);
	}
	usb_serial_port_softint(port);
}


/***********************************************************************
* Name: usbrsa_mcr_callbacks
* 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->mcr_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%x", __func__, status);
	}
	wake_up(&priv->wait_flag);
}


/***********************************************************************
* Name: usbrsa_msr_callbackS
* 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->msr_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%x", __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;
	struct urb *urb;
	void *buf = NULL;

	printk("%s() Mark 1\n", __func__);
	/*dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__); */
	printk("%s() Mark 2\n", __func__);

	for (i = 0; i < priv_data->urb_pool_size; i++) {
		printk("%s; i=%d\n", __func__, i);
		urb = usb_alloc_urb(0, GFP_KERNEL);
		printk("%s; urb=%p\n", __func__, urb);
		printk("%s: pre priv_data->write_urb_pool[%d]=%p\n", __func__, i,
priv_data->write_urb_pool[i]);
		priv_data->write_urb_pool[i] = urb;
		printk("%s: post priv_data->write_urb_pool[%d]=%p\n", __func__, i,
priv_data->write_urb_pool[i]);
		if (priv_data->write_urb_pool[i] == NULL) {
			goto out_alloc_write_urb_pool;
		}
		printk("%s: pre priv_data->write_urb_pool[%d]->transfer_buffer=%p\n",
				__func__, i, priv_data->write_urb_pool[i]->transfer_buffer);
		buf = kmalloc(priv_data->parent_port->bulk_out_size, GFP_KERNEL);
		printk("%s: post buf=%p\n", __func__, buf);
		priv_data->write_urb_pool[i]->transfer_buffer = buf;

		printk("%s: post priv_data->write_urb_pool[%d]->transfer_buffer=%p\n",
				__func__, i, priv_data->write_urb_pool[i]->transfer_buffer);
		if (priv_data->write_urb_pool[i]->transfer_buffer == NULL) {
			goto out_alloc_write_urb_pool;
		}
		printk("%s; Mark3\n", __func__);
		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; Mark4\n", __func__);

		printk("%s -- clear_bit\n", __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 -- clear_bit end\n", __func__);
	}
	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]);
			}
		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;
	struct urb *urb;
	void *buf = NULL;

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

	for (i = 0; i < priv_data->urb_pool_size; i++)	{
		urb = usb_alloc_urb(0, GFP_KERNEL);
		priv_data->read_urb_pool[i]  = urb;
		if (priv_data->read_urb_pool[i] == NULL) {
			goto out_alloc_read_urb_pool;
		}
		buf = kmalloc(priv_data->parent_port->bulk_in_size, GFP_KERNEL);
		priv_data->read_urb_pool[i]->transfer_buffer = buf;
		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]);
			}
		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__); */
	printk("%s: Start priv_data=%p\n", __func__, priv_data);
	priv_data->baudrate_lcr_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (priv_data->baudrate_lcr_urb == NULL) {
		goto out_alloc_baudrate_lcr;
	}
	printk("%s: urb successfully allocated @ %p\n", __func__,
priv_data->baudrate_lcr_urb);
	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;
	}
	printk("%s: transfer buffer successfully allocated @ %p\n",
			__func__, priv_data->baudrate_lcr_urb->transfer_buffer);
	/* 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\n", __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__);*/
	printk("%s() start\n", __func__);

	priv_data->reset_urb = usb_alloc_urb(0, GFP_KERNEL);
	printk("%s() reset_urb=%p\n", __func__, priv_data->reset_urb);
	if (priv_data->reset_urb == NULL) {
		goto out_alloc_reset;
	}

	priv_data->reset_urb->transfer_buffer = NULL;
	printk("%s() reset_urb->transfer_buffer=%p\n", __func__, priv_data->reset_urb);

	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__);*/
	printk("%s() -- start\n", __func__);

	priv_data->status_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (priv_data->status_urb == NULL) {
		goto out_alloc_status;
	}
	printk("%s()  priv_data->status_urb =%p\n", __func__, priv_data->status_urb);
	priv_data->status_urb->transfer_buffer = kmalloc(EP3INLEN, GFP_KERNEL);
	if (priv_data->status_urb->transfer_buffer == NULL)	{
		goto out_alloc_status;
	}
	printk("%s()  priv_data->status_urb->transfer_buffer =%p\n", __func__,
			priv_data->status_urb->transfer_buffer);
	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;

	printk("%s() -- start\n", __func__);
	/*dev_dbg(&(priv_data->parent_port->dev),"%s()",__func__);*/
	priv_data->mcr_urb = usb_alloc_urb(0, GFP_KERNEL);
	printk("%s() priv_data->mcr_urb = %p\n", __func__, priv_data->mcr_urb);

	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;
	}
	printk("%s() priv_data->mcr_urb->transfer_buffer = %p\n", __func__,
			priv_data->mcr_urb->transfer_buffer);

	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__);*/
	printk("%s() -- start \n", __func__);

	priv_data->msr_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (priv_data->msr_urb == NULL)	{
		goto out_alloc_msr;
	}
	printk("%s() priv_data->msr_urb=%p \n", __func__, priv_data->msr_urb);

	priv_data->msr_urb->transfer_buffer = kmalloc(EP3OUTLEN, GFP_KERNEL);
	if (priv_data->msr_urb->transfer_buffer == NULL) {
		goto out_alloc_msr;
	}
	printk("%s() priv_data->msr_urb->transfer_buffer=%p \n",
			__func__, priv_data->msr_urb->transfer_buffer);

	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():
pool_size=%d",__func__,priv_data->urb_pool_size);*/
	printk("%s(): pool_size=%d\n", __func__, priv_data->urb_pool_size);
	for (i = 0; i < priv_data->urb_pool_size; i++) {
		printk("%s: post priv_data->write_urb_pool[%d]=%p\n",
				__func__, i, priv_data->write_urb_pool[i]);
		if (priv_data->write_urb_pool[i] != NULL) {
			printk("%s: post priv_data->write_urb_pool[%d]->transfer_buffer=%p\n",
__func__, i,
									priv_data->write_urb_pool[i]->transfer_buffer);
			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__);*/
	printk("%s()\n", __func__);
	for (i = 0; i < priv_data->urb_pool_size; i++) {
		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__);*/
	printk("%s: Start", __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__);*/
	printk("%s()\n", __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__);*/
	printk("%s()\n", __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__);*/
	printk("%s()\n", __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__);*/
	printk("%s()\n", __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");


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




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

  Powered by Linux