/*
  Date Created:	9/15/2006
  File Name:		metro-usb.c
  Description:	metro-usb.c is the drivers main source file. The driver is a USB to Serial converter.
		The driver takes USB data and sends it to a virtual ttyUSB# serial port.
		The driver interfaces with the usbserial.ko driver supplied by Linux. 

		NOTES: 
		To install the driver:
		1. Install the usbserial.ko module supplied by Linux with: # insmod usbserial.ko
		2. Install the metro-usb.ko module with: # insmod metro-usb.ko vender=0x#### product=0x#### debug=1
		   The vendor, product and debug parameters are optional.

		Some of this code is credited to Linux USB open source files that are distributed with Linux.

  Copyright:	2007 Metrologic Instruments. All rights reserved.
  Requirements: gedit.exe, notepad.exe
  Kernel Support: SUSE Linux 2.6.34.
 
  Revision History:

  Date:			Developer:			Revisions:		
  ------------------------------------------------------------------------------
  1/30/2007		Philip Nicastro		Initial release. (v1.0.0.0)
  2/27/2007		Philip Nicastro		Changed the metrousb_read_int_callback function to use a loop with the tty_insert_flip_char function to copy each byte to the tty layer. Removed the tty_buffer_request_room and the tty_insert_flip_string function calls. These calls were not supported on Fedora 5.
  2/27/2007		Philip Nicastro		Released. (v1.1.0.0)
  3/14/2007		Philip Nicastro		Added the ability to write data to the USB port.
  3/14/2007		Philip Nicastro		Added the write, write_int_callback, and the write_room functions to handle the needed communcation to write to the virtual serial port created from this driver.
  3/15/2007		Philip Nicastro		Changed the open function to set the DTR and RTS to be off in the open function.
  3/19/2007		Philip Nicastro		Released. (v1.2.0.0)
  5/03/2010		Guenther Teubl    Changed code to be compatible to Kernel 2.6.27
  3/30/2010   Andreas Schmidt, 
              Dmitri Klevakin   Changed code to be compatible to Kernel 2.6.34                                              
  4/26/2011     Guenther Teubl      Addes Support for UNIDIRECTIONAL Scanners
 

*/

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/usb.h>
//#include <linux/config.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/usb/serial.h>
#include "metro-usb.h"

/* Version Information */
#define DRIVER_VERSION "v1.2.0.1"
#define DRIVER_AUTHOR "Philip Nicastro"
#define DRIVER_DESC "Metrologic Instruments Inc. - USB-POS driver (SUSE 2.6.34)"


/* Device table list. */
static struct usb_device_id id_table [] = {
	{ USB_DEVICE(METROLOGIC_VENDOR_ID, UNI_DIRECTIONAL_PRODUCT_ID) },	
	{ USB_DEVICE(METROLOGIC_VENDOR_ID, BI_DIRECTIONAL_PRODUCT_ID) },	
	{ }, /* Optional paramenter entry. */
	{ }, /* Terminating entry. */
};
MODULE_DEVICE_TABLE(usb, id_table);

/* Input parameter constants. */
static int debug;
static __u16 vendor;
static __u16 product;

/* Function prototypes. */
static void metrousb_cleanup (struct usb_serial_port *port);
static void metrousb_close (struct usb_serial_port *port);
static int  metrousb_open (struct tty_struct *tty, struct usb_serial_port *port);
static void metrousb_read_int_callback2 (struct urb *urb);
static void metrousb_disconnect (struct usb_serial *serial);
static void metrousb_release (struct usb_serial *serial);
static int metrousb_set_modem_ctrl(struct usb_serial *serial, unsigned int control_state);
static int metrousb_startup (struct usb_serial *serial);
static void metrousb_throttle(struct tty_struct *tty);
static int metrousb_tiocmget(struct tty_struct *tty, struct file *file);
static int metrousb_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear);
static void metrousb_unthrottle(struct tty_struct *tty);
static int metrousb_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count);
static void metrousb_write_int_callback2(struct urb *urb);
static int metrousb_write_room(struct tty_struct *tty);
static int metrousb_send_byte(struct usb_serial_port *port, unsigned char *buf);

/* Driver structure. */
static struct usb_driver metrousb_driver = {
	.name =		"metro-usb",
	.probe =	usb_serial_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	id_table,
	.no_dynamic_id = 	1,
};

/* Device structure. */
static struct usb_serial_driver metrousb_device = {
	.driver = {
		.owner =	THIS_MODULE,
		.name =		"metro-usb",
	},
	.description 		    = "Metrologic USB to serial converter.",
	.id_table 		      = id_table,
	.num_ports 		      = 1,
	.open 			        = metrousb_open,
	.close 			        = metrousb_close,
	.read_int_callback 	= metrousb_read_int_callback2,
	.attach 		        = metrousb_startup,
	.release            = metrousb_release,
	.disconnect 		    = metrousb_disconnect,
	.throttle          	= metrousb_throttle,
	.unthrottle        	= metrousb_unthrottle,
	.tiocmget          	= metrousb_tiocmget,
	.tiocmset          	= metrousb_tiocmset,
	.write 			        = metrousb_write,
	.write_int_callback = metrousb_write_int_callback2,
	.write_room		      = metrousb_write_room,
};

/* ----------------------------------------------------------------------------------------------
  Description:
	Clean up any urbs and port information.

  Input:  
	struct usb_serial_port *: pointer to a usb_serial_port structure.

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static void metrousb_cleanup (struct usb_serial_port *port)
{
	unsigned char buf[1];
	
	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	/* Clear memory. */
	memset (buf, 0x00, sizeof(buf));

	if (port->serial->dev) {
		/* Shutdown any interrupt_in_urbs. */
		if (port->interrupt_in_urb) {
			usb_unlink_urb(port->interrupt_in_urb);
			usb_kill_urb(port->interrupt_in_urb);
		}	

		/* Shutdown any interrupt_out_urbs. */		
		if (port->interrupt_out_urb) {
			usb_unlink_urb(port->interrupt_out_urb);
			usb_kill_urb(port->interrupt_out_urb);
		}
		
		/* Send the stop character for uni-directional devices. (Product ID = 0x0700) */ 		
		if (port->serial->dev->descriptor.idProduct == UNI_DIRECTIONAL_PRODUCT_ID)
		{
			/* Send the stop character two times to stop the device from reading. */
			buf[0] = METROUSB_STOP_CHAR;
			metrousb_send_byte(port, buf);
			metrousb_send_byte(port, buf);
		}
	}
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Close the open serial port. Cleanup any open serial port information.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.
	struct usb_serial_port *: pointer to a usb_serial_port structure.
	struct file *: pointer to a file structure.

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static void metrousb_close(struct usb_serial_port *port)
{
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);
	metrousb_cleanup(port);

	dev_info(&port->dev, "METRO-USB - %s - bytes in=%d  bytes out=%d\n", __FUNCTION__, metro_priv->bytes_in, metro_priv->bytes_out);
} 

/* ----------------------------------------------------------------------------------------------
  Description:
	Driver exit.

  Input:  
	None:	

  Output: 
	None:
*/
static void __exit metrousb_exit(void)
{
	dbg("METRO-USB - %s", __FUNCTION__);

	usb_deregister(&metrousb_driver);
	usb_serial_deregister(&metrousb_device);
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Driver initialization.

  Input:  
	None:	

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static int __init metrousb_init(void)
{
	int retval = 0;
	int i = 0;
	
	dbg("METRO-USB - %s", __FUNCTION__);

	/* Add the device parameters if entered. */
	if ((vendor > 0) && (product > 0)) {
		struct usb_device_id usb_dev_temp[] = { {USB_DEVICE(vendor, product) } };

		/* Find the last entry in id_table */
		for (i=0; i < ARRAY_SIZE(id_table); i++) {
			if (id_table[i].idVendor == 0) {
				id_table[i] = usb_dev_temp[0];
				break;
			}
		}

		dbg("METRO-USB - %s - support added for unknown device: vendor=0x%04X - product=0x%04X", __FUNCTION__, vendor, product);
		// info("Metro USB-POS support added for unknown device: vendor=0x%x - product=0x%x", vendor, product);
	}

	/* Register the devices. */
	retval = usb_serial_register(&metrousb_device);
	if (retval)
		return retval;

	/* Register the driver. */
	retval = usb_register(&metrousb_driver);
	if (retval)
		usb_serial_deregister(&metrousb_device);

//	info(DRIVER_DESC " : " DRIVER_VERSION);

	return retval;
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Open the drivers serial port.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.
	struct usb_serial_port *: pointer to a usb_serial_port structure.
	struct file *: pointer to a file structure.

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static int metrousb_open (struct tty_struct *tty, struct usb_serial_port *port)
	{
	struct usb_serial *serial = port->serial;
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
	unsigned long flags = 0;
	int result = 0;
    unsigned char buf[1];
	//int transfer_buffer_length = 8;

	dbg("METRO-USB - %s - port=%p, port number=%d", __FUNCTION__, port, port->number);

	/* Clear the urb pipe. */
	usb_clear_halt(serial->dev, port->interrupt_in_urb->pipe);
	//usb_clear_halt(serial->dev, port->write_urb->pipe);
	//usb_clear_halt(serial->dev, port->read_urb->pipe);
	usb_clear_halt(serial->dev, 0x81);
	usb_clear_halt(serial->dev, 0x02);

	dbg("METRO-USB open nach usb_clear_halt");

    /* Clear memory. */
	memset (buf, 0x00, sizeof(buf));

	/* Make sure the urb is initialized. */
	if (!port->interrupt_in_urb) {
		dbg("METRO-USB - %s - interrupt urb not initialized for port number=%d", __FUNCTION__, port->number);
		return -ENODEV;
	}

	/* Set the private data information for the port. */
	spin_lock_irqsave(&metro_priv->lock, flags);
	metro_priv->bytes_in = 0;
	metro_priv->bytes_out = 0;
	metro_priv->control_state = TIOCM_DTR | TIOCM_RTS; 
	metro_priv->throttled = 0;
	spin_unlock_irqrestore(&metro_priv->lock, flags);

	/* Set the modem state because this is what is usually set by the serial port open. */
	metrousb_set_modem_ctrl(serial, metro_priv->control_state);

	/*
	 * Force low_latency on so that our tty_push actually forces the data
	 * through, otherwise it is scheduled, and with high data rates (like
	 * with OHCI) data can get lost.
	 */
	if (tty)
		tty->low_latency = 1;

	dbg("METRO-USB vor fill_int_urb\n");

	/* Send the start character for uni-directional devices. (Product ID = 0x0700) */
	if (port->serial->dev->descriptor.idProduct == UNI_DIRECTIONAL_PRODUCT_ID)
	{
		buf[0] = METROUSB_START_CHAR;
		result = metrousb_send_byte(port, buf);
		if (result) {
			dbg("METRO-USB - %s - failed sending character for port number=%d, error code=%d"
				, __FUNCTION__, port->number, result);
			goto exit;
		}
	}
	
	/* Start reading from the device */
	usb_fill_int_urb (port->interrupt_in_urb, serial->dev,
			   usb_rcvintpipe (serial->dev, port->interrupt_in_endpointAddress),
			   port->interrupt_in_urb->transfer_buffer,
			   port->interrupt_in_urb->transfer_buffer_length,
			   metrousb_read_int_callback2, port, 1);
	result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);

	dbg("METRO-USB nach submit_urb\n");
	
	if (result) {
		dbg("METRO-USB - %s - failed submitting interrupt_in_urb for port number=%d, error code=%d"
			, __FUNCTION__, port->number, result);
		goto exit;
	}

	dbg("METRO-USB - %s - port open for port number=%d", __FUNCTION__, port->number);
exit:	
	return result;
} 

/* ----------------------------------------------------------------------------------------------
  Description:
	Read the port from the read interrupt.

  Input:  
	struct urb *: urb structure to get data.

  Output: 
	None: 
*/
static void metrousb_read_int_callback2 (struct urb *urb) // struct pt_regs *regs)
{
	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
    struct tty_struct *tty = port->port.tty;
	unsigned char *data = urb->transfer_buffer;
	int throttled = 0;
	int result = 0;
	unsigned long flags = 0;
	int i = 0;

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	switch (urb->status) {
		case 0:
			/* Success status, read from the port. */
			break;
		case -ECONNRESET:
		case -ENOENT:
		case -ESHUTDOWN:
			/* urb has been terminated. */
			dbg("METRO-USB - %s - urb shutting down, port number=%d, error code=%d",
				__FUNCTION__, port->number, result);
			return;
		default:
			dbg("METRO-USB - %s - non-zero urb received, port number=%d, error code=%d",
				__FUNCTION__, port->number, result);
			goto exit;
	}

	/* Set the data read from the usb port into the serial port buffer. */
	if (tty && urb->actual_length) {
		// Loop through the data copying each byte to the tty layer.
		for (i=0; i<urb->actual_length; i++) {
			// Copy the byte to the buffer.
			tty_insert_flip_char(tty, data[i], 0);
		}

		// Force the data to the tty layer.
		tty_flip_buffer_push(tty);
	}

	/* Set any port variables. */
	spin_lock_irqsave(&metro_priv->lock, flags);
	metro_priv->bytes_in += urb->actual_length;
	throttled = metro_priv->throttled;
	spin_unlock_irqrestore(&metro_priv->lock, flags);

	/* Continue trying to read if set. */
	if (!throttled) {
		usb_fill_int_urb (port->interrupt_in_urb, port->serial->dev,
				   usb_rcvintpipe (port->serial->dev, port->interrupt_in_endpointAddress),
				   port->interrupt_in_urb->transfer_buffer,
				   port->interrupt_in_urb->transfer_buffer_length,
				   metrousb_read_int_callback2, port, 1);
		
		result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);

		if (result) {
			dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d",
				__FUNCTION__, port->number, result);
		}
	}	
	return;

exit:
	/* Try to resubmit the urb. */
	result = usb_submit_urb (urb, GFP_ATOMIC);
	if (result) {
		dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d",
			__FUNCTION__, port->number, result);
	}
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Send a byte to the open usb port.

  Input:  
	struct usb_serial_port *: pointer to a usb_serial_port structure.
	unsigned char *: byte to send to the port.

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static int metrousb_send_byte(struct usb_serial_port *port, unsigned char *buf)
{
	struct usb_serial *serial = port->serial;
	struct urb *urb;
	int result = 0;

	dbg("METRO-USB - %s - byte to send=0x%02X", __FUNCTION__, buf[0]);

	/* Allocate memory for the write urb. */
	urb = usb_alloc_urb(0, GFP_ATOMIC);
	if (!urb) {
		dbg("METRO-USB - %s - port number=%d. No more free urbs available.", __FUNCTION__, port->number);
		return -ENOMEM;
	}

	/* Copy the data to the urb buffer. */
	memcpy(port->interrupt_out_urb->transfer_buffer, buf, 1);

	/* Submit the urb. */
	usb_fill_int_urb(urb, serial->dev,
			  usb_sndintpipe(serial->dev, port->interrupt_out_endpointAddress),
			  port->interrupt_out_urb->transfer_buffer, 
			  1,
			  metrousb_write_int_callback2, port, 1);

	/* Send it down the pipe. */
	result = usb_submit_urb(urb, GFP_ATOMIC);

	/* Done with this urb, let the host driver free it when it is finished with it. */
	usb_free_urb (urb);

	return result;
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Set the modem control state for the entered serial port.

  Input:  
	struct usb_serial_port *: pointer to a usb_serial_port structure.
	unsigned int: control state value to set.

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static int metrousb_set_modem_ctrl(struct usb_serial *serial, unsigned int control_state)
{
	int retval = 0;
	unsigned char mcr = METROUSB_MCR_NONE;
	 
	dbg("METRO-USB - %s - control state=%d", __FUNCTION__, control_state);

	/* Set the modem control value. */
	if (control_state & TIOCM_DTR)
		mcr |= METROUSB_MCR_DTR;
	if (control_state & TIOCM_RTS)
		mcr |= METROUSB_MCR_RTS;
	
	/* Send the command to the usb port. */
	retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
				METROUSB_SET_REQUEST_TYPE, METROUSB_SET_MODEM_CTRL_REQUEST,				
				control_state, 0, NULL, 0, WDR_TIMEOUT);
	if (retval < 0)
		dbg("METRO-USB - %s - set modem ctrl=0x%x failed, error code=%d", __FUNCTION__, mcr, retval);

	return retval;
}


/* ----------------------------------------------------------------------------------------------
  Description:
	Shutdown the driver.

  Input:  
	struct usb_serial *: pointer to a usb-serial structure.

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static void metrousb_disconnect (struct usb_serial *serial)
{
	int i = 0;

	dbg("METRO-USB - %s", __FUNCTION__);
	/* Stop reading and writing on all ports. */
	for (i=0; i < serial->num_ports; ++i) {
		/* Close any open urbs. */
		metrousb_cleanup(serial->port[i]);

		dbg("METRO-USB - %s - freed port number=%d", __FUNCTION__, serial->port[i]->number);
	}
}

static void metrousb_release (struct usb_serial *serial)
{
	int i = 0;
	
	dbg("METRO-USB - %s", __FUNCTION__);

	for (i=0; i < serial->num_ports; ++i) {
		/* Free memory. */
		kfree(usb_get_serial_port_data(serial->port[i]));

		dbg("METRO-USB - %s - freed port number=%d", __FUNCTION__, serial->port[i]->number);
	}

}

/* ----------------------------------------------------------------------------------------------
  Description:
	Startup the driver.

  Input:  
	struct usb_serial *: pointer to a usb-serial structure.

  Output: 
	int: Returns true (0) if successful, false otherwise. 
*/
static int metrousb_startup(struct usb_serial *serial)
{
	struct metrousb_private *metro_priv;
	struct usb_serial_port *port;
	int i = 0;

	dbg("METRO-USB - %s", __FUNCTION__);

	/* Loop through the serial ports setting up the private structures. 
	 * Currently we only use one port. */
	for (i = 0; i < serial->num_ports; ++i) {
		port = serial->port[i];

		/* Declare memory. */
		metro_priv = (struct metrousb_private *) kmalloc (sizeof(struct metrousb_private), GFP_KERNEL);
		if (!metro_priv)
			return -ENOMEM;

		/* Clear memory. */
		memset (metro_priv, 0x00, sizeof(struct metrousb_private));

		/* Initialize memory. */
		spin_lock_init(&metro_priv->lock);
		usb_set_serial_port_data(port, metro_priv);

		dbg("METRO-USB - %s - port number=%d.", __FUNCTION__, port->number);
	}

	return 0;
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Set the serial port throttle to stop reading from the port.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.

  Output: 
	None: 
*/
static void metrousb_throttle (struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
	unsigned long flags = 0;

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	/* Set the private information for the port to stop reading data. */
	spin_lock_irqsave(&metro_priv->lock, flags);
	metro_priv->throttled = 1;
	spin_unlock_irqrestore(&metro_priv->lock, flags);
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Get the serial port control line states.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.
	struct file *: pointer to a file structure.

  Output: 
	int: Returns the state of the control lines. 
*/
static int metrousb_tiocmget (struct tty_struct *tty, struct file *file)
{
	unsigned long control_state = 0;
    struct usb_serial_port *port = tty->driver_data;
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
	unsigned long flags = 0;

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	spin_lock_irqsave(&metro_priv->lock, flags);
	control_state = metro_priv->control_state;
	spin_unlock_irqrestore(&metro_priv->lock, flags);

	return control_state;
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Set the serial port control line states.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.
	struct file *: pointer to a file structure.
	unsigned int: line state to set.
	unsigned int: line state to clear.

  Output: 
	int: Returns the state of the control lines. 
*/
static int metrousb_tiocmset(struct tty_struct *tty, struct file *file,
			       unsigned int set, unsigned int clear)
{
    struct usb_serial_port *port = tty->driver_data;
    struct usb_serial *serial = port->serial;
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
	unsigned long flags = 0;
	unsigned long control_state = 0;

	dbg("METRO-USB - %s - port number=%d, set=%d, clear=%d", __FUNCTION__, port->number, set, clear);

	spin_lock_irqsave(&metro_priv->lock, flags);
	control_state = metro_priv->control_state;

	// Set the RTS and DTR values.
	if (set & TIOCM_RTS) 
		control_state |= TIOCM_RTS;
	if (set & TIOCM_DTR) 
		control_state |= TIOCM_DTR;
	if (clear & TIOCM_RTS) 
		control_state &= ~TIOCM_RTS;
	if (clear & TIOCM_DTR) 
		control_state &= ~TIOCM_DTR;

	dbg("testing  %s - port number=%d, state=%d", __FUNCTION__, port->number, (int)control_state);


	metro_priv->control_state = control_state;
	spin_unlock_irqrestore(&metro_priv->lock, flags);
	return metrousb_set_modem_ctrl(serial, control_state);
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Set the serial port unthrottle to resume reading from the port.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.

  Output: 
	None: 
*/
static void metrousb_unthrottle(struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
	unsigned long flags = 0;
	int result = 0;

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	/* Set the private information for the port to resume reading data. */
	spin_lock_irqsave(&metro_priv->lock, flags);
	metro_priv->throttled = 0;
	spin_unlock_irqrestore(&metro_priv->lock, flags);
	
	/* Submit the urb to read from the port. */
	port->interrupt_in_urb->dev = port->serial->dev;
	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
	if (result) {
		dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d",
			__FUNCTION__, port->number, result);
	}
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Write data to the open serial port.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.
	struct usb_serial_port *: pointer to a usb_serial_port structure.
	const unsigned char *: pointer to the data buffer to write.
	int: number of bytes to write.

  Output: 
	int: Returns the number of bytes sent to the urb. 
*/
static int metrousb_write (struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count)
{
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
	struct usb_serial *serial = port->serial;
	struct urb *urb;
	unsigned char *buffer;
	unsigned long flags = 0;
	int result = 0;

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	/* Check if we can use the urb. */
	spin_lock_irqsave(&metro_priv->lock, flags);
	if (metro_priv->outstanding_urbs > URB_UPPER_LIMIT) {
		spin_unlock_irqrestore(&metro_priv->lock, flags);
		dbg("METRO-USB - %s - port number=%d. Outstanding urb limit hit.", __FUNCTION__, port->number);
		return 0;
	}
	spin_unlock_irqrestore(&metro_priv->lock, flags);

	/* Allocate memory for the data buffer. */
	buffer = kmalloc (count, GFP_ATOMIC);
	if (!buffer) {
		dbg("METRO-USB - %s - port number=%d. Out of memory.", __FUNCTION__, port->number);
		return -ENOMEM;
	}

	/* Allocate memory for the write urb. */
	urb = usb_alloc_urb(0, GFP_ATOMIC);
	if (!urb) {
		dbg("METRO-USB - %s - port number=%d. No more free urbs available.", __FUNCTION__, port->number);
		kfree (buffer);
		return -ENOMEM;
	}

	/* Clear the memory. */
	memset(port->interrupt_out_urb->transfer_buffer, 0x00, port->interrupt_out_size);

	/* Set the size of the data buffer to copy. */
	count = (count > METROUSB_BUF_SIZE) ? METROUSB_BUF_SIZE : count;

	/* Copy the data to the urb buffer. */
	memcpy(port->interrupt_out_urb->transfer_buffer, buf, count);


	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->interrupt_out_urb->transfer_buffer);

	/* Submit the urb. */
	usb_fill_int_urb(urb, serial->dev,
			  usb_sndintpipe(serial->dev, port->interrupt_out_endpointAddress),
			  port->interrupt_out_urb->transfer_buffer, 
			  count,
			  metrousb_write_int_callback2, port, count);

	/* Send it down the pipe. */
	result = usb_submit_urb(urb, GFP_ATOMIC);
	if (result) {
		dbg("METRO-USB - %s - failed submitting interrupt out urb for port number=%d, error code=%d",
			__FUNCTION__, port->number, result);
		count = result;
		kfree (buffer);
	} else {
		spin_lock_irqsave(&metro_priv->lock, flags);
		++metro_priv->outstanding_urbs;
		metro_priv->bytes_out += count;
		spin_unlock_irqrestore(&metro_priv->lock, flags);
	}

	/* Done with this urb, let the host driver free it when it is finished with it. */
	usb_free_urb (urb);

	return count;
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Write int callback.

  Input:  
	struct urb *: pointer to a urb structure.

  Output: 
	None: 
*/
static void metrousb_write_int_callback2(struct urb *urb) // struct pt_regs *regs)
{
	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
	struct metrousb_private *metro_priv = usb_get_serial_port_data(port);
	int result = 0;
	unsigned long flags = 0;

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	switch (urb->status) {
		case 0:
			/* Success status, accept the write urb. */
			break;
		case -ECONNRESET:
		case -ENOENT:
		case -ESHUTDOWN:
			/* urb has been terminated. */
			dbg("METRO-USB - %s - urb shutting down, port number=%d, error code=%d",
				__FUNCTION__, port->number, result);
			return;
		case -EPIPE: /* No break needed. */
			usb_clear_halt(port->serial->dev, 0x02);	
		default:
			dbg("METRO-USB - %s - non-zero urb received, port number=%d, error code=%d",
				__FUNCTION__, port->number, result);
			goto exit;
	}

	/* Remove the urb from the counter. */
	spin_lock_irqsave(&metro_priv->lock, flags);
	--metro_priv->outstanding_urbs;
	spin_unlock_irqrestore(&metro_priv->lock, flags);

	return;

exit:
	/* Try to resubmit the urb. */
	result = usb_submit_urb (urb, GFP_ATOMIC);
	if (result) {
		dbg("METRO-USB - %s - failed submitting interrupt in urb for port number=%d, error code=%d",
			__FUNCTION__, port->number, result);
	}
}

/* ----------------------------------------------------------------------------------------------
  Description:
	Gets the number of bytes available to write.

  Input:  
	struct tty_struct *: pointer to a tty_struct structure.
	struct pt_regs *: pointer to the data buffer to write.

  Output: 
	None: 
*/
static int metrousb_write_room (struct tty_struct *tty)
{
    struct usb_serial_port *port = tty->driver_data;

	dbg("METRO-USB - %s - port number=%d", __FUNCTION__, port->number);

	return METROUSB_BUF_SIZE;
}

/* Standard module function. */
module_init(metrousb_init);
module_exit(metrousb_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );

/* Module input parameters */
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Print debug info (bool 1=on, 0=off)");

module_param(vendor, ushort, 0);
MODULE_PARM_DESC(vendor, "User specified vendor ID (ushort)");

module_param(product, ushort, 0);
MODULE_PARM_DESC(product, "User specified product ID (ushort)");




