| /* vi: ts=8 sw=8 | 
 |  * | 
 |  * TI 3410/5052 USB Serial Driver | 
 |  * | 
 |  * Copyright (C) 2004 Texas Instruments | 
 |  * | 
 |  * This driver is based on the Linux io_ti driver, which is | 
 |  *   Copyright (C) 2000-2002 Inside Out Networks | 
 |  *   Copyright (C) 2001-2002 Greg Kroah-Hartman | 
 |  * | 
 |  * 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. | 
 |  * | 
 |  * For questions or problems with this driver, contact Texas Instruments | 
 |  * technical support, or Al Borchers <alborchers@steinerpoint.com>, or | 
 |  * Peter Berger <pberger@brimson.com>. | 
 |  *  | 
 |  * This driver needs this hotplug script in /etc/hotplug/usb/ti_usb_3410_5052 | 
 |  * or in /etc/hotplug.d/usb/ti_usb_3410_5052.hotplug to set the device | 
 |  * configuration. | 
 |  * | 
 |  * #!/bin/bash | 
 |  * | 
 |  * BOOT_CONFIG=1 | 
 |  * ACTIVE_CONFIG=2 | 
 |  * | 
 |  * if [[ "$ACTION" != "add" ]] | 
 |  * then | 
 |  * 	exit | 
 |  * fi | 
 |  * | 
 |  * CONFIG_PATH=/sys${DEVPATH%/?*}/bConfigurationValue | 
 |  * | 
 |  * if [[ 0`cat $CONFIG_PATH` -ne $BOOT_CONFIG ]] | 
 |  * then | 
 |  * 	exit | 
 |  * fi | 
 |  * | 
 |  * PRODUCT=${PRODUCT%/?*}		# delete version | 
 |  * VENDOR_ID=`printf "%d" 0x${PRODUCT%/?*}` | 
 |  * PRODUCT_ID=`printf "%d" 0x${PRODUCT#*?/}` | 
 |  * | 
 |  * PARAM_PATH=/sys/module/ti_usb_3410_5052/parameters | 
 |  * | 
 |  * function scan() { | 
 |  * 	s=$1 | 
 |  * 	shift | 
 |  * 	for i | 
 |  * 	do | 
 |  * 		if [[ $s -eq $i ]] | 
 |  * 		then | 
 |  * 			return 0 | 
 |  * 		fi | 
 |  * 	done | 
 |  * 	return 1 | 
 |  * } | 
 |  * | 
 |  * IFS=$IFS, | 
 |  * | 
 |  * if (scan $VENDOR_ID 1105 `cat $PARAM_PATH/vendor_3410` && | 
 |  * scan $PRODUCT_ID 13328 `cat $PARAM_PATH/product_3410`) || | 
 |  * (scan $VENDOR_ID 1105 `cat $PARAM_PATH/vendor_5052` && | 
 |  * scan $PRODUCT_ID 20562 20818 20570 20575 `cat $PARAM_PATH/product_5052`) | 
 |  * then | 
 |  * 	echo $ACTIVE_CONFIG > $CONFIG_PATH | 
 |  * fi | 
 |  */ | 
 |  | 
 | #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/ioctl.h> | 
 | #include <linux/serial.h> | 
 | #include <linux/circ_buf.h> | 
 | #include <asm/uaccess.h> | 
 | #include <asm/semaphore.h> | 
 | #include <linux/usb.h> | 
 | #include <linux/usb/serial.h> | 
 |  | 
 | #include "ti_usb_3410_5052.h" | 
 | #include "ti_fw_3410.h"		/* firmware image for 3410 */ | 
 | #include "ti_fw_5052.h"		/* firmware image for 5052 */ | 
 |  | 
 |  | 
 | /* Defines */ | 
 |  | 
 | #define TI_DRIVER_VERSION	"v0.9" | 
 | #define TI_DRIVER_AUTHOR	"Al Borchers <alborchers@steinerpoint.com>" | 
 | #define TI_DRIVER_DESC		"TI USB 3410/5052 Serial Driver" | 
 |  | 
 | #define TI_FIRMWARE_BUF_SIZE	16284 | 
 |  | 
 | #define TI_WRITE_BUF_SIZE	1024 | 
 |  | 
 | #define TI_TRANSFER_TIMEOUT	2 | 
 |  | 
 | #define TI_DEFAULT_LOW_LATENCY	0 | 
 | #define TI_DEFAULT_CLOSING_WAIT	4000		/* in .01 secs */ | 
 |  | 
 | /* supported setserial flags */ | 
 | #define TI_SET_SERIAL_FLAGS	(ASYNC_LOW_LATENCY) | 
 |  | 
 | /* read urb states */ | 
 | #define TI_READ_URB_RUNNING	0 | 
 | #define TI_READ_URB_STOPPING	1 | 
 | #define TI_READ_URB_STOPPED	2 | 
 |  | 
 | #define TI_EXTRA_VID_PID_COUNT	5 | 
 |  | 
 |  | 
 | /* Structures */ | 
 |  | 
 | struct ti_port { | 
 | 	int			tp_is_open; | 
 | 	__u8			tp_msr; | 
 | 	__u8			tp_lsr; | 
 | 	__u8			tp_shadow_mcr; | 
 | 	__u8			tp_uart_mode;	/* 232 or 485 modes */ | 
 | 	unsigned int		tp_uart_base_addr; | 
 | 	int			tp_flags; | 
 | 	int			tp_closing_wait;/* in .01 secs */ | 
 | 	struct async_icount	tp_icount; | 
 | 	wait_queue_head_t	tp_msr_wait;	/* wait for msr change */ | 
 | 	wait_queue_head_t	tp_write_wait; | 
 | 	struct ti_device	*tp_tdev; | 
 | 	struct usb_serial_port	*tp_port; | 
 | 	spinlock_t		tp_lock; | 
 | 	int			tp_read_urb_state; | 
 | 	int			tp_write_urb_in_use; | 
 | 	struct circ_buf		*tp_write_buf; | 
 | }; | 
 |  | 
 | struct ti_device { | 
 | 	struct semaphore	td_open_close_sem; | 
 | 	int			td_open_port_count; | 
 | 	struct usb_serial	*td_serial; | 
 | 	int			td_is_3410; | 
 | 	int			td_urb_error; | 
 | }; | 
 |  | 
 |  | 
 | /* Function Declarations */ | 
 |  | 
 | static int ti_startup(struct usb_serial *serial); | 
 | static void ti_shutdown(struct usb_serial *serial); | 
 | static int ti_open(struct usb_serial_port *port, struct file *file); | 
 | static void ti_close(struct usb_serial_port *port, struct file *file); | 
 | static int ti_write(struct usb_serial_port *port, const unsigned char *data, | 
 | 	int count); | 
 | static int ti_write_room(struct usb_serial_port *port); | 
 | static int ti_chars_in_buffer(struct usb_serial_port *port); | 
 | static void ti_throttle(struct usb_serial_port *port); | 
 | static void ti_unthrottle(struct usb_serial_port *port); | 
 | static int ti_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg); | 
 | static void ti_set_termios(struct usb_serial_port *port, | 
 | 	struct termios *old_termios); | 
 | static int ti_tiocmget(struct usb_serial_port *port, struct file *file); | 
 | static int ti_tiocmset(struct usb_serial_port *port, struct file *file, | 
 | 	unsigned int set, unsigned int clear); | 
 | static void ti_break(struct usb_serial_port *port, int break_state); | 
 | static void ti_interrupt_callback(struct urb *urb); | 
 | static void ti_bulk_in_callback(struct urb *urb); | 
 | static void ti_bulk_out_callback(struct urb *urb); | 
 |  | 
 | static void ti_recv(struct device *dev, struct tty_struct *tty, | 
 | 	unsigned char *data, int length); | 
 | static void ti_send(struct ti_port *tport); | 
 | static int ti_set_mcr(struct ti_port *tport, unsigned int mcr); | 
 | static int ti_get_lsr(struct ti_port *tport); | 
 | static int ti_get_serial_info(struct ti_port *tport, | 
 | 	struct serial_struct __user *ret_arg); | 
 | static int ti_set_serial_info(struct ti_port *tport, | 
 | 	struct serial_struct __user *new_arg); | 
 | static void ti_handle_new_msr(struct ti_port *tport, __u8 msr); | 
 |  | 
 | static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush); | 
 |  | 
 | static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty); | 
 | static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty); | 
 |  | 
 | static int ti_command_out_sync(struct ti_device *tdev, __u8 command, | 
 | 	__u16 moduleid, __u16 value, __u8 *data, int size); | 
 | static int ti_command_in_sync(struct ti_device *tdev, __u8 command, | 
 | 	__u16 moduleid, __u16 value, __u8 *data, int size); | 
 |  | 
 | static int ti_write_byte(struct ti_device *tdev, unsigned long addr, | 
 | 	__u8 mask, __u8 byte); | 
 |  | 
 | static int ti_download_firmware(struct ti_device *tdev, | 
 | 	unsigned char *firmware, unsigned int firmware_size); | 
 |  | 
 | /* circular buffer */ | 
 | static struct circ_buf *ti_buf_alloc(void); | 
 | static void ti_buf_free(struct circ_buf *cb); | 
 | static void ti_buf_clear(struct circ_buf *cb); | 
 | static int ti_buf_data_avail(struct circ_buf *cb); | 
 | static int ti_buf_space_avail(struct circ_buf *cb); | 
 | static int ti_buf_put(struct circ_buf *cb, const char *buf, int count); | 
 | static int ti_buf_get(struct circ_buf *cb, char *buf, int count); | 
 |  | 
 |  | 
 | /* Data */ | 
 |  | 
 | /* module parameters */ | 
 | static int debug; | 
 | static int low_latency = TI_DEFAULT_LOW_LATENCY; | 
 | static int closing_wait = TI_DEFAULT_CLOSING_WAIT; | 
 | static ushort vendor_3410[TI_EXTRA_VID_PID_COUNT]; | 
 | static int vendor_3410_count; | 
 | static ushort product_3410[TI_EXTRA_VID_PID_COUNT]; | 
 | static int product_3410_count; | 
 | static ushort vendor_5052[TI_EXTRA_VID_PID_COUNT]; | 
 | static int vendor_5052_count; | 
 | static ushort product_5052[TI_EXTRA_VID_PID_COUNT]; | 
 | static int product_5052_count; | 
 |  | 
 | /* supported devices */ | 
 | /* the array dimension is the number of default entries plus */ | 
 | /* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */ | 
 | /* null entry */ | 
 | static struct usb_device_id ti_id_table_3410[1+TI_EXTRA_VID_PID_COUNT+1] = { | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, | 
 | }; | 
 |  | 
 | static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) }, | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, | 
 | }; | 
 |  | 
 | static struct usb_device_id ti_id_table_combined[] = { | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) }, | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) }, | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, | 
 | 	{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, | 
 | 	{ } | 
 | }; | 
 |  | 
 | static struct usb_driver ti_usb_driver = { | 
 | 	.name			= "ti_usb_3410_5052", | 
 | 	.probe			= usb_serial_probe, | 
 | 	.disconnect		= usb_serial_disconnect, | 
 | 	.id_table		= ti_id_table_combined, | 
 | 	.no_dynamic_id = 	1, | 
 | }; | 
 |  | 
 | static struct usb_serial_driver ti_1port_device = { | 
 | 	.driver = { | 
 | 		.owner		= THIS_MODULE, | 
 | 		.name		= "ti_usb_3410_5052_1", | 
 | 	}, | 
 | 	.description		= "TI USB 3410 1 port adapter", | 
 | 	.id_table		= ti_id_table_3410, | 
 | 	.num_interrupt_in	= 1, | 
 | 	.num_bulk_in		= 1, | 
 | 	.num_bulk_out		= 1, | 
 | 	.num_ports		= 1, | 
 | 	.attach			= ti_startup, | 
 | 	.shutdown		= ti_shutdown, | 
 | 	.open			= ti_open, | 
 | 	.close			= ti_close, | 
 | 	.write			= ti_write, | 
 | 	.write_room		= ti_write_room, | 
 | 	.chars_in_buffer	= ti_chars_in_buffer, | 
 | 	.throttle		= ti_throttle, | 
 | 	.unthrottle		= ti_unthrottle, | 
 | 	.ioctl			= ti_ioctl, | 
 | 	.set_termios		= ti_set_termios, | 
 | 	.tiocmget		= ti_tiocmget, | 
 | 	.tiocmset		= ti_tiocmset, | 
 | 	.break_ctl		= ti_break, | 
 | 	.read_int_callback	= ti_interrupt_callback, | 
 | 	.read_bulk_callback	= ti_bulk_in_callback, | 
 | 	.write_bulk_callback	= ti_bulk_out_callback, | 
 | }; | 
 |  | 
 | static struct usb_serial_driver ti_2port_device = { | 
 | 	.driver = { | 
 | 		.owner		= THIS_MODULE, | 
 | 		.name		= "ti_usb_3410_5052_2", | 
 | 	}, | 
 | 	.description		= "TI USB 5052 2 port adapter", | 
 | 	.id_table		= ti_id_table_5052, | 
 | 	.num_interrupt_in	= 1, | 
 | 	.num_bulk_in		= 2, | 
 | 	.num_bulk_out		= 2, | 
 | 	.num_ports		= 2, | 
 | 	.attach			= ti_startup, | 
 | 	.shutdown		= ti_shutdown, | 
 | 	.open			= ti_open, | 
 | 	.close			= ti_close, | 
 | 	.write			= ti_write, | 
 | 	.write_room		= ti_write_room, | 
 | 	.chars_in_buffer	= ti_chars_in_buffer, | 
 | 	.throttle		= ti_throttle, | 
 | 	.unthrottle		= ti_unthrottle, | 
 | 	.ioctl			= ti_ioctl, | 
 | 	.set_termios		= ti_set_termios, | 
 | 	.tiocmget		= ti_tiocmget, | 
 | 	.tiocmset		= ti_tiocmset, | 
 | 	.break_ctl		= ti_break, | 
 | 	.read_int_callback	= ti_interrupt_callback, | 
 | 	.read_bulk_callback	= ti_bulk_in_callback, | 
 | 	.write_bulk_callback	= ti_bulk_out_callback, | 
 | }; | 
 |  | 
 |  | 
 | /* Module */ | 
 |  | 
 | MODULE_AUTHOR(TI_DRIVER_AUTHOR); | 
 | MODULE_DESCRIPTION(TI_DRIVER_DESC); | 
 | MODULE_VERSION(TI_DRIVER_VERSION); | 
 | MODULE_LICENSE("GPL"); | 
 |  | 
 | module_param(debug, bool, S_IRUGO | S_IWUSR); | 
 | MODULE_PARM_DESC(debug, "Enable debugging, 0=no, 1=yes"); | 
 |  | 
 | module_param(low_latency, bool, S_IRUGO | S_IWUSR); | 
 | MODULE_PARM_DESC(low_latency, "TTY low_latency flag, 0=off, 1=on, default is off"); | 
 |  | 
 | module_param(closing_wait, int, S_IRUGO | S_IWUSR); | 
 | MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain in close, in .01 secs, default is 4000"); | 
 |  | 
 | module_param_array(vendor_3410, ushort, &vendor_3410_count, S_IRUGO); | 
 | MODULE_PARM_DESC(vendor_3410, "Vendor ids for 3410 based devices, 1-5 short integers"); | 
 | module_param_array(product_3410, ushort, &product_3410_count, S_IRUGO); | 
 | MODULE_PARM_DESC(product_3410, "Product ids for 3410 based devices, 1-5 short integers"); | 
 | module_param_array(vendor_5052, ushort, &vendor_5052_count, S_IRUGO); | 
 | MODULE_PARM_DESC(vendor_5052, "Vendor ids for 5052 based devices, 1-5 short integers"); | 
 | module_param_array(product_5052, ushort, &product_5052_count, S_IRUGO); | 
 | MODULE_PARM_DESC(product_5052, "Product ids for 5052 based devices, 1-5 short integers"); | 
 |  | 
 | MODULE_DEVICE_TABLE(usb, ti_id_table_combined); | 
 |  | 
 |  | 
 | /* Functions */ | 
 |  | 
 | static int __init ti_init(void) | 
 | { | 
 | 	int i,j; | 
 | 	int ret; | 
 |  | 
 | 	/* insert extra vendor and product ids */ | 
 | 	j = ARRAY_SIZE(ti_id_table_3410) - TI_EXTRA_VID_PID_COUNT - 1; | 
 | 	for (i=0; i<min(vendor_3410_count,product_3410_count); i++,j++) { | 
 | 		ti_id_table_3410[j].idVendor = vendor_3410[i]; | 
 | 		ti_id_table_3410[j].idProduct = product_3410[i]; | 
 | 		ti_id_table_3410[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE; | 
 | 	} | 
 | 	j = ARRAY_SIZE(ti_id_table_5052) - TI_EXTRA_VID_PID_COUNT - 1; | 
 | 	for (i=0; i<min(vendor_5052_count,product_5052_count); i++,j++) { | 
 | 		ti_id_table_5052[j].idVendor = vendor_5052[i]; | 
 | 		ti_id_table_5052[j].idProduct = product_5052[i]; | 
 | 		ti_id_table_5052[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE; | 
 | 	} | 
 |  | 
 | 	ret = usb_serial_register(&ti_1port_device); | 
 | 	if (ret) | 
 | 		goto failed_1port; | 
 | 	ret = usb_serial_register(&ti_2port_device); | 
 | 	if (ret) | 
 | 		goto failed_2port; | 
 |  | 
 | 	ret = usb_register(&ti_usb_driver); | 
 | 	if (ret) | 
 | 		goto failed_usb; | 
 |  | 
 | 	info(TI_DRIVER_DESC " " TI_DRIVER_VERSION); | 
 |  | 
 | 	return 0; | 
 |  | 
 | failed_usb: | 
 | 	usb_serial_deregister(&ti_2port_device); | 
 | failed_2port: | 
 | 	usb_serial_deregister(&ti_1port_device); | 
 | failed_1port: | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | static void __exit ti_exit(void) | 
 | { | 
 | 	usb_serial_deregister(&ti_1port_device); | 
 | 	usb_serial_deregister(&ti_2port_device); | 
 | 	usb_deregister(&ti_usb_driver); | 
 | } | 
 |  | 
 |  | 
 | module_init(ti_init); | 
 | module_exit(ti_exit); | 
 |  | 
 |  | 
 | static int ti_startup(struct usb_serial *serial) | 
 | { | 
 | 	struct ti_device *tdev; | 
 | 	struct ti_port *tport; | 
 | 	struct usb_device *dev = serial->dev; | 
 | 	int status; | 
 | 	int i; | 
 |  | 
 |  | 
 | 	dbg("%s - product 0x%4X, num configurations %d, configuration value %d", | 
 | 	    __FUNCTION__, le16_to_cpu(dev->descriptor.idProduct), | 
 | 	    dev->descriptor.bNumConfigurations, | 
 | 	    dev->actconfig->desc.bConfigurationValue); | 
 |  | 
 | 	/* create device structure */ | 
 | 	tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL); | 
 | 	if (tdev == NULL) { | 
 | 		dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); | 
 | 		return -ENOMEM; | 
 | 	} | 
 | 	sema_init(&tdev->td_open_close_sem, 1); | 
 | 	tdev->td_serial = serial; | 
 | 	usb_set_serial_data(serial, tdev); | 
 |  | 
 | 	/* determine device type */ | 
 | 	if (usb_match_id(serial->interface, ti_id_table_3410)) | 
 | 		tdev->td_is_3410 = 1; | 
 | 	dbg("%s - device type is %s", __FUNCTION__, tdev->td_is_3410 ? "3410" : "5052"); | 
 |  | 
 | 	/* if we have only 1 configuration, download firmware */ | 
 | 	if (dev->descriptor.bNumConfigurations == 1) { | 
 |  | 
 | 		if (tdev->td_is_3410) | 
 | 			status = ti_download_firmware(tdev, ti_fw_3410, | 
 | 				sizeof(ti_fw_3410)); | 
 | 		else | 
 | 			status = ti_download_firmware(tdev, ti_fw_5052, | 
 | 				sizeof(ti_fw_5052)); | 
 | 		if (status) | 
 | 			goto free_tdev; | 
 |  | 
 | 		/* 3410 must be reset, 5052 resets itself */ | 
 | 		if (tdev->td_is_3410) { | 
 | 			msleep_interruptible(100); | 
 | 			usb_reset_device(dev); | 
 | 		} | 
 |  | 
 | 		status = -ENODEV; | 
 | 		goto free_tdev; | 
 | 	}  | 
 |  | 
 | 	/* the second configuration must be set (in sysfs by hotplug script) */ | 
 | 	if (dev->actconfig->desc.bConfigurationValue == TI_BOOT_CONFIG) { | 
 | 		status = -ENODEV; | 
 | 		goto free_tdev; | 
 | 	} | 
 |  | 
 | 	/* set up port structures */ | 
 | 	for (i = 0; i < serial->num_ports; ++i) { | 
 | 		tport = kmalloc(sizeof(struct ti_port), GFP_KERNEL); | 
 | 		if (tport == NULL) { | 
 | 			dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); | 
 | 			status = -ENOMEM; | 
 | 			goto free_tports; | 
 | 		} | 
 | 		memset(tport, 0, sizeof(struct ti_port)); | 
 | 		spin_lock_init(&tport->tp_lock); | 
 | 		tport->tp_uart_base_addr = (i == 0 ? TI_UART1_BASE_ADDR : TI_UART2_BASE_ADDR); | 
 | 		tport->tp_flags = low_latency ? ASYNC_LOW_LATENCY : 0; | 
 | 		tport->tp_closing_wait = closing_wait; | 
 | 		init_waitqueue_head(&tport->tp_msr_wait); | 
 | 		init_waitqueue_head(&tport->tp_write_wait); | 
 | 		tport->tp_write_buf = ti_buf_alloc(); | 
 | 		if (tport->tp_write_buf == NULL) { | 
 | 			dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); | 
 | 			kfree(tport); | 
 | 			status = -ENOMEM; | 
 | 			goto free_tports; | 
 | 		} | 
 | 		tport->tp_port = serial->port[i]; | 
 | 		tport->tp_tdev = tdev; | 
 | 		usb_set_serial_port_data(serial->port[i], tport); | 
 | 		tport->tp_uart_mode = 0;	/* default is RS232 */ | 
 | 	} | 
 | 	 | 
 | 	return 0; | 
 |  | 
 | free_tports: | 
 | 	for (--i; i>=0; --i) { | 
 | 		tport = usb_get_serial_port_data(serial->port[i]); | 
 | 		ti_buf_free(tport->tp_write_buf); | 
 | 		kfree(tport); | 
 | 		usb_set_serial_port_data(serial->port[i], NULL); | 
 | 	} | 
 | free_tdev: | 
 | 	kfree(tdev); | 
 | 	usb_set_serial_data(serial, NULL); | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static void ti_shutdown(struct usb_serial *serial) | 
 | { | 
 | 	int i; | 
 | 	struct ti_device *tdev = usb_get_serial_data(serial); | 
 | 	struct ti_port *tport; | 
 |  | 
 | 	dbg("%s", __FUNCTION__); | 
 |  | 
 | 	for (i=0; i < serial->num_ports; ++i) { | 
 | 		tport = usb_get_serial_port_data(serial->port[i]); | 
 | 		if (tport) { | 
 | 			ti_buf_free(tport->tp_write_buf); | 
 | 			kfree(tport); | 
 | 			usb_set_serial_port_data(serial->port[i], NULL); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	kfree(tdev); | 
 | 	usb_set_serial_data(serial, NULL); | 
 | } | 
 |  | 
 |  | 
 | static int ti_open(struct usb_serial_port *port, struct file *file) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	struct ti_device *tdev; | 
 | 	struct usb_device *dev; | 
 | 	struct urb *urb; | 
 | 	int port_number; | 
 | 	int status; | 
 | 	__u16 open_settings = (__u8)(TI_PIPE_MODE_CONTINOUS |  | 
 | 			     TI_PIPE_TIMEOUT_ENABLE |  | 
 | 			     (TI_TRANSFER_TIMEOUT << 2)); | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return -ENODEV; | 
 |  | 
 | 	dev = port->serial->dev; | 
 | 	tdev = tport->tp_tdev; | 
 |  | 
 | 	/* only one open on any port on a device at a time */ | 
 | 	if (down_interruptible(&tdev->td_open_close_sem)) | 
 | 		return -ERESTARTSYS; | 
 |  | 
 | 	if (port->tty) | 
 | 		port->tty->low_latency =  | 
 | 			(tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; | 
 |  | 
 | 	port_number = port->number - port->serial->minor; | 
 |  | 
 | 	memset(&(tport->tp_icount), 0x00, sizeof(tport->tp_icount)); | 
 |  | 
 | 	tport->tp_msr = 0; | 
 | 	tport->tp_shadow_mcr |= (TI_MCR_RTS | TI_MCR_DTR); | 
 |  | 
 | 	/* start interrupt urb the first time a port is opened on this device */ | 
 | 	if (tdev->td_open_port_count == 0) { | 
 | 		dbg("%s - start interrupt in urb", __FUNCTION__); | 
 | 		urb = tdev->td_serial->port[0]->interrupt_in_urb; | 
 | 		if (!urb) { | 
 | 			dev_err(&port->dev, "%s - no interrupt urb\n", __FUNCTION__); | 
 | 			status = -EINVAL; | 
 | 			goto up_sem; | 
 | 		} | 
 | 		urb->complete = ti_interrupt_callback; | 
 | 		urb->context = tdev; | 
 | 		urb->dev = dev; | 
 | 		status = usb_submit_urb(urb, GFP_KERNEL); | 
 | 		if (status) { | 
 | 			dev_err(&port->dev, "%s - submit interrupt urb failed, %d\n", __FUNCTION__, status); | 
 | 			goto up_sem; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	ti_set_termios(port, NULL); | 
 |  | 
 | 	dbg("%s - sending TI_OPEN_PORT", __FUNCTION__); | 
 | 	status = ti_command_out_sync(tdev, TI_OPEN_PORT, | 
 | 		(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - cannot send open command, %d\n", __FUNCTION__, status); | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 |  | 
 | 	dbg("%s - sending TI_START_PORT", __FUNCTION__); | 
 | 	status = ti_command_out_sync(tdev, TI_START_PORT, | 
 | 		(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - cannot send start command, %d\n", __FUNCTION__, status); | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 |  | 
 | 	dbg("%s - sending TI_PURGE_PORT", __FUNCTION__); | 
 | 	status = ti_command_out_sync(tdev, TI_PURGE_PORT, | 
 | 		(__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - cannot clear input buffers, %d\n", __FUNCTION__, status); | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 | 	status = ti_command_out_sync(tdev, TI_PURGE_PORT, | 
 | 		(__u8)(TI_UART1_PORT + port_number), TI_PURGE_OUTPUT, NULL, 0); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - cannot clear output buffers, %d\n", __FUNCTION__, status); | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 |  | 
 | 	/* reset the data toggle on the bulk endpoints to work around bug in | 
 | 	 * host controllers where things get out of sync some times */ | 
 | 	usb_clear_halt(dev, port->write_urb->pipe); | 
 | 	usb_clear_halt(dev, port->read_urb->pipe); | 
 |  | 
 | 	ti_set_termios(port, NULL); | 
 |  | 
 | 	dbg("%s - sending TI_OPEN_PORT (2)", __FUNCTION__); | 
 | 	status = ti_command_out_sync(tdev, TI_OPEN_PORT, | 
 | 		(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - cannot send open command (2), %d\n", __FUNCTION__, status); | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 |  | 
 | 	dbg("%s - sending TI_START_PORT (2)", __FUNCTION__); | 
 | 	status = ti_command_out_sync(tdev, TI_START_PORT, | 
 | 		(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - cannot send start command (2), %d\n", __FUNCTION__, status); | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 |  | 
 | 	/* start read urb */ | 
 | 	dbg("%s - start read urb", __FUNCTION__); | 
 | 	urb = port->read_urb; | 
 | 	if (!urb) { | 
 | 		dev_err(&port->dev, "%s - no read urb\n", __FUNCTION__); | 
 | 		status = -EINVAL; | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 | 	tport->tp_read_urb_state = TI_READ_URB_RUNNING; | 
 | 	urb->complete = ti_bulk_in_callback; | 
 | 	urb->context = tport; | 
 | 	urb->dev = dev; | 
 | 	status = usb_submit_urb(urb, GFP_KERNEL); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - submit read urb failed, %d\n", __FUNCTION__, status); | 
 | 		goto unlink_int_urb; | 
 | 	} | 
 |  | 
 | 	tport->tp_is_open = 1; | 
 | 	++tdev->td_open_port_count; | 
 |  | 
 | 	goto up_sem; | 
 |  | 
 | unlink_int_urb: | 
 | 	if (tdev->td_open_port_count == 0) | 
 | 		usb_kill_urb(port->serial->port[0]->interrupt_in_urb); | 
 | up_sem: | 
 | 	up(&tdev->td_open_close_sem); | 
 | 	dbg("%s - exit %d", __FUNCTION__, status); | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static void ti_close(struct usb_serial_port *port, struct file *file) | 
 | { | 
 | 	struct ti_device *tdev; | 
 | 	struct ti_port *tport; | 
 | 	int port_number; | 
 | 	int status; | 
 | 	int do_up; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 | 			  | 
 | 	tdev = usb_get_serial_data(port->serial); | 
 | 	tport = usb_get_serial_port_data(port); | 
 | 	if (tdev == NULL || tport == NULL) | 
 | 		return; | 
 |  | 
 | 	tport->tp_is_open = 0; | 
 |  | 
 | 	ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 1); | 
 |  | 
 | 	usb_kill_urb(port->read_urb); | 
 | 	usb_kill_urb(port->write_urb); | 
 | 	tport->tp_write_urb_in_use = 0; | 
 |  | 
 | 	port_number = port->number - port->serial->minor; | 
 |  | 
 | 	dbg("%s - sending TI_CLOSE_PORT", __FUNCTION__); | 
 | 	status = ti_command_out_sync(tdev, TI_CLOSE_PORT, | 
 | 		     (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0); | 
 | 	if (status) | 
 | 		dev_err(&port->dev, "%s - cannot send close port command, %d\n" , __FUNCTION__, status); | 
 |  | 
 | 	/* if down is interrupted, continue anyway */ | 
 | 	do_up = !down_interruptible(&tdev->td_open_close_sem); | 
 | 	--tport->tp_tdev->td_open_port_count; | 
 | 	if (tport->tp_tdev->td_open_port_count <= 0) { | 
 | 		/* last port is closed, shut down interrupt urb */ | 
 | 		usb_kill_urb(port->serial->port[0]->interrupt_in_urb); | 
 | 		tport->tp_tdev->td_open_port_count = 0; | 
 | 	} | 
 | 	if (do_up) | 
 | 		up(&tdev->td_open_close_sem); | 
 |  | 
 | 	dbg("%s - exit", __FUNCTION__); | 
 | } | 
 |  | 
 |  | 
 | static int ti_write(struct usb_serial_port *port, const unsigned char *data, | 
 | 	int count) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	unsigned long flags; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (count == 0) { | 
 | 		dbg("%s - write request of 0 bytes", __FUNCTION__); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if (tport == NULL || !tport->tp_is_open) | 
 | 		return -ENODEV; | 
 |  | 
 | 	spin_lock_irqsave(&tport->tp_lock, flags); | 
 | 	count = ti_buf_put(tport->tp_write_buf, data, count); | 
 | 	spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 |  | 
 | 	ti_send(tport); | 
 |  | 
 | 	return count; | 
 | } | 
 |  | 
 |  | 
 | static int ti_write_room(struct usb_serial_port *port) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	int room = 0; | 
 | 	unsigned long flags; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return -ENODEV; | 
 | 	 | 
 | 	spin_lock_irqsave(&tport->tp_lock, flags); | 
 | 	room = ti_buf_space_avail(tport->tp_write_buf); | 
 | 	spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 |  | 
 | 	dbg("%s - returns %d", __FUNCTION__, room); | 
 | 	return room; | 
 | } | 
 |  | 
 |  | 
 | static int ti_chars_in_buffer(struct usb_serial_port *port) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	int chars = 0; | 
 | 	unsigned long flags; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return -ENODEV; | 
 |  | 
 | 	spin_lock_irqsave(&tport->tp_lock, flags); | 
 | 	chars = ti_buf_data_avail(tport->tp_write_buf); | 
 | 	spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 |  | 
 | 	dbg("%s - returns %d", __FUNCTION__, chars); | 
 | 	return chars; | 
 | } | 
 |  | 
 |  | 
 | static void ti_throttle(struct usb_serial_port *port) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	struct tty_struct *tty; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return; | 
 |  | 
 | 	tty = port->tty; | 
 | 	if (!tty) { | 
 | 		dbg("%s - no tty", __FUNCTION__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (I_IXOFF(tty) || C_CRTSCTS(tty)) | 
 | 		ti_stop_read(tport, tty); | 
 |  | 
 | } | 
 |  | 
 |  | 
 | static void ti_unthrottle(struct usb_serial_port *port) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	struct tty_struct *tty; | 
 | 	int status; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return; | 
 |  | 
 | 	tty = port->tty; | 
 | 	if (!tty) { | 
 | 		dbg("%s - no tty", __FUNCTION__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (I_IXOFF(tty) || C_CRTSCTS(tty)) { | 
 | 		status = ti_restart_read(tport, tty); | 
 | 		if (status) | 
 | 			dev_err(&port->dev, "%s - cannot restart read, %d\n", __FUNCTION__, status); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | static int ti_ioctl(struct usb_serial_port *port, struct file *file, | 
 | 	unsigned int cmd, unsigned long arg) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	struct async_icount cnow; | 
 | 	struct async_icount cprev; | 
 |  | 
 | 	dbg("%s - port %d, cmd = 0x%04X", __FUNCTION__, port->number, cmd); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return -ENODEV; | 
 |  | 
 | 	switch (cmd) { | 
 | 		case TIOCGSERIAL: | 
 | 			dbg("%s - (%d) TIOCGSERIAL", __FUNCTION__, port->number); | 
 | 			return ti_get_serial_info(tport, (struct serial_struct __user *)arg); | 
 | 			break; | 
 |  | 
 | 		case TIOCSSERIAL: | 
 | 			dbg("%s - (%d) TIOCSSERIAL", __FUNCTION__, port->number); | 
 | 			return ti_set_serial_info(tport, (struct serial_struct __user *)arg); | 
 | 			break; | 
 |  | 
 | 		case TIOCMIWAIT: | 
 | 			dbg("%s - (%d) TIOCMIWAIT", __FUNCTION__, port->number); | 
 | 			cprev = tport->tp_icount; | 
 | 			while (1) { | 
 | 				interruptible_sleep_on(&tport->tp_msr_wait); | 
 | 				if (signal_pending(current)) | 
 | 					return -ERESTARTSYS; | 
 | 				cnow = tport->tp_icount; | 
 | 				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && | 
 | 				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) | 
 | 					return -EIO; /* no change => error */ | 
 | 				if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || | 
 | 				    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || | 
 | 				    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) || | 
 | 				    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { | 
 | 					return 0; | 
 | 				} | 
 | 				cprev = cnow; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case TIOCGICOUNT: | 
 | 			dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__, port->number, tport->tp_icount.rx, tport->tp_icount.tx); | 
 | 			if (copy_to_user((void __user *)arg, &tport->tp_icount, sizeof(tport->tp_icount))) | 
 | 				return -EFAULT; | 
 | 			return 0; | 
 | 	} | 
 |  | 
 | 	return -ENOIOCTLCMD; | 
 | } | 
 |  | 
 |  | 
 | static void ti_set_termios(struct usb_serial_port *port, | 
 | 	struct termios *old_termios) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	struct tty_struct *tty = port->tty; | 
 | 	struct ti_uart_config *config; | 
 | 	tcflag_t cflag,iflag; | 
 | 	int baud; | 
 | 	int status; | 
 | 	int port_number = port->number - port->serial->minor; | 
 | 	unsigned int mcr; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (!tty || !tty->termios) { | 
 | 		dbg("%s - no tty or termios", __FUNCTION__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	cflag = tty->termios->c_cflag; | 
 | 	iflag = tty->termios->c_iflag; | 
 |  | 
 | 	if (old_termios && cflag == old_termios->c_cflag | 
 | 	&& iflag == old_termios->c_iflag) { | 
 | 		dbg("%s - nothing to change", __FUNCTION__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	dbg("%s - clfag %08x, iflag %08x", __FUNCTION__, cflag, iflag); | 
 |  | 
 | 	if (old_termios) | 
 | 		dbg("%s - old clfag %08x, old iflag %08x", __FUNCTION__, old_termios->c_cflag, old_termios->c_iflag); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return; | 
 |  | 
 | 	config = kmalloc(sizeof(*config), GFP_KERNEL); | 
 | 	if (!config) { | 
 | 		dev_err(&port->dev, "%s - out of memory\n", __FUNCTION__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	config->wFlags = 0; | 
 |  | 
 | 	/* these flags must be set */ | 
 | 	config->wFlags |= TI_UART_ENABLE_MS_INTS; | 
 | 	config->wFlags |= TI_UART_ENABLE_AUTO_START_DMA; | 
 | 	config->bUartMode = (__u8)(tport->tp_uart_mode); | 
 |  | 
 | 	switch (cflag & CSIZE) { | 
 | 		case CS5: | 
 | 			    config->bDataBits = TI_UART_5_DATA_BITS; | 
 | 			    break; | 
 | 		case CS6: | 
 | 			    config->bDataBits = TI_UART_6_DATA_BITS; | 
 | 			    break; | 
 | 		case CS7: | 
 | 			    config->bDataBits = TI_UART_7_DATA_BITS; | 
 | 			    break; | 
 | 		default: | 
 | 		case CS8: | 
 | 			    config->bDataBits = TI_UART_8_DATA_BITS; | 
 | 			    break; | 
 | 	} | 
 |  | 
 | 	if (cflag & PARENB) { | 
 | 		if (cflag & PARODD) { | 
 | 			config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING; | 
 | 			config->bParity = TI_UART_ODD_PARITY; | 
 | 		} else { | 
 | 			config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING; | 
 | 			config->bParity = TI_UART_EVEN_PARITY; | 
 | 		} | 
 | 	} else { | 
 | 		config->wFlags &= ~TI_UART_ENABLE_PARITY_CHECKING; | 
 | 		config->bParity = TI_UART_NO_PARITY; 	 | 
 | 	} | 
 |  | 
 | 	if (cflag & CSTOPB) | 
 | 		config->bStopBits = TI_UART_2_STOP_BITS; | 
 | 	else | 
 | 		config->bStopBits = TI_UART_1_STOP_BITS; | 
 |  | 
 | 	if (cflag & CRTSCTS) { | 
 | 		/* RTS flow control must be off to drop RTS for baud rate B0 */ | 
 | 		if ((cflag & CBAUD) != B0) | 
 | 			config->wFlags |= TI_UART_ENABLE_RTS_IN; | 
 | 		config->wFlags |= TI_UART_ENABLE_CTS_OUT; | 
 | 	} else { | 
 | 		tty->hw_stopped = 0; | 
 | 		ti_restart_read(tport, tty); | 
 | 	} | 
 |  | 
 | 	if (I_IXOFF(tty) || I_IXON(tty)) { | 
 | 		config->cXon  = START_CHAR(tty); | 
 | 		config->cXoff = STOP_CHAR(tty); | 
 |  | 
 | 		if (I_IXOFF(tty)) | 
 | 			config->wFlags |= TI_UART_ENABLE_X_IN; | 
 | 		else | 
 | 			ti_restart_read(tport, tty); | 
 |  | 
 | 		if (I_IXON(tty)) | 
 | 			config->wFlags |= TI_UART_ENABLE_X_OUT; | 
 | 	} | 
 |  | 
 | 	baud = tty_get_baud_rate(tty); | 
 | 	if (!baud) baud = 9600; | 
 | 	if (tport->tp_tdev->td_is_3410) | 
 | 		config->wBaudRate = (__u16)((923077 + baud/2) / baud); | 
 | 	else | 
 | 		config->wBaudRate = (__u16)((461538 + baud/2) / baud); | 
 |  | 
 | 	dbg("%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d", | 
 | 	__FUNCTION__, baud, config->wBaudRate, config->wFlags, config->bDataBits, config->bParity, config->bStopBits, config->cXon, config->cXoff, config->bUartMode); | 
 |  | 
 | 	cpu_to_be16s(&config->wBaudRate); | 
 | 	cpu_to_be16s(&config->wFlags); | 
 |  | 
 | 	status = ti_command_out_sync(tport->tp_tdev, TI_SET_CONFIG, | 
 | 		(__u8)(TI_UART1_PORT + port_number), 0, (__u8 *)config, | 
 | 		sizeof(*config)); | 
 | 	if (status) | 
 | 		dev_err(&port->dev, "%s - cannot set config on port %d, %d\n", __FUNCTION__, port_number, status); | 
 |  | 
 | 	/* SET_CONFIG asserts RTS and DTR, reset them correctly */ | 
 | 	mcr = tport->tp_shadow_mcr; | 
 | 	/* if baud rate is B0, clear RTS and DTR */ | 
 | 	if ((cflag & CBAUD) == B0) | 
 | 		mcr &= ~(TI_MCR_DTR | TI_MCR_RTS); | 
 | 	status = ti_set_mcr(tport, mcr); | 
 | 	if (status) | 
 | 		dev_err(&port->dev, "%s - cannot set modem control on port %d, %d\n", __FUNCTION__, port_number, status); | 
 |  | 
 | 	kfree(config); | 
 | } | 
 |  | 
 |  | 
 | static int ti_tiocmget(struct usb_serial_port *port, struct file *file) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	unsigned int result; | 
 | 	unsigned int msr; | 
 | 	unsigned int mcr; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return -ENODEV; | 
 |  | 
 | 	msr = tport->tp_msr; | 
 | 	mcr = tport->tp_shadow_mcr; | 
 |  | 
 | 	result = ((mcr & TI_MCR_DTR) ? TIOCM_DTR : 0) | 
 | 		| ((mcr & TI_MCR_RTS) ? TIOCM_RTS : 0) | 
 | 		| ((mcr & TI_MCR_LOOP) ? TIOCM_LOOP : 0) | 
 | 		| ((msr & TI_MSR_CTS) ? TIOCM_CTS : 0) | 
 | 		| ((msr & TI_MSR_CD) ? TIOCM_CAR : 0) | 
 | 		| ((msr & TI_MSR_RI) ? TIOCM_RI : 0) | 
 | 		| ((msr & TI_MSR_DSR) ? TIOCM_DSR : 0); | 
 |  | 
 | 	dbg("%s - 0x%04X", __FUNCTION__, result); | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 |  | 
 | static int ti_tiocmset(struct usb_serial_port *port, struct file *file, | 
 | 	unsigned int set, unsigned int clear) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	unsigned int mcr; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return -ENODEV; | 
 |  | 
 | 	mcr = tport->tp_shadow_mcr; | 
 |  | 
 | 	if (set & TIOCM_RTS) | 
 | 		mcr |= TI_MCR_RTS; | 
 | 	if (set & TIOCM_DTR) | 
 | 		mcr |= TI_MCR_DTR; | 
 | 	if (set & TIOCM_LOOP) | 
 | 		mcr |= TI_MCR_LOOP; | 
 |  | 
 | 	if (clear & TIOCM_RTS) | 
 | 		mcr &= ~TI_MCR_RTS; | 
 | 	if (clear & TIOCM_DTR) | 
 | 		mcr &= ~TI_MCR_DTR; | 
 | 	if (clear & TIOCM_LOOP) | 
 | 		mcr &= ~TI_MCR_LOOP; | 
 |  | 
 | 	return ti_set_mcr(tport, mcr); | 
 | } | 
 |  | 
 |  | 
 | static void ti_break(struct usb_serial_port *port, int break_state) | 
 | { | 
 | 	struct ti_port *tport = usb_get_serial_port_data(port); | 
 | 	int status; | 
 |  | 
 | 	dbg("%s - state = %d", __FUNCTION__, break_state); | 
 |  | 
 | 	if (tport == NULL) | 
 | 		return; | 
 |  | 
 | 	ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 0); | 
 |  | 
 | 	status = ti_write_byte(tport->tp_tdev, | 
 | 		tport->tp_uart_base_addr + TI_UART_OFFSET_LCR, | 
 | 		TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0); | 
 |  | 
 | 	if (status) | 
 | 		dbg("%s - error setting break, %d", __FUNCTION__, status); | 
 | } | 
 |  | 
 |  | 
 | static void ti_interrupt_callback(struct urb *urb) | 
 | { | 
 | 	struct ti_device *tdev = (struct ti_device *)urb->context; | 
 | 	struct usb_serial_port *port; | 
 | 	struct usb_serial *serial = tdev->td_serial; | 
 | 	struct ti_port *tport; | 
 | 	struct device *dev = &urb->dev->dev; | 
 | 	unsigned char *data = urb->transfer_buffer; | 
 | 	int length = urb->actual_length; | 
 | 	int port_number; | 
 | 	int function; | 
 | 	int status; | 
 | 	__u8 msr; | 
 |  | 
 | 	dbg("%s", __FUNCTION__); | 
 |  | 
 | 	switch (urb->status) { | 
 | 	case 0: | 
 | 		break; | 
 | 	case -ECONNRESET: | 
 | 	case -ENOENT: | 
 | 	case -ESHUTDOWN: | 
 | 		dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status); | 
 | 		tdev->td_urb_error = 1; | 
 | 		return; | 
 | 	default: | 
 | 		dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status); | 
 | 		tdev->td_urb_error = 1; | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	if (length != 2) { | 
 | 		dbg("%s - bad packet size, %d", __FUNCTION__, length); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	if (data[0] == TI_CODE_HARDWARE_ERROR) { | 
 | 		dev_err(dev, "%s - hardware error, %d\n", __FUNCTION__, data[1]); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	port_number = TI_GET_PORT_FROM_CODE(data[0]); | 
 | 	function = TI_GET_FUNC_FROM_CODE(data[0]); | 
 |  | 
 | 	dbg("%s - port_number %d, function %d, data 0x%02X", __FUNCTION__, port_number, function, data[1]); | 
 |  | 
 | 	if (port_number >= serial->num_ports) { | 
 | 		dev_err(dev, "%s - bad port number, %d\n", __FUNCTION__, port_number); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	port = serial->port[port_number]; | 
 |  | 
 | 	tport = usb_get_serial_port_data(port); | 
 | 	if (!tport) | 
 | 		goto exit; | 
 |  | 
 | 	switch (function) { | 
 | 	case TI_CODE_DATA_ERROR: | 
 | 		dev_err(dev, "%s - DATA ERROR, port %d, data 0x%02X\n", __FUNCTION__, port_number, data[1]); | 
 | 		break; | 
 |  | 
 | 	case TI_CODE_MODEM_STATUS: | 
 | 		msr = data[1]; | 
 | 		dbg("%s - port %d, msr 0x%02X", __FUNCTION__, port_number, msr); | 
 | 		ti_handle_new_msr(tport, msr); | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		dev_err(dev, "%s - unknown interrupt code, 0x%02X\n", __FUNCTION__, data[1]); | 
 | 		break; | 
 | 	} | 
 |  | 
 | exit: | 
 | 	status = usb_submit_urb(urb, GFP_ATOMIC); | 
 | 	if (status) | 
 | 		dev_err(dev, "%s - resubmit interrupt urb failed, %d\n", __FUNCTION__, status); | 
 | } | 
 |  | 
 |  | 
 | static void ti_bulk_in_callback(struct urb *urb) | 
 | { | 
 | 	struct ti_port *tport = (struct ti_port *)urb->context; | 
 | 	struct usb_serial_port *port = tport->tp_port; | 
 | 	struct device *dev = &urb->dev->dev; | 
 | 	int status = 0; | 
 |  | 
 | 	dbg("%s", __FUNCTION__); | 
 |  | 
 | 	switch (urb->status) { | 
 | 	case 0: | 
 | 		break; | 
 | 	case -ECONNRESET: | 
 | 	case -ENOENT: | 
 | 	case -ESHUTDOWN: | 
 | 		dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status); | 
 | 		tport->tp_tdev->td_urb_error = 1; | 
 | 		wake_up_interruptible(&tport->tp_write_wait); | 
 | 		return; | 
 | 	default: | 
 | 		dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status ); | 
 | 		tport->tp_tdev->td_urb_error = 1; | 
 | 		wake_up_interruptible(&tport->tp_write_wait); | 
 | 	} | 
 |  | 
 | 	if (urb->status == -EPIPE) | 
 | 		goto exit; | 
 |  | 
 | 	if (urb->status) { | 
 | 		dev_err(dev, "%s - stopping read!\n", __FUNCTION__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (port->tty && urb->actual_length) { | 
 | 		usb_serial_debug_data(debug, dev, __FUNCTION__, | 
 | 			urb->actual_length, urb->transfer_buffer); | 
 |  | 
 | 		if (!tport->tp_is_open) | 
 | 			dbg("%s - port closed, dropping data", __FUNCTION__); | 
 | 		else | 
 | 			ti_recv(&urb->dev->dev, port->tty, urb->transfer_buffer, | 
 | 				urb->actual_length); | 
 |  | 
 | 		spin_lock(&tport->tp_lock); | 
 | 		tport->tp_icount.rx += urb->actual_length; | 
 | 		spin_unlock(&tport->tp_lock); | 
 | 	} | 
 |  | 
 | exit: | 
 | 	/* continue to read unless stopping */ | 
 | 	spin_lock(&tport->tp_lock); | 
 | 	if (tport->tp_read_urb_state == TI_READ_URB_RUNNING) { | 
 | 		urb->dev = port->serial->dev; | 
 | 		status = usb_submit_urb(urb, GFP_ATOMIC); | 
 | 	} else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING) { | 
 | 		tport->tp_read_urb_state = TI_READ_URB_STOPPED; | 
 | 	} | 
 | 	spin_unlock(&tport->tp_lock); | 
 | 	if (status) | 
 | 		dev_err(dev, "%s - resubmit read urb failed, %d\n", __FUNCTION__, status); | 
 | } | 
 |  | 
 |  | 
 | static void ti_bulk_out_callback(struct urb *urb) | 
 | { | 
 | 	struct ti_port *tport = (struct ti_port *)urb->context; | 
 | 	struct usb_serial_port *port = tport->tp_port; | 
 | 	struct device *dev = &urb->dev->dev; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	tport->tp_write_urb_in_use = 0; | 
 |  | 
 | 	switch (urb->status) { | 
 | 	case 0: | 
 | 		break; | 
 | 	case -ECONNRESET: | 
 | 	case -ENOENT: | 
 | 	case -ESHUTDOWN: | 
 | 		dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status); | 
 | 		tport->tp_tdev->td_urb_error = 1; | 
 | 		wake_up_interruptible(&tport->tp_write_wait); | 
 | 		return; | 
 | 	default: | 
 | 		dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status); | 
 | 		tport->tp_tdev->td_urb_error = 1; | 
 | 		wake_up_interruptible(&tport->tp_write_wait); | 
 | 	} | 
 |  | 
 | 	/* send any buffered data */ | 
 | 	ti_send(tport); | 
 | } | 
 |  | 
 |  | 
 | static void ti_recv(struct device *dev, struct tty_struct *tty, | 
 | 	unsigned char *data, int length) | 
 | { | 
 | 	int cnt; | 
 |  | 
 | 	do { | 
 | 		cnt = tty_buffer_request_room(tty, length); | 
 | 		if (cnt < length) { | 
 | 			dev_err(dev, "%s - dropping data, %d bytes lost\n", __FUNCTION__, length - cnt); | 
 | 			if(cnt == 0) | 
 | 				break; | 
 | 		} | 
 | 		tty_insert_flip_string(tty, data, cnt); | 
 | 		tty_flip_buffer_push(tty); | 
 | 		data += cnt; | 
 | 		length -= cnt; | 
 | 	} while (length > 0); | 
 |  | 
 | } | 
 |  | 
 |  | 
 | static void ti_send(struct ti_port *tport) | 
 | { | 
 | 	int count, result; | 
 | 	struct usb_serial_port *port = tport->tp_port; | 
 | 	struct tty_struct *tty = port->tty; | 
 | 	unsigned long flags; | 
 |  | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	spin_lock_irqsave(&tport->tp_lock, flags); | 
 |  | 
 | 	if (tport->tp_write_urb_in_use) { | 
 | 		spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	count = ti_buf_get(tport->tp_write_buf, | 
 | 				port->write_urb->transfer_buffer, | 
 | 				port->bulk_out_size); | 
 |  | 
 | 	if (count == 0) { | 
 | 		spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	tport->tp_write_urb_in_use = 1; | 
 |  | 
 | 	spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 |  | 
 | 	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer); | 
 |  | 
 | 	usb_fill_bulk_urb(port->write_urb, port->serial->dev, | 
 | 			   usb_sndbulkpipe(port->serial->dev, | 
 | 					    port->bulk_out_endpointAddress), | 
 | 			   port->write_urb->transfer_buffer, count, | 
 | 			   ti_bulk_out_callback, tport); | 
 |  | 
 | 	result = usb_submit_urb(port->write_urb, GFP_ATOMIC); | 
 | 	if (result) { | 
 | 		dev_err(&port->dev, "%s - submit write urb failed, %d\n", __FUNCTION__, result); | 
 | 		tport->tp_write_urb_in_use = 0;  | 
 | 		/* TODO: reschedule ti_send */ | 
 | 	} else { | 
 | 		spin_lock_irqsave(&tport->tp_lock, flags); | 
 | 		tport->tp_icount.tx += count; | 
 | 		spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 | 	} | 
 |  | 
 | 	/* more room in the buffer for new writes, wakeup */ | 
 | 	if (tty) | 
 | 		tty_wakeup(tty); | 
 | 	wake_up_interruptible(&tport->tp_write_wait); | 
 | } | 
 |  | 
 |  | 
 | static int ti_set_mcr(struct ti_port *tport, unsigned int mcr) | 
 | { | 
 | 	int status; | 
 |  | 
 | 	status = ti_write_byte(tport->tp_tdev, | 
 | 		tport->tp_uart_base_addr + TI_UART_OFFSET_MCR, | 
 | 		TI_MCR_RTS | TI_MCR_DTR | TI_MCR_LOOP, mcr); | 
 |  | 
 | 	if (!status) | 
 | 		tport->tp_shadow_mcr = mcr; | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static int ti_get_lsr(struct ti_port *tport) | 
 | { | 
 | 	int size,status; | 
 | 	struct ti_device *tdev = tport->tp_tdev; | 
 | 	struct usb_serial_port *port = tport->tp_port; | 
 | 	int port_number = port->number - port->serial->minor; | 
 | 	struct ti_port_status *data; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	size = sizeof(struct ti_port_status); | 
 | 	data = kmalloc(size, GFP_KERNEL); | 
 | 	if (!data) { | 
 | 		dev_err(&port->dev, "%s - out of memory\n", __FUNCTION__); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	status = ti_command_in_sync(tdev, TI_GET_PORT_STATUS, | 
 | 		(__u8)(TI_UART1_PORT+port_number), 0, (__u8 *)data, size); | 
 | 	if (status) { | 
 | 		dev_err(&port->dev, "%s - get port status command failed, %d\n", __FUNCTION__, status); | 
 | 		goto free_data; | 
 | 	} | 
 |  | 
 | 	dbg("%s - lsr 0x%02X", __FUNCTION__, data->bLSR); | 
 |  | 
 | 	tport->tp_lsr = data->bLSR; | 
 |  | 
 | free_data: | 
 | 	kfree(data); | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static int ti_get_serial_info(struct ti_port *tport, | 
 | 	struct serial_struct __user *ret_arg) | 
 | { | 
 | 	struct usb_serial_port *port = tport->tp_port; | 
 | 	struct serial_struct ret_serial; | 
 |  | 
 | 	if (!ret_arg) | 
 | 		return -EFAULT; | 
 |  | 
 | 	memset(&ret_serial, 0, sizeof(ret_serial)); | 
 |  | 
 | 	ret_serial.type = PORT_16550A; | 
 | 	ret_serial.line = port->serial->minor; | 
 | 	ret_serial.port = port->number - port->serial->minor; | 
 | 	ret_serial.flags = tport->tp_flags; | 
 | 	ret_serial.xmit_fifo_size = TI_WRITE_BUF_SIZE; | 
 | 	ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800; | 
 | 	ret_serial.closing_wait = tport->tp_closing_wait; | 
 |  | 
 | 	if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg))) | 
 | 		return -EFAULT; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static int ti_set_serial_info(struct ti_port *tport, | 
 | 	struct serial_struct __user *new_arg) | 
 | { | 
 | 	struct usb_serial_port *port = tport->tp_port; | 
 | 	struct serial_struct new_serial; | 
 |  | 
 | 	if (copy_from_user(&new_serial, new_arg, sizeof(new_serial))) | 
 | 		return -EFAULT; | 
 |  | 
 | 	tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; | 
 | 	if (port->tty) | 
 | 		port->tty->low_latency = | 
 | 			(tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; | 
 | 	tport->tp_closing_wait = new_serial.closing_wait; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) | 
 | { | 
 | 	struct async_icount *icount; | 
 | 	struct tty_struct *tty; | 
 | 	unsigned long flags; | 
 |  | 
 | 	dbg("%s - msr 0x%02X", __FUNCTION__, msr); | 
 |  | 
 | 	if (msr & TI_MSR_DELTA_MASK) { | 
 | 		spin_lock_irqsave(&tport->tp_lock, flags); | 
 | 		icount = &tport->tp_icount; | 
 | 		if (msr & TI_MSR_DELTA_CTS) | 
 | 			icount->cts++; | 
 | 		if (msr & TI_MSR_DELTA_DSR) | 
 | 			icount->dsr++; | 
 | 		if (msr & TI_MSR_DELTA_CD) | 
 | 			icount->dcd++; | 
 | 		if (msr & TI_MSR_DELTA_RI) | 
 | 			icount->rng++; | 
 | 		wake_up_interruptible(&tport->tp_msr_wait); | 
 | 		spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 | 	} | 
 |  | 
 | 	tport->tp_msr = msr & TI_MSR_MASK; | 
 |  | 
 | 	/* handle CTS flow control */ | 
 | 	tty = tport->tp_port->tty; | 
 | 	if (tty && C_CRTSCTS(tty)) { | 
 | 		if (msr & TI_MSR_CTS) { | 
 | 			tty->hw_stopped = 0; | 
 | 			tty_wakeup(tty); | 
 | 		} else { | 
 | 			tty->hw_stopped = 1; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush) | 
 | { | 
 | 	struct ti_device *tdev = tport->tp_tdev; | 
 | 	struct usb_serial_port *port = tport->tp_port; | 
 | 	wait_queue_t wait; | 
 | 	unsigned long flags; | 
 |  | 
 | 	dbg("%s - port %d", __FUNCTION__, port->number); | 
 |  | 
 | 	spin_lock_irqsave(&tport->tp_lock, flags); | 
 |  | 
 | 	/* wait for data to drain from the buffer */ | 
 | 	tdev->td_urb_error = 0; | 
 | 	init_waitqueue_entry(&wait, current); | 
 | 	add_wait_queue(&tport->tp_write_wait, &wait); | 
 | 	for (;;) { | 
 | 		set_current_state(TASK_INTERRUPTIBLE); | 
 | 		if (ti_buf_data_avail(tport->tp_write_buf) == 0 | 
 | 		|| timeout == 0 || signal_pending(current) | 
 | 		|| tdev->td_urb_error | 
 | 		|| !usb_get_intfdata(port->serial->interface))  /* disconnect */ | 
 | 			break; | 
 | 		spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 | 		timeout = schedule_timeout(timeout); | 
 | 		spin_lock_irqsave(&tport->tp_lock, flags); | 
 | 	} | 
 | 	set_current_state(TASK_RUNNING); | 
 | 	remove_wait_queue(&tport->tp_write_wait, &wait); | 
 |  | 
 | 	/* flush any remaining data in the buffer */ | 
 | 	if (flush) | 
 | 		ti_buf_clear(tport->tp_write_buf); | 
 |  | 
 | 	spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 |  | 
 | 	/* wait for data to drain from the device */ | 
 | 	/* wait for empty tx register, plus 20 ms */ | 
 | 	timeout += jiffies; | 
 | 	tport->tp_lsr &= ~TI_LSR_TX_EMPTY; | 
 | 	while ((long)(jiffies - timeout) < 0 && !signal_pending(current) | 
 | 	&& !(tport->tp_lsr&TI_LSR_TX_EMPTY) && !tdev->td_urb_error | 
 | 	&& usb_get_intfdata(port->serial->interface)) {  /* not disconnected */ | 
 | 		if (ti_get_lsr(tport)) | 
 | 			break; | 
 | 		msleep_interruptible(20); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty) | 
 | { | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&tport->tp_lock, flags); | 
 |  | 
 | 	if (tport->tp_read_urb_state == TI_READ_URB_RUNNING) | 
 | 		tport->tp_read_urb_state = TI_READ_URB_STOPPING; | 
 |  | 
 | 	spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 | } | 
 |  | 
 |  | 
 | static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty) | 
 | { | 
 | 	struct urb *urb; | 
 | 	int status = 0; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&tport->tp_lock, flags); | 
 |  | 
 | 	if (tport->tp_read_urb_state == TI_READ_URB_STOPPED) { | 
 | 		urb = tport->tp_port->read_urb; | 
 | 		urb->complete = ti_bulk_in_callback; | 
 | 		urb->context = tport; | 
 | 		urb->dev = tport->tp_port->serial->dev; | 
 | 		status = usb_submit_urb(urb, GFP_KERNEL); | 
 | 	} | 
 | 	tport->tp_read_urb_state = TI_READ_URB_RUNNING; | 
 |  | 
 | 	spin_unlock_irqrestore(&tport->tp_lock, flags); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static int ti_command_out_sync(struct ti_device *tdev, __u8 command, | 
 | 	__u16 moduleid, __u16 value, __u8 *data, int size) | 
 | { | 
 | 	int status; | 
 |  | 
 | 	status = usb_control_msg(tdev->td_serial->dev, | 
 | 		usb_sndctrlpipe(tdev->td_serial->dev, 0), command, | 
 | 		(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT), | 
 | 		value, moduleid, data, size, 1000); | 
 |  | 
 | 	if (status == size) | 
 | 		status = 0; | 
 |  | 
 | 	if (status > 0) | 
 | 		status = -ECOMM; | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static int ti_command_in_sync(struct ti_device *tdev, __u8 command, | 
 | 	__u16 moduleid, __u16 value, __u8 *data, int size) | 
 | { | 
 | 	int status; | 
 |  | 
 | 	status = usb_control_msg(tdev->td_serial->dev, | 
 | 		usb_rcvctrlpipe(tdev->td_serial->dev, 0), command, | 
 | 		(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN), | 
 | 		value, moduleid, data, size, 1000); | 
 |  | 
 | 	if (status == size) | 
 | 		status = 0; | 
 |  | 
 | 	if (status > 0) | 
 | 		status = -ECOMM; | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static int ti_write_byte(struct ti_device *tdev, unsigned long addr, | 
 | 	__u8 mask, __u8 byte) | 
 | { | 
 | 	int status; | 
 | 	unsigned int size; | 
 | 	struct ti_write_data_bytes *data; | 
 | 	struct device *dev = &tdev->td_serial->dev->dev; | 
 |  | 
 | 	dbg("%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X", __FUNCTION__, addr, mask, byte); | 
 |  | 
 | 	size = sizeof(struct ti_write_data_bytes) + 2; | 
 | 	data = kmalloc(size, GFP_KERNEL); | 
 | 	if (!data) { | 
 | 		dev_err(dev, "%s - out of memory\n", __FUNCTION__); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	data->bAddrType = TI_RW_DATA_ADDR_XDATA; | 
 | 	data->bDataType = TI_RW_DATA_BYTE; | 
 | 	data->bDataCounter = 1; | 
 | 	data->wBaseAddrHi = cpu_to_be16(addr>>16); | 
 | 	data->wBaseAddrLo = cpu_to_be16(addr); | 
 | 	data->bData[0] = mask; | 
 | 	data->bData[1] = byte; | 
 |  | 
 | 	status = ti_command_out_sync(tdev, TI_WRITE_DATA, TI_RAM_PORT, 0, | 
 | 		(__u8 *)data, size); | 
 |  | 
 | 	if (status < 0) | 
 | 		dev_err(dev, "%s - failed, %d\n", __FUNCTION__, status); | 
 |  | 
 | 	kfree(data); | 
 |  | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | static int ti_download_firmware(struct ti_device *tdev, | 
 | 	unsigned char *firmware, unsigned int firmware_size) | 
 | { | 
 | 	int status = 0; | 
 | 	int buffer_size; | 
 | 	int pos; | 
 | 	int len; | 
 | 	int done; | 
 | 	__u8 cs = 0; | 
 | 	__u8 *buffer; | 
 | 	struct usb_device *dev = tdev->td_serial->dev; | 
 | 	struct ti_firmware_header *header; | 
 | 	unsigned int pipe = usb_sndbulkpipe(dev, | 
 | 		tdev->td_serial->port[0]->bulk_out_endpointAddress); | 
 |  | 
 |  | 
 | 	buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header); | 
 | 	buffer = kmalloc(buffer_size, GFP_KERNEL); | 
 | 	if (!buffer) { | 
 | 		dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	memcpy(buffer, firmware, firmware_size); | 
 | 	memset(buffer+firmware_size, 0xff, buffer_size-firmware_size); | 
 |  | 
 | 	for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++) | 
 | 		cs = (__u8)(cs + buffer[pos]); | 
 |  | 
 | 	header = (struct ti_firmware_header *)buffer; | 
 | 	header->wLength = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_firmware_header))); | 
 | 	header->bCheckSum = cs; | 
 |  | 
 | 	dbg("%s - downloading firmware", __FUNCTION__); | 
 | 	for (pos = 0; pos < buffer_size; pos += done) { | 
 | 		len = min(buffer_size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE); | 
 | 		status = usb_bulk_msg(dev, pipe, buffer+pos, len, &done, 1000); | 
 | 		if (status) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	kfree(buffer); | 
 |  | 
 | 	if (status) { | 
 | 		dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __FUNCTION__, status); | 
 | 		return status; | 
 | 	} | 
 |  | 
 | 	dbg("%s - download successful", __FUNCTION__); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | /* Circular Buffer Functions */ | 
 |  | 
 | /* | 
 |  * ti_buf_alloc | 
 |  * | 
 |  * Allocate a circular buffer and all associated memory. | 
 |  */ | 
 |  | 
 | static struct circ_buf *ti_buf_alloc(void) | 
 | { | 
 | 	struct circ_buf *cb; | 
 |  | 
 | 	cb = (struct circ_buf *)kmalloc(sizeof(struct circ_buf), GFP_KERNEL); | 
 | 	if (cb == NULL) | 
 | 		return NULL; | 
 |  | 
 | 	cb->buf = kmalloc(TI_WRITE_BUF_SIZE, GFP_KERNEL); | 
 | 	if (cb->buf == NULL) { | 
 | 		kfree(cb); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	ti_buf_clear(cb); | 
 |  | 
 | 	return cb; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * ti_buf_free | 
 |  * | 
 |  * Free the buffer and all associated memory. | 
 |  */ | 
 |  | 
 | static void ti_buf_free(struct circ_buf *cb) | 
 | { | 
 | 	kfree(cb->buf); | 
 | 	kfree(cb); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * ti_buf_clear | 
 |  * | 
 |  * Clear out all data in the circular buffer. | 
 |  */ | 
 |  | 
 | static void ti_buf_clear(struct circ_buf *cb) | 
 | { | 
 | 	cb->head = cb->tail = 0; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * ti_buf_data_avail | 
 |  * | 
 |  * Return the number of bytes of data available in the circular | 
 |  * buffer. | 
 |  */ | 
 |  | 
 | static int ti_buf_data_avail(struct circ_buf *cb) | 
 | { | 
 | 	return CIRC_CNT(cb->head,cb->tail,TI_WRITE_BUF_SIZE); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * ti_buf_space_avail | 
 |  * | 
 |  * Return the number of bytes of space available in the circular | 
 |  * buffer. | 
 |  */ | 
 |  | 
 | static int ti_buf_space_avail(struct circ_buf *cb) | 
 | { | 
 | 	return CIRC_SPACE(cb->head,cb->tail,TI_WRITE_BUF_SIZE); | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * ti_buf_put | 
 |  * | 
 |  * Copy data data from a user buffer and put it into the circular buffer. | 
 |  * Restrict to the amount of space available. | 
 |  * | 
 |  * Return the number of bytes copied. | 
 |  */ | 
 |  | 
 | static int ti_buf_put(struct circ_buf *cb, const char *buf, int count) | 
 | { | 
 | 	int c, ret = 0; | 
 |  | 
 | 	while (1) { | 
 | 		c = CIRC_SPACE_TO_END(cb->head, cb->tail, TI_WRITE_BUF_SIZE); | 
 | 		if (count < c) | 
 | 			c = count; | 
 | 		if (c <= 0) | 
 | 			break; | 
 | 		memcpy(cb->buf + cb->head, buf, c); | 
 | 		cb->head = (cb->head + c) & (TI_WRITE_BUF_SIZE-1); | 
 | 		buf += c; | 
 | 		count -= c; | 
 | 		ret += c; | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * ti_buf_get | 
 |  * | 
 |  * Get data from the circular buffer and copy to the given buffer. | 
 |  * Restrict to the amount of data available. | 
 |  * | 
 |  * Return the number of bytes copied. | 
 |  */ | 
 |  | 
 | static int ti_buf_get(struct circ_buf *cb, char *buf, int count) | 
 | { | 
 | 	int c, ret = 0; | 
 |  | 
 | 	while (1) { | 
 | 		c = CIRC_CNT_TO_END(cb->head, cb->tail, TI_WRITE_BUF_SIZE); | 
 | 		if (count < c) | 
 | 			c = count; | 
 | 		if (c <= 0) | 
 | 			break; | 
 | 		memcpy(buf, cb->buf + cb->tail, c); | 
 | 		cb->tail = (cb->tail + c) & (TI_WRITE_BUF_SIZE-1); | 
 | 		buf += c; | 
 | 		count -= c; | 
 | 		ret += c; | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } |