whiteheat.c revision 5cbded585d129d0226cb48ac4202b253c781be26
1a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
2ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek * USB ConnectTech WhiteHEAT driver
3ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *
4a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Copyright (C) 2002
5a5fb297d634ba20bd53a7d6fecd611bbfd342e78Lennert Buytenhek *	    Connect Tech Inc.
6a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
7a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Copyright (C) 1999 - 2001
8a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	    Greg Kroah-Hartman (greg@kroah.com)
9a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
10a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	This program is free software; you can redistribute it and/or modify
11a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	it under the terms of the GNU General Public License as published by
12a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	the Free Software Foundation; either version 2 of the License, or
13a6b7a407865aab9f849dd99a71072b7cd1175116Alexey Dobriyan *	(at your option) any later version.
14a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
15a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * See Documentation/usb/usb-serial.txt for more information on using this driver
163d76e82c9538d8104e578ca460d35f214bfddfd3Lennert Buytenhek *
17a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * (10/09/2002) Stuart MacDonald (stuartm@connecttech.com)
18a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Upgrade to full working driver
19a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
20a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * (05/30/2001) gkh
21a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	switched from using spinlock to a semaphore, which fixes lots of problems.
22a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
235a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo * (04/08/2001) gb
24a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Identify version on module load.
25a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
26a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * 2001_Mar_19 gkh
27a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Fixed MOD_INC and MOD_DEC logic, the ability to open a port more
28a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	than once, and the got the proper usb_device_id table entries so
29a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	the driver works again.
30a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
3100e8e69270cc8d5b9db98dcb73d26f21c2539010Yogesh Ashok Powar * (11/01/2000) Adam J. Richter
32a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	usb_device_id table support
330863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo *
34eb93992207dadb946a3b5cf4544957dc924a6f58Rusty Russell * (10/05/2000) gkh
350863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo *	Fixed bug with urb->dev not being set properly, now that the usb
360863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo *	core needs it.
370863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo *
380863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo * (10/03/2000) smd
39a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	firmware is improved to guard against crap sent to device
40a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	firmware now replies CMD_FAILURE on bad things
41ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *	read_callback fix you provided for private info struct
42ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *	command_finished now indicates success or fail
43a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	setup_port struct now packed to avoid gcc padding
44ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *	firmware uses 1 based port numbering, driver now handles that
45ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *
46ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek * (09/11/2000) gkh
47a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
48a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
49a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * (07/19/2000) gkh
50a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Added module_init and module_exit functions to handle the fact that this
51a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	driver is a loadable module now.
52a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Fixed bug with port->minor that was found by Al Borchers
53a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
54a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * (07/04/2000) gkh
55ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *	Added support for port settings. Baud rate can now be changed. Line signals
56ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *	are not transferred to and from the tty layer yet, but things seem to be
57ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *	working well now.
58ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek *
59a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * (05/04/2000) gkh
60a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	First cut at open and close commands. Data can flow through the ports at
61a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	default speeds now.
62a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
63a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * (03/26/2000) gkh
64a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *	Split driver up into device specific pieces.
65a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *
66ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek */
673aefc37ee789188f0d4488cae04ff618f4c4ddf6Nishant Sarmukadam
68ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/kernel.h>
69ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/errno.h>
70ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/init.h>
71ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/slab.h>
72ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/tty.h>
73ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/tty_driver.h>
74ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/tty_flip.h>
75ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/module.h>
76ce9e2e1b8433c8795459a259ee87bc4e424e7c50Lennert Buytenhek#include <linux/spinlock.h>
77a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#include <asm/uaccess.h>
78566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat#include <asm/termbits.h>
79566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat#include <linux/usb.h>
80566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat#include <linux/serial_reg.h>
81566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat#include <linux/serial.h>
82566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat#include <linux/usb/serial.h>
83566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat#include "whiteheat_fw.h"		/* firmware for the ConnectTech WhiteHEAT device */
84566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat#include "whiteheat.h"			/* WhiteHEAT specific commands */
85566875db5058f582ea56da891f9c3cabc01efff5Pradeep Nemavat
86a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int debug;
87a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
88a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#ifndef CMSPAR
89a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define CMSPAR 0
90a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#endif
91a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
92a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
93a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Version Information
94a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek */
953aefc37ee789188f0d4488cae04ff618f4c4ddf6Nishant Sarmukadam#define DRIVER_VERSION "v2.0"
963aefc37ee789188f0d4488cae04ff618f4c4ddf6Nishant Sarmukadam#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>"
97a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
98a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
99e600707b021efdc109e7becd467798da339ec26dBrian Cavagnolo#define CONNECT_TECH_VENDOR_ID		0x0710
1008a7a578c2e3ac463a17fe30b11ada0509658a952Brian Cavagnolo#define CONNECT_TECH_FAKE_WHITE_HEAT_ID	0x0001
101e600707b021efdc109e7becd467798da339ec26dBrian Cavagnolo#define CONNECT_TECH_WHITE_HEAT_ID	0x8001
102e600707b021efdc109e7becd467798da339ec26dBrian Cavagnolo
103a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
1047fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar   ID tables for whiteheat are unusual, because we want to different
1057fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar   things for different versions of the device.  Eventually, this
1067fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar   will be doable from a single table.  But, for now, we define two
1077fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar   separate ID tables, and then a third table that combines them
1087fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar   just for the purpose of exporting the autoloading information.
1097fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar*/
1107fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powarstatic struct usb_device_id id_table_std [] = {
1117fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
1127fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar	{ }						/* Terminating entry */
1137fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar};
1147fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powar
1157fb978b7e93b5c4a378eba5767c7513540b56642Yogesh Ashok Powarstatic struct usb_device_id id_table_prerenumeration [] = {
11654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
11754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	{ }						/* Terminating entry */
11854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek};
11954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
12020f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhekstatic struct usb_device_id id_table_combined [] = {
1210d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
12254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	{ USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
12354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	{ }						/* Terminating entry */
12445a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek};
125a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek
126a74b295edb3e2b39d6c03255b24dca862a843c59Lennert BuytenhekMODULE_DEVICE_TABLE (usb, id_table_combined);
1270863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo
1280863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolostatic struct usb_driver whiteheat_driver = {
12989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	.name =		"whiteheat",
130952a0e963fb02e50f4afbf502f7d468a8fe2b0faBrian Cavagnolo	.probe =	usb_serial_probe,
13145a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek	.disconnect =	usb_serial_disconnect,
13245a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek	.id_table =	id_table_combined,
133a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.no_dynamic_id = 	1,
13445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek};
135a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
136a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
13745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhekstatic int  whiteheat_firmware_download	(struct usb_serial *serial, const struct usb_device_id *id);
138a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int  whiteheat_firmware_attach	(struct usb_serial *serial);
139a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
14045eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek/* function prototypes for the Connect Tech WhiteHEAT serial converter */
141a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int  whiteheat_attach		(struct usb_serial *serial);
14254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhekstatic void whiteheat_shutdown		(struct usb_serial *serial);
14345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhekstatic int  whiteheat_open		(struct usb_serial_port *port, struct file *filp);
144788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhekstatic void whiteheat_close		(struct usb_serial_port *port, struct file *filp);
145788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhekstatic int  whiteheat_write		(struct usb_serial_port *port, const unsigned char *buf, int count);
14653b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonoristatic int  whiteheat_write_room	(struct usb_serial_port *port);
147788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhekstatic int  whiteheat_ioctl		(struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
148a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void whiteheat_set_termios	(struct usb_serial_port *port, struct ktermios * old);
149a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int  whiteheat_tiocmget		(struct usb_serial_port *port, struct file *file);
150a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int  whiteheat_tiocmset		(struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear);
151a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void whiteheat_break_ctl		(struct usb_serial_port *port, int break_state);
15245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhekstatic int  whiteheat_chars_in_buffer	(struct usb_serial_port *port);
153a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void whiteheat_throttle		(struct usb_serial_port *port);
154a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void whiteheat_unthrottle	(struct usb_serial_port *port);
15545eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhekstatic void whiteheat_read_callback	(struct urb *urb);
156a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void whiteheat_write_callback	(struct urb *urb);
1578ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo
15845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhekstatic struct usb_serial_driver whiteheat_fake_device = {
15945eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	.driver = {
16045eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		.owner =	THIS_MODULE,
161a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		.name =		"whiteheatnofirm",
162a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	},
163ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	.description =		"Connect Tech - WhiteHEAT - (prerenumeration)",
164ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	.id_table =		id_table_prerenumeration,
165ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	.num_interrupt_in =	NUM_DONT_CARE,
166ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	.num_bulk_in =		NUM_DONT_CARE,
167ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	.num_bulk_out =		NUM_DONT_CARE,
168ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	.num_ports =		1,
169ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	.probe =		whiteheat_firmware_download,
1705faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam	.attach =		whiteheat_firmware_attach,
1715faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam};
1725faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam
1735faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadamstatic struct usb_serial_driver whiteheat_device = {
1745faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam	.driver = {
1755faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam		.owner =	THIS_MODULE,
1765faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam		.name =		"whiteheat",
177a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	},
178a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.description =		"Connect Tech - WhiteHEAT",
179a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.id_table =		id_table_std,
180bf3ca7f752d8f5009c9a83db56035566f3e313deBrian Cavagnolo	.num_interrupt_in =	NUM_DONT_CARE,
181a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.num_bulk_in =		NUM_DONT_CARE,
18245a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek	.num_bulk_out =		NUM_DONT_CARE,
18345a390ddd70d8bec0b17dd37bc8f77372c9c3d33Lennert Buytenhek	.num_ports =		4,
184be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	.attach =		whiteheat_attach,
185be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	.shutdown =		whiteheat_shutdown,
186be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	.open =			whiteheat_open,
187be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	.close =		whiteheat_close,
188d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	.write =		whiteheat_write,
189d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	.write_room =		whiteheat_write_room,
190a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	.ioctl =		whiteheat_ioctl,
191be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	.set_termios =		whiteheat_set_termios,
192be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	.break_ctl =		whiteheat_break_ctl,
193be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek	.tiocmget =		whiteheat_tiocmget,
194777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	.tiocmset =		whiteheat_tiocmset,
195777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	.chars_in_buffer =	whiteheat_chars_in_buffer,
196777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	.throttle =		whiteheat_throttle,
1974eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	.unthrottle =		whiteheat_unthrottle,
1984eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	.read_bulk_callback =	whiteheat_read_callback,
1994eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	.write_bulk_callback =	whiteheat_write_callback,
200ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek};
201ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek
202be695fc4f0a7ff9c9f91d59536b029b781cfea62Lennert Buytenhek
2038a7a578c2e3ac463a17fe30b11ada0509658a952Brian Cavagnolostruct whiteheat_command_private {
2048a7a578c2e3ac463a17fe30b11ada0509658a952Brian Cavagnolo	spinlock_t		lock;
205ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	__u8			port_running;
206ac109fd0427008e5b55e0e52e59c364de6d686feBrian Cavagnolo	__u8			command_finished;
2073aefc37ee789188f0d4488cae04ff618f4c4ddf6Nishant Sarmukadam	wait_queue_head_t	wait_command;	/* for handling sleeping while waiting for a command to finish */
2088a7a578c2e3ac463a17fe30b11ada0509658a952Brian Cavagnolo	__u8			result_buffer[64];
209618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek};
210618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
211618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
2126b6accc3832e5a124eeb144c6b3b1ff65b503d2bYogesh Ashok Powar#define THROTTLED		0x01
213618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek#define ACTUALLY_THROTTLED	0x02
214618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
215618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhekstatic int urb_pool_size = 8;
216c27a54d3f12383789b57a404649daf98a05cbd72Yogesh Ashok Powar
217c27a54d3f12383789b57a404649daf98a05cbd72Yogesh Ashok Powarstruct whiteheat_urb_wrap {
218a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head	list;
219a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct urb		*urb;
220a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
22188de754ad59025eba797e7a8375807755577f450Lennert Buytenhek
22288de754ad59025eba797e7a8375807755577f450Lennert Buytenhekstruct whiteheat_private {
22388de754ad59025eba797e7a8375807755577f450Lennert Buytenhek	spinlock_t		lock;
224f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	__u8			flags;
225ee0ddf1865954f44ee929d963e2c968eb377f447Lennert Buytenhek	__u8			mcr;
226f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek	struct list_head	rx_urbs_free;
227a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head	rx_urbs_submitted;
228a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head	rx_urb_q;
229a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct work_struct	rx_work;
230a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct usb_serial_port	*port;
231a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head	tx_urbs_free;
232a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head	tx_urbs_submitted;
233a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek};
2342aa7b01fe4f2d0978115bfd40364f52d86003606Lennert Buytenhek
235a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
236a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/* local function prototypes */
237a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int start_command_port(struct usb_serial *serial);
238a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void stop_command_port(struct usb_serial *serial);
239a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void command_port_write_callback(struct urb *urb);
240a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void command_port_read_callback(struct urb *urb);
241a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
242a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int start_port_read(struct usb_serial_port *port);
243e600707b021efdc109e7becd467798da339ec26dBrian Cavagnolostatic struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb, struct list_head *head);
244e600707b021efdc109e7becd467798da339ec26dBrian Cavagnolostatic struct list_head *list_first(struct list_head *head);
245a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void rx_data_softint(struct work_struct *work);
246c46563b714b09d44eec4d1fd8a0f53e79ddaa3aaLennert Buytenhek
24768ce38845c23443b15e374fb7362916c1231278eLennert Buytenhekstatic int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize);
248a43c49a817f31ce1accc029239827b108319ecf9Lennert Buytenhekstatic int firm_open(struct usb_serial_port *port);
2490439b1f55646ea944b0d58337f5065b79a1c1be0Lennert Buytenhekstatic int firm_close(struct usb_serial_port *port);
250a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_setup_port(struct usb_serial_port *port);
251a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
252a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
253d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhekstatic int firm_set_break(struct usb_serial_port *port, __u8 onoff);
254a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_purge(struct usb_serial_port *port, __u8 rxtx);
255a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_get_dtr_rts(struct usb_serial_port *port);
256a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_report_tx_done(struct usb_serial_port *port);
257a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
258a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
259a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define COMMAND_PORT		4
260a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define COMMAND_TIMEOUT		(2*HZ)	/* 2 second timeout for a command */
261a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define	COMMAND_TIMEOUT_MS	2000
262a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek#define CLOSING_DELAY		(30 * HZ)
263a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
2641e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek
2651e9f9de3b17db3aa358f39d6932662324178350dLennert Buytenhek/*****************************************************************************
26667e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek * Connect Tech's White Heat prerenumeration driver functions
26767e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek *****************************************************************************/
26867e2eb27958cae758ccbc86443c360ec285acc3eLennert Buytenhek
2690d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville/* steps to download the firmware to the WhiteHEAT device:
2700d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville - hold the reset (by writing to the reset bit of the CPUCS register)
2710d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
2720863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo - release the reset (by writing to the CPUCS register)
2730863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo - download the WH.HEX file for all addresses greater than 0x1b3f using
2740863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo   VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
2750863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo - hold the reset
2760863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo - download the WH.HEX file for all addresses less than 0x1b40 using
277e600707b021efdc109e7becd467798da339ec26dBrian Cavagnolo   VENDOR_REQUEST_ANCHOR_LOAD
27899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo - release the reset
2796b6accc3832e5a124eeb144c6b3b1ff65b503d2bYogesh Ashok Powar - device renumerated itself and comes up as new device id with all
2806b6accc3832e5a124eeb144c6b3b1ff65b503d2bYogesh Ashok Powar   firmware download completed.
2816b6accc3832e5a124eeb144c6b3b1ff65b503d2bYogesh Ashok Powar*/
2826b6accc3832e5a124eeb144c6b3b1ff65b503d2bYogesh Ashok Powarstatic int whiteheat_firmware_download (struct usb_serial *serial, const struct usb_device_id *id)
28399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo{
28499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	int response;
28599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	const struct whiteheat_hex_record *record;
28699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
28799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	dbg("%s", __FUNCTION__);
288e882efc96dba0a48adbe460b04d552d9a749a7c0Yogesh Ashok Powar
289e882efc96dba0a48adbe460b04d552d9a749a7c0Yogesh Ashok Powar	response = ezusb_set_reset (serial, 1);
290e882efc96dba0a48adbe460b04d552d9a749a7c0Yogesh Ashok Powar
291a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	record = &whiteheat_loader[0];
292a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	while (record->address != 0xffff) {
293e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		response = ezusb_writememory (serial, record->address,
294e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam				(unsigned char *)record->data, record->data_size, 0xa0);
295e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		if (response < 0) {
296a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			err("%s - ezusb_writememory failed for loader (%d %04X %p %d)",
297a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				__FUNCTION__, response, record->address, record->data, record->data_size);
298f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek			break;
299f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek		}
300f5bb87cfba5ae9dd3724b846ac2fa7e033425c1cLennert Buytenhek		++record;
301f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	}
302f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek
303f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek	response = ezusb_set_reset (serial, 0);
304c2c2b12a8b6cd23d4abbc086642647c656bf406cLennert Buytenhek
305a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	record = &whiteheat_firmware[0];
306e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	while (record->address < 0x1b40) {
307e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		++record;
308e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	}
309e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	while (record->address != 0xffff) {
310e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		response = ezusb_writememory (serial, record->address,
311e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam				(unsigned char *)record->data, record->data_size, 0xa3);
312d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		if (response < 0) {
313d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			err("%s - ezusb_writememory failed for first firmware step (%d %04X %p %d)",
314d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				__FUNCTION__, response, record->address, record->data, record->data_size);
315d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			break;
316d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		}
317d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		++record;
318a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
319a94cc97e14c5750ec2b50b2e4ecdfb0f369ed0f4Lennert Buytenhek
320fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam	response = ezusb_set_reset (serial, 1);
321a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
322d0805c1c5758f8fd16c88bf1efa8fb4be4408ce1Brian Cavagnolo	record = &whiteheat_firmware[0];
323d0805c1c5758f8fd16c88bf1efa8fb4be4408ce1Brian Cavagnolo	while (record->address < 0x1b40) {
324d0805c1c5758f8fd16c88bf1efa8fb4be4408ce1Brian Cavagnolo		response = ezusb_writememory (serial, record->address,
325d0805c1c5758f8fd16c88bf1efa8fb4be4408ce1Brian Cavagnolo				(unsigned char *)record->data, record->data_size, 0xa0);
326d0805c1c5758f8fd16c88bf1efa8fb4be4408ce1Brian Cavagnolo		if (response < 0) {
327d0805c1c5758f8fd16c88bf1efa8fb4be4408ce1Brian Cavagnolo			err("%s - ezusb_writememory failed for second firmware step (%d %04X %p %d)",
328a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek				__FUNCTION__, response, record->address, record->data, record->data_size);
329a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek			break;
330a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek		}
331170335432ad36584a6d24fc1fd903024d221ef55Nishant Sarmukadam		++record;
332d0805c1c5758f8fd16c88bf1efa8fb4be4408ce1Brian Cavagnolo	}
333a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
334a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek	response = ezusb_set_reset (serial, 0);
335a680400e8ac32adda81b5e2d7f23dfac63e064feLennert Buytenhek
336777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	return 0;
337a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
338a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
339a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
340a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int whiteheat_firmware_attach (struct usb_serial *serial)
341a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
342a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* We want this device to fail to have a driver assigned to it */
343a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return 1;
344a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
345a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
346a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
347a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*****************************************************************************
348647ca6b01a5289948e970ea7c1f656f9d90b0a27Lennert Buytenhek * Connect Tech's White Heat serial driver functions
349647ca6b01a5289948e970ea7c1f656f9d90b0a27Lennert Buytenhek *****************************************************************************/
350647ca6b01a5289948e970ea7c1f656f9d90b0a27Lennert Buytenhekstatic int whiteheat_attach (struct usb_serial *serial)
351a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
352a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct usb_serial_port *command_port;
353777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek	struct whiteheat_command_private *command_info;
354a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct usb_serial_port *port;
355a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_private *info;
356a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_hw_info *hw_info;
3575dfd3e2c6fb69cf4295ec139107f4ebd3f7fbff0Lennert Buytenhek	int pipe;
3585dfd3e2c6fb69cf4295ec139107f4ebd3f7fbff0Lennert Buytenhek	int ret;
359a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int alen;
360a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 *command;
361a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	__u8 *result;
362a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int i;
363a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int j;
364a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct urb *urb;
365a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int buf_size;
366a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_urb_wrap *wrap;
367140eb5e2c1978622d7cd979d59a1c0586fe3bbdbLennert Buytenhek	struct list_head *tmp;
368140eb5e2c1978622d7cd979d59a1c0586fe3bbdbLennert Buytenhek
369140eb5e2c1978622d7cd979d59a1c0586fe3bbdbLennert Buytenhek	command_port = serial->port[COMMAND_PORT];
3704eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
3714eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	pipe = usb_sndbulkpipe (serial->dev, command_port->bulk_out_endpointAddress);
3724eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	command = kmalloc(2, GFP_KERNEL);
3734eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	if (!command)
3744eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek		goto no_command_buffer;
3754eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	command[0] = WHITEHEAT_GET_HW_INFO;
3764eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	command[1] = 0;
3774eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek
3784eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL);
3794eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	if (!result)
3804eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek		goto no_result_buffer;
3814eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	/*
3824eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	 * When the module is reloaded the firmware is still there and
3834eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	 * the endpoints are still in the usb core unchanged. This is the
3844eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek         * unlinking bug in disguise. Same for the call below.
3854eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek         */
3864eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	usb_clear_halt(serial->dev, pipe);
3874eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	ret = usb_bulk_msg (serial->dev, pipe, command, 2, &alen, COMMAND_TIMEOUT_MS);
3884eae9edd38c0a9ce34e39100ccc69ff520bc1224Lennert Buytenhek	if (ret) {
389a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		err("%s: Couldn't send command [%d]", serial->type->description, ret);
390a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto no_firmware;
39141fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam	} else if (alen != 2) {
39241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		err("%s: Send command incomplete [%d]", serial->type->description, alen);
393a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto no_firmware;
394a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
395a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
396a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	pipe = usb_rcvbulkpipe (serial->dev, command_port->bulk_in_endpointAddress);
39742fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek	/* See the comment on the usb_clear_halt() above */
398a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	usb_clear_halt(serial->dev, pipe);
399a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	ret = usb_bulk_msg (serial->dev, pipe, result, sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS);
400ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek	if (ret) {
401ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		err("%s: Couldn't get results [%d]", serial->type->description, ret);
40241fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam		goto no_firmware;
40308b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek	} else if (alen != sizeof(*hw_info) + 1) {
404aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek		err("%s: Get results incomplete [%d]", serial->type->description, alen);
405a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto no_firmware;
406a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	} else if (result[0] != command[0]) {
407ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		err("%s: Command failed [%d]", serial->type->description, result[0]);
408ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		goto no_firmware;
409ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek	}
410ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek
411ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek	hw_info = (struct whiteheat_hw_info *)&result[1];
412a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
413ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek	info("%s: Driver %s: Firmware v%d.%02d", serial->type->description,
414ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek	     DRIVER_VERSION, hw_info->sw_major_rev, hw_info->sw_minor_rev);
415a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
416ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek	for (i = 0; i < serial->num_ports; i++) {
417a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port = serial->port[i];
418aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek
419a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
4203aefc37ee789188f0d4488cae04ff618f4c4ddf6Nishant Sarmukadam		if (info == NULL) {
421197a4e4e1f7ef11458f09b4dd74397baf6758133Yogesh Ashok Powar			err("%s: Out of memory for port structures\n", serial->type->description);
422aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek			goto no_private;
423aa21d0f69a5ca28d33f584b8952cca154115fd26Lennert Buytenhek		}
424fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam
425ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek		spin_lock_init(&info->lock);
4265faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam		info->flags = 0;
427a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		info->mcr = 0;
428b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		INIT_WORK(&info->rx_work, rx_data_softint);
429a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		info->port = port;
430b603742f49c3ec922522602e18ac22e8f6835132John W. Linville
431b603742f49c3ec922522602e18ac22e8f6835132John W. Linville		INIT_LIST_HEAD(&info->rx_urbs_free);
432a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		INIT_LIST_HEAD(&info->rx_urbs_submitted);
433a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		INIT_LIST_HEAD(&info->rx_urb_q);
434a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		INIT_LIST_HEAD(&info->tx_urbs_free);
435a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		INIT_LIST_HEAD(&info->tx_urbs_submitted);
436b603742f49c3ec922522602e18ac22e8f6835132John W. Linville
437a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		for (j = 0; j < urb_pool_size; j++) {
438a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			urb = usb_alloc_urb(0, GFP_KERNEL);
43942fba21d56df644887488a29b158cc8916b9ba99Lennert Buytenhek			if (!urb) {
440a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				err("No free urbs available");
441a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				goto no_rx_urb;
442a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			}
443a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			buf_size = port->read_urb->transfer_buffer_length;
44441fdf0974d9eb81215cb578211a6d8f8a022a9ebNishant Sarmukadam			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
44508b063477e45cb366df7204cbcdc79ff86513ef9Lennert Buytenhek			if (!urb->transfer_buffer) {
446b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek				err("Couldn't allocate urb buffer");
447a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				goto no_rx_buf;
448a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			}
449a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
450ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek			if (!wrap) {
451ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek				err("Couldn't allocate urb wrapper");
452ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek				goto no_rx_wrap;
453ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek			}
454a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			usb_fill_bulk_urb(urb, serial->dev,
455ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek					usb_rcvbulkpipe(serial->dev,
456ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek						port->bulk_in_endpointAddress),
457a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek					urb->transfer_buffer, buf_size,
458ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek					whiteheat_read_callback, port);
459a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			wrap->urb = urb;
46032060e1b64f23fe315a35d2df8c2c7ad010df73eLennert Buytenhek			list_add(&wrap->list, &info->rx_urbs_free);
461a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
462b64fe619e371fc17d8d686d6d44aef1b41317880Lennert Buytenhek			urb = usb_alloc_urb(0, GFP_KERNEL);
4633f5610ff560aeaccf051a6f93f25535c219599a0Lennert Buytenhek			if (!urb) {
464fcdc403c31ed5bb5f3baf42f4e2b5e7198fef0c0Nishant Sarmukadam				err("No free urbs available");
465ff45fc60ad583f45ecf10a41f7dbecf78519bcc1Lennert Buytenhek				goto no_tx_urb;
4665faa1aff08ef8d82b98ac2dfd7beb62ae6eda5e5Nishant Sarmukadam			}
4673aefc37ee789188f0d4488cae04ff618f4c4ddf6Nishant Sarmukadam			buf_size = port->write_urb->transfer_buffer_length;
468a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
469a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			if (!urb->transfer_buffer) {
470a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				err("Couldn't allocate urb buffer");
471a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				goto no_tx_buf;
472a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			}
473a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
474a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			if (!wrap) {
475a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				err("Couldn't allocate urb wrapper");
476a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				goto no_tx_wrap;
477a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			}
478a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			usb_fill_bulk_urb(urb, serial->dev,
479a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek					usb_sndbulkpipe(serial->dev,
480a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek						port->bulk_out_endpointAddress),
481a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek					urb->transfer_buffer, buf_size,
482a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek					whiteheat_write_callback, port);
483a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			wrap->urb = urb;
484a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			list_add(&wrap->list, &info->tx_urbs_free);
485a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
486a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
487d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo		usb_set_serial_port_data(port, info);
488a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
489a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
490a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	command_info = kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL);
491a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (command_info == NULL) {
492a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		err("%s: Out of memory for port structures\n", serial->type->description);
493a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto no_command_private;
494a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
495a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
496a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_lock_init(&command_info->lock);
49722be40d9c53faa10d03a679160e0854ad115b610Lennert Buytenhek	command_info->port_running = 0;
49822be40d9c53faa10d03a679160e0854ad115b610Lennert Buytenhek	init_waitqueue_head(&command_info->wait_command);
499a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	usb_set_serial_port_data(command_port, command_info);
500a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	command_port->write_urb->complete = command_port_write_callback;
50199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	command_port->read_urb->complete = command_port_read_callback;
50299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	kfree(result);
50399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	kfree(command);
50499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
50599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	return 0;
50699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
50799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolono_firmware:
50899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	/* Firmware likely not running */
50999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	err("%s: Unable to retrieve firmware version, try replugging\n", serial->type->description);
510a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	err("%s: If the firmware is not running (status led not blinking)\n", serial->type->description);
511a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	err("%s: please contact support@connecttech.com\n", serial->type->description);
512d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	kfree(result);
51399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	return -ENODEV;
514a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
515a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekno_command_private:
516a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = serial->num_ports - 1; i >= 0; i--) {
517a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port = serial->port[i];
518a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		info = usb_get_serial_port_data(port);
51999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		for (j = urb_pool_size - 1; j >= 0; j--) {
52099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			tmp = list_first(&info->tx_urbs_free);
52199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			list_del(tmp);
52299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
52399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			urb = wrap->urb;
524d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo			kfree(wrap);
525a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekno_tx_wrap:
526a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			kfree(urb->transfer_buffer);
52799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolono_tx_buf:
52899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			usb_free_urb(urb);
529a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekno_tx_urb:
530a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek			tmp = list_first(&info->rx_urbs_free);
531a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			list_del(tmp);
532a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
533a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek			urb = wrap->urb;
53499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			kfree(wrap);
53599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolono_rx_wrap:
53699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			kfree(urb->transfer_buffer);
53799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolono_rx_buf:
53899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			usb_free_urb(urb);
53999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolono_rx_urb:
54099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo			;
54199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		}
54299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		kfree(info);
54399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolono_private:
54499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo		;
545a74b295edb3e2b39d6c03255b24dca862a843c59Lennert Buytenhek	}
546a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree(result);
547a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekno_result_buffer:
54899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	kfree(command);
54999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolono_command_buffer:
55099020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	return -ENOMEM;
55199020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo}
55299020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
55399020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo
55499020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolostatic void whiteheat_shutdown (struct usb_serial *serial)
55599020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo{
55699020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	struct usb_serial_port *command_port;
55799020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	struct usb_serial_port *port;
55899020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	struct whiteheat_private *info;
55999020471001dbbd6edf61f105368cb6667cc683dBrian Cavagnolo	struct whiteheat_urb_wrap *wrap;
560a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct urb *urb;
561c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	struct list_head *tmp;
5620863ade8d6bde1d151f75720d999ff27f9fe3533Brian Cavagnolo	struct list_head *tmp2;
56322be40d9c53faa10d03a679160e0854ad115b610Lennert Buytenhek	int i;
564a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
565a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s", __FUNCTION__);
566a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
567a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* free up our private data for our command port */
568a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	command_port = serial->port[COMMAND_PORT];
569a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	kfree (usb_get_serial_port_data(command_port));
570a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
571a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	for (i = 0; i < serial->num_ports; i++) {
572a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port = serial->port[i];
573f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek		info = usb_get_serial_port_data(port);
574f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek		list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
575a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			list_del(tmp);
576a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
577ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet			urb = wrap->urb;
578a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			kfree(wrap);
579a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			kfree(urb->transfer_buffer);
580a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			usb_free_urb(urb);
581a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
582a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		list_for_each_safe(tmp, tmp2, &info->tx_urbs_free) {
583a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			list_del(tmp);
584a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
585a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			urb = wrap->urb;
586a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			kfree(wrap);
587a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			kfree(urb->transfer_buffer);
588a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			usb_free_urb(urb);
589a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
590a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		kfree(info);
591a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
592a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
593a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return;
594a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
595a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
596a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
597a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int whiteheat_open (struct usb_serial_port *port, struct file *filp)
598a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
599a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int		retval = 0;
600a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct ktermios	old_term;
601a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
602a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
603a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
604a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	retval = start_command_port(port->serial);
605a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (retval)
606a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto exit;
607a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
608a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (port->tty)
609a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port->tty->low_latency = 1;
6103d76e82c9538d8104e578ca460d35f214bfddfd3Lennert Buytenhek
611a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* send an open port command */
612a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	retval = firm_open(port);
613a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (retval) {
614a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		stop_command_port(port->serial);
615a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto exit;
616d4b7057052236e81ab0788cc8df306dc02b0e7beLennert Buytenhek	}
617a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
618a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX);
619a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (retval) {
620a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		firm_close(port);
621a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		stop_command_port(port->serial);
622a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto exit;
623a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
624a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
625a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	old_term.c_cflag = ~port->tty->termios->c_cflag;
626a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	old_term.c_iflag = ~port->tty->termios->c_iflag;
627a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	whiteheat_set_termios(port, &old_term);
628a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
629a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Work around HCD bugs */
630a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	usb_clear_halt(port->serial->dev, port->read_urb->pipe);
631a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	usb_clear_halt(port->serial->dev, port->write_urb->pipe);
632f57ca9c1af3c1e30a40ad99d75940176d8c3ff3aLennert Buytenhek
633a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* Start reading from the device */
634a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	retval = start_port_read(port);
635a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (retval) {
636a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		err("%s - failed submitting read urb, error %d", __FUNCTION__, retval);
637a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		firm_close(port);
638a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		stop_command_port(port->serial);
639a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto exit;
640a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
641a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
642a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekexit:
643a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - exit, retval = %d", __FUNCTION__, retval);
644a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return retval;
645a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
646a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
647a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
648a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void whiteheat_close(struct usb_serial_port *port, struct file * filp)
649a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
650a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_private *info = usb_get_serial_port_data(port);
651a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_urb_wrap *wrap;
652a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct urb *urb;
653a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head *tmp;
654a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head *tmp2;
655a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned long flags;
656a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
657a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
658a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
659a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* filp is NULL when called from usb_serial_disconnect */
660a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (filp && (tty_hung_up_p(filp))) {
661a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return;
662a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
663a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
664a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	port->tty->closing = 1;
665a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
666a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*
667a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Not currently in use; tty_wait_until_sent() calls
668a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * serial_chars_in_buffer() which deadlocks on the second semaphore
669a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * acquisition. This should be fixed at some point. Greg's been
670a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * notified.
671a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) {
672a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		tty_wait_until_sent(port->tty, CLOSING_DELAY);
673a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
674a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek*/
675a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
676a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (port->tty->driver->flush_buffer)
677a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port->tty->driver->flush_buffer(port->tty);
678a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	tty_ldisc_flush(port->tty);
679a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
680a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	firm_report_tx_done(port);
681a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
682a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	firm_close(port);
683a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
684a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* shutdown our bulk reads and writes */
685a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_lock_irqsave(&info->lock, flags);
686a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
687a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
688a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		urb = wrap->urb;
689a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		usb_kill_urb(urb);
690a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		list_move(tmp, &info->rx_urbs_free);
691a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
692a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
693a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		list_move(tmp, &info->rx_urbs_free);
694a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
695a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
696a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
697a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		urb = wrap->urb;
698a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		usb_kill_urb(urb);
699a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		list_move(tmp, &info->tx_urbs_free);
700a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
701a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_unlock_irqrestore(&info->lock, flags);
702a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
703a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	stop_command_port(port->serial);
704a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
705a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	port->tty->closing = 0;
706a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
707a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
708a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
709a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int whiteheat_write(struct usb_serial_port *port, const unsigned char *buf, int count)
710a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
711a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct usb_serial *serial = port->serial;
712a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_private *info = usb_get_serial_port_data(port);
713a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_urb_wrap *wrap;
714a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct urb *urb;
715a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int result;
716a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int bytes;
717a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int sent = 0;
718a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned long flags;
719a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head *tmp;
720c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek
721a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
722c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek
723d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	if (count == 0) {
724c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		dbg("%s - write request of 0 bytes", __FUNCTION__);
725c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		return (0);
726c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	}
727c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek
728d1f9e41d1d739cd4393840d35e7554f4a439a4f1Brian Cavagnolo	while (count) {
729a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		spin_lock_irqsave(&info->lock, flags);
730c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		if (list_empty(&info->tx_urbs_free)) {
731c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			spin_unlock_irqrestore(&info->lock, flags);
732c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			break;
733c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		}
734c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		tmp = list_first(&info->tx_urbs_free);
735a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		list_del(tmp);
736c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		spin_unlock_irqrestore(&info->lock, flags);
737a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
738a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
739c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		urb = wrap->urb;
740a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		bytes = (count > port->bulk_out_size) ? port->bulk_out_size : count;
741a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		memcpy (urb->transfer_buffer, buf + sent, bytes);
742ba30c4a58ceb10e81dbf6bd80aeb6a4db42db8feYogesh Ashok Powar
743a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		usb_serial_debug_data(debug, &port->dev, __FUNCTION__, bytes, urb->transfer_buffer);
744c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek
745a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		urb->dev = serial->dev;
746c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek		urb->transfer_buffer_length = bytes;
747a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		result = usb_submit_urb(urb, GFP_ATOMIC);
748a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (result) {
749a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
750c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			sent = result;
751c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek			spin_lock_irqsave(&info->lock, flags);
752a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			list_add(tmp, &info->tx_urbs_free);
753a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			spin_unlock_irqrestore(&info->lock, flags);
754a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
75589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		} else {
756a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			sent += bytes;
75789b872e2e476833cde8aaac658c75817f67e8f81Lennert Buytenhek			count -= bytes;
758a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			spin_lock_irqsave(&info->lock, flags);
759eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek			list_add(tmp, &info->tx_urbs_submitted);
760eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek			spin_unlock_irqrestore(&info->lock, flags);
761eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek		}
762eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek	}
7633db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell
764eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek	return sent;
765eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek}
7663db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell
767a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
768eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhekstatic int whiteheat_write_room(struct usb_serial_port *port)
769eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek{
770eae74e6545d995ab0baa8fb07309f714d9616293Lennert Buytenhek	struct whiteheat_private *info = usb_get_serial_port_data(port);
771a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head *tmp;
772a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int room = 0;
773a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned long flags;
774a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
775a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
776a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
777a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_lock_irqsave(&info->lock, flags);
778a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	list_for_each(tmp, &info->tx_urbs_free)
779a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		room++;
780a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_unlock_irqrestore(&info->lock, flags);
781a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	room *= port->bulk_out_size;
78220f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
783ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet	dbg("%s - returns %d", __FUNCTION__, room);
784a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return (room);
785a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
78620f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
787a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
78820f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhekstatic int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file)
78920f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek{
79020f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	struct whiteheat_private *info = usb_get_serial_port_data(port);
79120f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	unsigned int modem_signals = 0;
79220f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
79320f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
79420f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
79520f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	firm_get_dtr_rts(port);
79620f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	if (info->mcr & UART_MCR_DTR)
79720f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		modem_signals |= TIOCM_DTR;
79820f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	if (info->mcr & UART_MCR_RTS)
79920f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		modem_signals |= TIOCM_RTS;
80020f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
801a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return modem_signals;
80220f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek}
80320f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
80420f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
805a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file,
806a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			       unsigned int set, unsigned int clear)
807ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar{
808ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar	struct whiteheat_private *info = usb_get_serial_port_data(port);
809252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam
810e4eefec73ea0a740bfe8736e3ac30dfe92fe392bYogesh Ashok Powar	dbg("%s - port %d", __FUNCTION__, port->number);
811e4eefec73ea0a740bfe8736e3ac30dfe92fe392bYogesh Ashok Powar
812a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (set & TIOCM_RTS)
813a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		info->mcr |= UART_MCR_RTS;
814ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	if (set & TIOCM_DTR)
815252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam		info->mcr |= UART_MCR_DTR;
816a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
817a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (clear & TIOCM_RTS)
818ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek		info->mcr &= ~UART_MCR_RTS;
819ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	if (clear & TIOCM_DTR)
820ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek		info->mcr &= ~UART_MCR_DTR;
821ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek
822ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	firm_set_dtr(port, info->mcr & UART_MCR_DTR);
823ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek	firm_set_rts(port, info->mcr & UART_MCR_RTS);
824a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return 0;
825ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek}
826a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
827ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar
828ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powarstatic int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
829ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar{
830ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar	struct serial_struct serstruct;
831ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar	void __user *user_arg = (void __user *)arg;
832ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar
833ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar	dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
834ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar
835ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar	switch (cmd) {
836ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar		case TIOCGSERIAL:
837ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar			memset(&serstruct, 0, sizeof(serstruct));
838ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar			serstruct.type = PORT_16654;
839ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar			serstruct.line = port->serial->minor;
840ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar			serstruct.port = port->number;
841ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar			serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
842ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar			serstruct.xmit_fifo_size = port->bulk_out_size;
843e4eefec73ea0a740bfe8736e3ac30dfe92fe392bYogesh Ashok Powar			serstruct.custom_divisor = 0;
844252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam			serstruct.baud_base = 460800;
845252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam			serstruct.close_delay = CLOSING_DELAY;
846252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam			serstruct.closing_wait = CLOSING_DELAY;
847a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
848ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek			if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
849252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam				return -EFAULT;
850a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
851a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
852a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
853a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		case TIOCSSERIAL:
854ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek			if (copy_from_user(&serstruct, user_arg, sizeof(serstruct)))
855ca00930153c14b323c31b97623ac5c4f7855ed6aLennert Buytenhek				return -EFAULT;
856a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
857a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			/*
858a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			 * For now this is ignored. dip sets the ASYNC_[V]HI flags
859a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			 * but this isn't used by us at all. Maybe someone somewhere
860a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			 * will need the custom_divisor setting.
861a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			 */
862252486a13a36e2846ff046c18aae67658692ecedNishant Sarmukadam
863a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			break;
864a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
865ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar		default:
866ff776cecec92fe7cac4a9ce1919576ad6e737e08Yogesh Ashok Powar			break;
867e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	}
868e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
869e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	return -ENOIOCTLCMD;
870e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam}
871e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
872e4eefec73ea0a740bfe8736e3ac30dfe92fe392bYogesh Ashok Powar
873e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadamstatic void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios *old_termios)
874e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam{
875e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	dbg("%s -port %d", __FUNCTION__, port->number);
876e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
877e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	if ((!port->tty) || (!port->tty->termios)) {
878e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		dbg("%s - no tty structures", __FUNCTION__);
879e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		goto exit;
880e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	}
881e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
882e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	/* check that they really want us to change something */
883e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	if (old_termios) {
884e4eefec73ea0a740bfe8736e3ac30dfe92fe392bYogesh Ashok Powar		if ((port->tty->termios->c_cflag == old_termios->c_cflag) &&
885e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		    (port->tty->termios->c_iflag == old_termios->c_iflag)) {
886e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			dbg("%s - nothing to change...", __FUNCTION__);
887e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam			goto exit;
888e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam		}
889e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	}
890e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
891e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	firm_setup_port(port);
892e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
893e4eefec73ea0a740bfe8736e3ac30dfe92fe392bYogesh Ashok Powarexit:
894e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	return;
895e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam}
896e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
897e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
898e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadamstatic void whiteheat_break_ctl(struct usb_serial_port *port, int break_state) {
899e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	firm_set_break(port, break_state);
900e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam}
901e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
902e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam
903e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadamstatic int whiteheat_chars_in_buffer(struct usb_serial_port *port)
904e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam{
905e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	struct whiteheat_private *info = usb_get_serial_port_data(port);
906e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	struct list_head *tmp;
907e4eefec73ea0a740bfe8736e3ac30dfe92fe392bYogesh Ashok Powar	struct whiteheat_urb_wrap *wrap;
908e53d9b964e2568172149d41b3157af9cde9accafNishant Sarmukadam	int chars = 0;
909a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned long flags;
910a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
91189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
9126f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
91389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	spin_lock_irqsave(&info->lock, flags);
9146f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	list_for_each(tmp, &info->tx_urbs_submitted) {
9156f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
9166f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		chars += wrap->urb->transfer_buffer_length;
9176f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	}
9186f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	spin_unlock_irqrestore(&info->lock, flags);
9196f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9206f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	dbg ("%s - returns %d", __FUNCTION__, chars);
9216f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	return (chars);
9226f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek}
9236f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9246f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9256f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhekstatic void whiteheat_throttle (struct usb_serial_port *port)
9266f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek{
9276f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	struct whiteheat_private *info = usb_get_serial_port_data(port);
9286f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	unsigned long flags;
929ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet
9306f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
93189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
93289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	spin_lock_irqsave(&info->lock, flags);
93389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	info->flags |= THROTTLED;
9348e9f33f0ced82a797d285b233e1c956cbd5c7de3Lennert Buytenhek	spin_unlock_irqrestore(&info->lock, flags);
93589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
9366f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	return;
937d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam}
938d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
939d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
940d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadamstatic void whiteheat_unthrottle (struct usb_serial_port *port)
941d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam{
942d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct whiteheat_private *info = usb_get_serial_port_data(port);
943d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	int actually_throttled;
94489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	unsigned long flags;
9456f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
94689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
9476f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9486f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	spin_lock_irqsave(&info->lock, flags);
94989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	actually_throttled = info->flags & ACTUALLY_THROTTLED;
9506f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
9516f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	spin_unlock_irqrestore(&info->lock, flags);
95289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
9536f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	if (actually_throttled)
95489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		rx_data_softint(&info->rx_work);
9556f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9566f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	return;
9576f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek}
9586f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9596f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9606f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek/*****************************************************************************
9616f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek * Connect Tech's White Heat callback routines
9626f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek *****************************************************************************/
96389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic void command_port_write_callback (struct urb *urb)
9640d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville{
9656f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	dbg("%s", __FUNCTION__);
96689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
9676f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	if (urb->status) {
96889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		dbg ("nonzero urb status: %d", urb->status);
9696f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		return;
9706f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	}
9716f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek}
9726f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9736f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9746f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhekstatic void command_port_read_callback (struct urb *urb)
9750d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville{
9766f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	struct usb_serial_port *command_port = (struct usb_serial_port *)urb->context;
97789a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct whiteheat_command_private *command_info;
9786f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	unsigned char *data = urb->transfer_buffer;
97989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	int result;
9808e9f33f0ced82a797d285b233e1c956cbd5c7de3Lennert Buytenhek	unsigned long flags;
98189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
9826f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	dbg("%s", __FUNCTION__);
9836f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9846f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	if (urb->status) {
985777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status);
986777ad375d5960e0d2a945a34032b182eb2952d45Lennert Buytenhek		return;
9876f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	}
9886f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9896f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data);
9906f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek
9916f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	command_info = usb_get_serial_port_data(command_port);
9926f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	if (!command_info) {
993854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		dbg ("%s - command_info is NULL, exiting.", __FUNCTION__);
994854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		return;
995854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	}
996854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	spin_lock_irqsave(&command_info->lock, flags);
997854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek
998854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	if (data[0] == WHITEHEAT_CMD_COMPLETE) {
999854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
100059eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf		wake_up_interruptible(&command_info->wait_command);
100159eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf	} else if (data[0] == WHITEHEAT_CMD_FAILURE) {
10026f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		command_info->command_finished = WHITEHEAT_CMD_FAILURE;
100320f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek		wake_up_interruptible(&command_info->wait_command);
100420f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	} else if (data[0] == WHITEHEAT_EVENT) {
1005d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		/* These are unsolicited reports from the firmware, hence no waiting command to wakeup */
1006d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		dbg("%s - event received", __FUNCTION__);
1007d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	} else if (data[0] == WHITEHEAT_GET_DTR_RTS) {
1008d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1);
1009d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
10106f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		wake_up_interruptible(&command_info->wait_command);
10116f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	} else {
10126f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		dbg("%s - bad reply from firmware", __FUNCTION__);
101389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	}
101489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
101589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	/* Continue trying to always read */
101689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	command_port->read_urb->dev = command_port->serial->dev;
101789a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
10186f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	spin_unlock_irqrestore(&command_info->lock, flags);
10196f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek	if (result)
10206f6d1e9a8a7fea5e4400cad64bed717e322208e1Lennert Buytenhek		dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
102189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek}
1022a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
102389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
1024a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void whiteheat_read_callback(struct urb *urb)
1025a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1026a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
1027a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_urb_wrap *wrap;
102845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	unsigned char *data = urb->transfer_buffer;
1029a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_private *info = usb_get_serial_port_data(port);
1030a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1031a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
1032a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1033a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_lock(&info->lock);
1034a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	wrap = urb_to_wrap(urb, &info->rx_urbs_submitted);
1035a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (!wrap) {
1036a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		spin_unlock(&info->lock);
1037a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		err("%s - Not my urb!", __FUNCTION__);
1038ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet		return;
1039a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
104089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	list_del(&wrap->list);
104189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	spin_unlock(&info->lock);
104289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
104389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (urb->status) {
104489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
104589a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		spin_lock(&info->lock);
104654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		list_add(&wrap->list, &info->rx_urbs_free);
104789a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		spin_unlock(&info->lock);
1048d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		return;
1049d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	}
1050d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1051d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
1052d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
105354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	spin_lock(&info->lock);
105489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	list_add_tail(&wrap->list, &info->rx_urb_q);
105554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	if (info->flags & THROTTLED) {
105689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		info->flags |= ACTUALLY_THROTTLED;
105754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		spin_unlock(&info->lock);
105854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		return;
105989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	}
106054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	spin_unlock(&info->lock);
106154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
106289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	schedule_work(&info->rx_work);
106354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek}
106489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
106554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
106654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhekstatic void whiteheat_write_callback(struct urb *urb)
106754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek{
106854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
106954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	struct whiteheat_private *info = usb_get_serial_port_data(port);
107054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	struct whiteheat_urb_wrap *wrap;
107154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
107254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	dbg("%s - port %d", __FUNCTION__, port->number);
107389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
10740d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	spin_lock(&info->lock);
107554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);
107689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	if (!wrap) {
107754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		spin_unlock(&info->lock);
107854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		err("%s - Not my urb!", __FUNCTION__);
107989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		return;
108054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	}
108154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	list_move(&wrap->list, &info->tx_urbs_free);
108254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	spin_unlock(&info->lock);
108354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
108454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	if (urb->status) {
108554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
108654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		return;
108754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	}
10880d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville
108989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	usb_serial_port_softint(port);
109089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek}
109154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
109289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
109354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek/*****************************************************************************
109489a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek * Connect Tech's White Heat firmware interface
109554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek *****************************************************************************/
109689a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhekstatic int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize)
109754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek{
109889a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	struct usb_serial_port *command_port;
109954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	struct whiteheat_command_private *command_info;
110054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	struct whiteheat_private *info;
1101854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	__u8 *transfer_buffer;
1102854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	int retval = 0;
1103854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	unsigned long flags;
1104854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek
1105854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	dbg("%s - command %d", __FUNCTION__, command);
1106854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek
1107854783444bab0024556c0aefdb0a860f2f1da286Lennert Buytenhek	command_port = port->serial->port[COMMAND_PORT];
110859eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf	command_info = usb_get_serial_port_data(command_port);
110959eb21a6504731fc16db4cf9463065dd61093e08Bruno Randolf	spin_lock_irqsave(&command_info->lock, flags);
111054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	command_info->command_finished = FALSE;
111120f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek
1112d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
1113d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	transfer_buffer[0] = command;
1114d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	memcpy (&transfer_buffer[1], data, datasize);
111520f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	command_port->write_urb->transfer_buffer_length = datasize + 1;
111654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	command_port->write_urb->dev = port->serial->dev;
111754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL);
111854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	if (retval) {
111989a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		dbg("%s - submit urb failed", __FUNCTION__);
112089a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek		goto exit;
112189a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	}
112289a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek	spin_unlock_irqrestore(&command_info->lock, flags);
112389a91f4f4cdae92daf3693d4852ae4958abb6c58Lennert Buytenhek
112454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	/* wait for the command to complete */
112554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	wait_event_interruptible_timeout(command_info->wait_command,
112654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		(command_info->command_finished != FALSE), COMMAND_TIMEOUT);
1127a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1128a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_lock_irqsave(&command_info->lock, flags);
1129a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1130a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (command_info->command_finished == FALSE) {
1131a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		dbg("%s - command timed out.", __FUNCTION__);
1132a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		retval = -ETIMEDOUT;
1133a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		goto exit;
1134a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1135a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1136a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
113745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		dbg("%s - command failed.", __FUNCTION__);
113845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		retval = -EIO;
113945eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		goto exit;
1140a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
114154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
1142a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
114345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		dbg("%s - command completed.", __FUNCTION__);
114445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		switch (command) {
11455db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches			case WHITEHEAT_GET_DTR_RTS:
1146a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				info = usb_get_serial_port_data(port);
1147a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				memcpy(&info->mcr, command_info->result_buffer, sizeof(struct whiteheat_dr_info));
114845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek				break;
1149a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		}
1150b9ede5f1dc03f96949dcaa8f8b3483766c047260Shan Wei	}
1151788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek
11525db5584441c2dceb75696fb31a44ac7b9b925359Joe Perchesexit:
115345eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	spin_unlock_irqrestore(&command_info->lock, flags);
1154a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return retval;
1155a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1156a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1157a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
115854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhekstatic int firm_open(struct usb_serial_port *port) {
115954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	struct whiteheat_simple open_command;
1160a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
116154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	open_command.port = port->number - port->serial->minor + 1;
116254bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	return firm_send_command(port, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command));
116354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek}
116454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
1165a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
116654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhekstatic int firm_close(struct usb_serial_port *port) {
116754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	struct whiteheat_simple close_command;
116854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
116954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	close_command.port = port->number - port->serial->minor + 1;
1170a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return firm_send_command(port, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command));
117154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek}
1172a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1173a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1174a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_setup_port(struct usb_serial_port *port) {
1175a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_port_settings port_settings;
1176a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	unsigned int cflag = port->tty->termios->c_cflag;
1177a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1178a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	port_settings.port = port->number + 1;
1179a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1180a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* get the byte size */
1181a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	switch (cflag & CSIZE) {
1182a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		case CS5:	port_settings.bits = 5;   break;
1183a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		case CS6:	port_settings.bits = 6;   break;
118445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		case CS7:	port_settings.bits = 7;   break;
1185a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		default:
1186788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		case CS8:	port_settings.bits = 8;   break;
1187a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
118854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	dbg("%s - data bits = %d", __FUNCTION__, port_settings.bits);
1189a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1190a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* determine the parity */
1191a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cflag & PARENB)
1192a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (cflag & CMSPAR)
1193a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			if (cflag & PARODD)
1194788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek				port_settings.parity = WHITEHEAT_PAR_MARK;
1195788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek			else
1196a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				port_settings.parity = WHITEHEAT_PAR_SPACE;
119754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		else
119854bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek			if (cflag & PARODD)
119954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek				port_settings.parity = WHITEHEAT_PAR_ODD;
120054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek			else
1201788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek				port_settings.parity = WHITEHEAT_PAR_EVEN;
120253b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori	else
120354bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		port_settings.parity = WHITEHEAT_PAR_NONE;
120454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	dbg("%s - parity = %c", __FUNCTION__, port_settings.parity);
120554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
1206a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* figure out the stop bits requested */
1207a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cflag & CSTOPB)
1208a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port_settings.stop = 2;
1209a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	else
1210a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port_settings.stop = 1;
1211a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - stop bits = %d", __FUNCTION__, port_settings.stop);
1212a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1213a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* figure out the flow control settings */
1214a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (cflag & CRTSCTS)
1215a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port_settings.hflow = (WHITEHEAT_HFLOW_CTS | WHITEHEAT_HFLOW_RTS);
1216a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	else
1217a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		port_settings.hflow = WHITEHEAT_HFLOW_NONE;
1218a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - hardware flow control = %s %s %s %s", __FUNCTION__,
1219a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	    (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
122073b46320209e9fe0d65aba1b8c21489278047574Brian Cavagnolo	    (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
122173b46320209e9fe0d65aba1b8c21489278047574Brian Cavagnolo	    (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
122273b46320209e9fe0d65aba1b8c21489278047574Brian Cavagnolo	    (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : "");
1223a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1224788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	/* determine software flow control */
1225788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	if (I_IXOFF(port->tty))
122653b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori		port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
1227788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	else
122853b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori		port_settings.sflow = WHITEHEAT_SFLOW_NONE;
1229788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	dbg("%s - software flow control = %c", __FUNCTION__, port_settings.sflow);
1230788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek
1231788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	port_settings.xon = START_CHAR(port->tty);
1232a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	port_settings.xoff = STOP_CHAR(port->tty);
1233a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - XON = %2x, XOFF = %2x", __FUNCTION__, port_settings.xon, port_settings.xoff);
1234a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1235788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	/* get the baud rate wanted */
1236788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	port_settings.baud = tty_get_baud_rate(port->tty);
1237a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dbg("%s - baud rate = %d", __FUNCTION__, port_settings.baud);
1238a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
123954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	/* handle any settings that aren't specified in the tty structure */
124045eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	port_settings.lloop = 0;
124145eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek
1242a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	/* now send the message to the device */
1243a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return firm_send_command(port, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings));
1244a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1245a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1246a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1247a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_set_rts(struct usb_serial_port *port, __u8 onoff) {
1248a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_set_rdb rts_command;
1249a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1250a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rts_command.port = port->number - port->serial->minor + 1;
1251a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	rts_command.state = onoff;
1252a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&rts_command, sizeof(rts_command));
1253a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
12542e42e4747ea72943c21551d8a206b51a9893b1e0Joe Perches
1255a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1256a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_set_dtr(struct usb_serial_port *port, __u8 onoff) {
12573779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek	struct whiteheat_set_rdb dtr_command;
12583779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek
1259a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	dtr_command.port = port->number - port->serial->minor + 1;
12603779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek	dtr_command.state = onoff;
12613779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek	return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&dtr_command, sizeof(dtr_command));
1262a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1263d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek
1264a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1265a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_set_break(struct usb_serial_port *port, __u8 onoff) {
1266a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_set_rdb break_command;
1267a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1268a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	break_command.port = port->number - port->serial->minor + 1;
1269a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	break_command.state = onoff;
1270a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&break_command, sizeof(break_command));
1271a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
12723779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek
1273a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1274a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_purge(struct usb_serial_port *port, __u8 rxtx) {
1275d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct whiteheat_purge purge_command;
1276d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1277d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	purge_command.port = port->number - port->serial->minor + 1;
1278d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	purge_command.what = rxtx;
1279d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	return firm_send_command(port, WHITEHEAT_PURGE, (__u8 *)&purge_command, sizeof(purge_command));
1280d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam}
1281d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1282d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1283d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadamstatic int firm_get_dtr_rts(struct usb_serial_port *port) {
1284d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct whiteheat_simple get_dr_command;
1285d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1286d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	get_dr_command.port = port->number - port->serial->minor + 1;
1287d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	return firm_send_command(port, WHITEHEAT_GET_DTR_RTS, (__u8 *)&get_dr_command, sizeof(get_dr_command));
1288d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam}
1289d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1290a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1291a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int firm_report_tx_done(struct usb_serial_port *port) {
1292a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_simple close_command;
1293d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1294a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	close_command.port = port->number - port->serial->minor + 1;
1295a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE, (__u8 *)&close_command, sizeof(close_command));
1296a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1297a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
129845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek
1299a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*****************************************************************************
130054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek * Connect Tech's White Heat utility functions
130154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek *****************************************************************************/
1302a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int start_command_port(struct usb_serial *serial)
1303d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam{
130420f09c3df7a8a623c290f62596c1a6b0da088030Lennert Buytenhek	struct usb_serial_port *command_port;
1305a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_command_private *command_info;
1306788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek	unsigned long flags;
1307d25f9f1357139bbdc79bc960ea84909a7c22ec2bLennert Buytenhek	int retval = 0;
1308d25f9f1357139bbdc79bc960ea84909a7c22ec2bLennert Buytenhek
130954bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	command_port = serial->port[COMMAND_PORT];
131054bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	command_info = usb_get_serial_port_data(command_port);
131154bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	spin_lock_irqsave(&command_info->lock, flags);
13120d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville	if (!command_info->port_running) {
13130d462bbb0e20863b6c796abd779bfdb534d60278John W. Linville		/* Work around HCD bugs */
131454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		usb_clear_halt(serial->dev, command_port->read_urb->pipe);
131554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
131654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek		command_port->read_urb->dev = serial->dev;
1317788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);
1318788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		if (retval) {
1319788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek			err("%s - failed submitting read urb, error %d", __FUNCTION__, retval);
132053b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori			goto exit;
1321788838ebe8a4caca93a91982c7bbf393edf24c08Lennert Buytenhek		}
132253b1b3e1f0feaa57b82d3dc344d887fe3eecc90bFUJITA Tomonori	}
1323a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	command_info->port_running++;
132454bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek
132554bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhekexit:
132654bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	spin_unlock_irqrestore(&command_info->lock, flags);
132754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	return retval;
132845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek}
1329a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1330d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1331a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic void stop_command_port(struct usb_serial *serial)
1332a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1333c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	struct usb_serial_port *command_port;
1334c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	struct whiteheat_command_private *command_info;
1335c2c357ce309221b85fd36e50aade66d01a556cdeLennert Buytenhek	unsigned long flags;
1336a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
133754bc3a0d7a0c9a13da31183609c42cf7786138e1Lennert Buytenhek	command_port = serial->port[COMMAND_PORT];
13383779752d764b86077375510b1fd13d8fb2c7c3a5Lennert Buytenhek	command_info = usb_get_serial_port_data(command_port);
1339a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	spin_lock_irqsave(&command_info->lock, flags);
1340d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	command_info->port_running--;
1341d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	if (!command_info->port_running)
1342d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		usb_kill_urb(command_port->read_urb);
1343d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	spin_unlock_irqrestore(&command_info->lock, flags);
1344d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam}
1345d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1346d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1347d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadamstatic int start_port_read(struct usb_serial_port *port)
1348d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam{
1349d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct whiteheat_private *info = usb_get_serial_port_data(port);
135023677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches	struct whiteheat_urb_wrap *wrap;
1351d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct urb *urb;
1352d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	int retval = 0;
1353d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	unsigned long flags;
1354d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct list_head *tmp;
1355d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	struct list_head *tmp2;
1356d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1357d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	spin_lock_irqsave(&info->lock, flags);
1358d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1359d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
1360d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		list_del(tmp);
1361d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
1362d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		urb = wrap->urb;
1363d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		urb->dev = port->serial->dev;
1364d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		retval = usb_submit_urb(urb, GFP_KERNEL);
1365d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		if (retval) {
1366d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			list_add(tmp, &info->rx_urbs_free);
1367d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
1368d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
1369d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				urb = wrap->urb;
1370d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				usb_kill_urb(urb);
1371d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam				list_move(tmp, &info->rx_urbs_free);
1372d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			}
1373d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam			break;
1374d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		}
1375d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam		list_add(tmp, &info->rx_urbs_submitted);
1376d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	}
1377d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1378d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam	spin_unlock_irqrestore(&info->lock, flags);
1379d9a07d4980514e701555c4f03b865a54c4c48b4aNishant Sarmukadam
1380f1d58c2521eb160178b2151d6326d8dc5d7c8560Johannes Berg	return retval;
1381f1d58c2521eb160178b2151d6326d8dc5d7c8560Johannes Berg}
1382a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1383a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1384a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic struct whiteheat_urb_wrap *urb_to_wrap(struct urb* urb, struct list_head *head)
1385a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1386a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_urb_wrap *wrap;
1387a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct list_head *tmp;
1388a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1389a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	list_for_each(tmp, head) {
1390a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
1391a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (wrap->urb == urb)
1392a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			return wrap;
1393a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1394a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1395a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	return NULL;
1396a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1397a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1398a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1399a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic struct list_head *list_first(struct list_head *head)
1400e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek{
1401e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek	return head->next;
1402e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek}
1403e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek
1404e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhek
1405e0493a8dd6351a114b53790dda6bd855ae73a85cLennert Buytenhekstatic void rx_data_softint(struct work_struct *work)
1406a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1407a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_private *info =
1408a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		container_of(work, struct whiteheat_private, rx_work);
1409a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct usb_serial_port *port = info->port;
1410a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct tty_struct *tty = port->tty;
1411a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct whiteheat_urb_wrap *wrap;
1412a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	struct urb *urb;
1413d89173f25228b8795af2d4b53e985cc44c729332Lennert Buytenhek	unsigned long flags;
141445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	struct list_head *tmp;
14158a7a578c2e3ac463a17fe30b11ada0509658a952Brian Cavagnolo	struct list_head *tmp2;
1416a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int result;
1417a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int sent = 0;
1418a1fe24b0fd8bf16b4e551ae3fb785bfc574b9ffbBrian Cavagnolo
1419ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet	spin_lock_irqsave(&info->lock, flags);
1420a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (info->flags & THROTTLED) {
1421a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		spin_unlock_irqrestore(&info->lock, flags);
1422a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		return;
1423a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	}
1424a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1425a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
1426a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		list_del(tmp);
1427a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		spin_unlock_irqrestore(&info->lock, flags);
1428a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1429a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
14308ccbc3b8b0c919e8609560ca56cd777ece8d2c41Kalle Valo		urb = wrap->urb;
143145eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek
143245eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		if (tty && urb->actual_length) {
1433a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			int len = tty_buffer_request_room(tty, urb->actual_length);
1434a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			/* This stuff can go away now I suspect */
1435a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			if (unlikely(len < urb->actual_length)) {
143645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek				spin_lock_irqsave(&info->lock, flags);
143745eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek				list_add(tmp, &info->rx_urb_q);
14385db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches				spin_unlock_irqrestore(&info->lock, flags);
1439a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				tty_flip_buffer_push(tty);
1440a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek				schedule_work(&info->rx_work);
144145eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek				return;
1442a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			}
1443b9ede5f1dc03f96949dcaa8f8b3483766c047260Shan Wei			tty_insert_flip_string(tty, urb->transfer_buffer, len);
144445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek			sent += len;
14455db5584441c2dceb75696fb31a44ac7b9b925359Joe Perches		}
144645eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek
1447a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		urb->dev = port->serial->dev;
1448a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		result = usb_submit_urb(urb, GFP_ATOMIC);
1449a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		if (result) {
1450a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
1451a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			spin_lock_irqsave(&info->lock, flags);
1452a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			list_add(tmp, &info->rx_urbs_free);
1453a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek			continue;
145445eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek		}
1455a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1456a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		spin_lock_irqsave(&info->lock, flags);
1457a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		list_add(tmp, &info->rx_urbs_submitted);
145845eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	}
145945eb400d50e1ad84a8e8f9e9a82cd8ae13d7d691Lennert Buytenhek	spin_unlock_irqrestore(&info->lock, flags);
1460a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1461a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (sent)
1462a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek		tty_flip_buffer_push(tty);
1463a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
1464a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1465a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1466a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek/*****************************************************************************
1467a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek * Connect Tech's White Heat module functions
1468a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek *****************************************************************************/
1469a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekstatic int __init whiteheat_init (void)
1470a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
1471a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	int retval;
1472a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	retval = usb_serial_register(&whiteheat_fake_device);
1473a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	if (retval)
14747e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		goto failed_fake_register;
1475a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	retval = usb_serial_register(&whiteheat_device);
14767e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	if (retval)
14777e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		goto failed_device_register;
14787e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	retval = usb_register(&whiteheat_driver);
1479e600707b021efdc109e7becd467798da339ec26dBrian Cavagnolo	if (retval)
14807e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek		goto failed_usb_register;
14817e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	info(DRIVER_DESC " " DRIVER_VERSION);
14827e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	return 0;
14837e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhekfailed_usb_register:
14847e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	usb_serial_deregister(&whiteheat_device);
14857e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhekfailed_device_register:
1486a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	usb_serial_deregister(&whiteheat_fake_device);
14877e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhekfailed_fake_register:
14887e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	return retval;
1489a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek}
14907e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
1491a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
14927e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhekstatic void __exit whiteheat_exit (void)
1493a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek{
14947e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek	usb_deregister (&whiteheat_driver);
1495a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	usb_serial_deregister (&whiteheat_fake_device);
1496a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek	usb_serial_deregister (&whiteheat_device);
14977e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek}
1498a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1499a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhek
1500c96c31e499b70964cfc88744046c998bb710e4b8Joe Perchesmodule_init(whiteheat_init);
1501c96c31e499b70964cfc88744046c998bb710e4b8Joe Perchesmodule_exit(whiteheat_exit);
1502c96c31e499b70964cfc88744046c998bb710e4b8Joe Perches
1503c96c31e499b70964cfc88744046c998bb710e4b8Joe PerchesMODULE_AUTHOR( DRIVER_AUTHOR );
1504c96c31e499b70964cfc88744046c998bb710e4b8Joe PerchesMODULE_DESCRIPTION( DRIVER_DESC );
1505c96c31e499b70964cfc88744046c998bb710e4b8Joe PerchesMODULE_LICENSE("GPL");
15067e1112d34aea10fdd689422e6bdc918309043bf3Lennert Buytenhek
1507a66098daacee2f354dab311b58011e7076aa248cLennert Buytenhekmodule_param(urb_pool_size, int, 0);
1508a66098daacee2f354dab311b58011e7076aa248cLennert BuytenhekMODULE_PARM_DESC(urb_pool_size, "Number of urbs to use for buffering");
1509618952a7b19b796fce98364fb26551cbe3e16a75Lennert Buytenhek
151088de754ad59025eba797e7a8375807755577f450Lennert Buytenhekmodule_param(debug, bool, S_IRUGO | S_IWUSR);
1511618952a7b19b796fce98364fb26551cbe3e16a75Lennert BuytenhekMODULE_PARM_DESC(debug, "Debug enabled or not");
151262abd3cfb2f1a0ab1963ac4c4087c477da6b1f2aLennert Buytenhek