blob: 522bf850a05da8e4d0f4dc15d9ed9ad6e842d25b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * USB ConnectTech WhiteHEAT driver
3 *
4 * Copyright (C) 2002
5 * Connect Tech Inc.
6 *
7 * Copyright (C) 1999 - 2001
8 * Greg Kroah-Hartman (greg@kroah.com)
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
Alan Cox80359a92008-07-22 11:09:16 +010015 * See Documentation/usb/usb-serial.txt for more information on using this
16 * driver
Linus Torvalds1da177e2005-04-16 15:20:36 -070017 */
18
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/kernel.h>
20#include <linux/errno.h>
21#include <linux/init.h>
22#include <linux/slab.h>
23#include <linux/tty.h>
24#include <linux/tty_driver.h>
25#include <linux/tty_flip.h>
26#include <linux/module.h>
27#include <linux/spinlock.h>
Oliver Neukum08a2b3b2007-05-24 13:52:51 +020028#include <linux/mutex.h>
Alan Cox80359a92008-07-22 11:09:16 +010029#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <asm/termbits.h>
31#include <linux/usb.h>
32#include <linux/serial_reg.h>
33#include <linux/serial.h>
Greg Kroah-Hartmana9698882006-07-11 21:22:58 -070034#include <linux/usb/serial.h>
David Woodhouseec6752f2008-05-31 01:35:29 +030035#include <linux/firmware.h>
36#include <linux/ihex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include "whiteheat.h" /* WhiteHEAT specific commands */
38
Rusty Russell90ab5ee2012-01-13 09:32:20 +103039static bool debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41#ifndef CMSPAR
42#define CMSPAR 0
43#endif
44
45/*
46 * Version Information
47 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>"
49#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
50
51#define CONNECT_TECH_VENDOR_ID 0x0710
52#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001
53#define CONNECT_TECH_WHITE_HEAT_ID 0x8001
54
55/*
56 ID tables for whiteheat are unusual, because we want to different
57 things for different versions of the device. Eventually, this
58 will be doable from a single table. But, for now, we define two
59 separate ID tables, and then a third table that combines them
60 just for the purpose of exporting the autoloading information.
61*/
Németh Márton7d40d7e2010-01-10 15:34:24 +010062static const struct usb_device_id id_table_std[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
64 { } /* Terminating entry */
65};
66
Németh Márton7d40d7e2010-01-10 15:34:24 +010067static const struct usb_device_id id_table_prerenumeration[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
69 { } /* Terminating entry */
70};
71
Németh Márton7d40d7e2010-01-10 15:34:24 +010072static const struct usb_device_id id_table_combined[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
74 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
75 { } /* Terminating entry */
76};
77
Alan Cox80359a92008-07-22 11:09:16 +010078MODULE_DEVICE_TABLE(usb, id_table_combined);
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
80static struct usb_driver whiteheat_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 .name = "whiteheat",
82 .probe = usb_serial_probe,
83 .disconnect = usb_serial_disconnect,
84 .id_table = id_table_combined,
85};
86
87/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
Alan Cox80359a92008-07-22 11:09:16 +010088static int whiteheat_firmware_download(struct usb_serial *serial,
89 const struct usb_device_id *id);
90static int whiteheat_firmware_attach(struct usb_serial *serial);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
92/* function prototypes for the Connect Tech WhiteHEAT serial converter */
Alan Cox80359a92008-07-22 11:09:16 +010093static int whiteheat_attach(struct usb_serial *serial);
Alan Sternf9c99bb2009-06-02 11:53:55 -040094static void whiteheat_release(struct usb_serial *serial);
Alan Cox80359a92008-07-22 11:09:16 +010095static int whiteheat_open(struct tty_struct *tty,
Alan Coxa509a7e2009-09-19 13:13:26 -070096 struct usb_serial_port *port);
Alan Cox335f8512009-06-11 12:26:29 +010097static void whiteheat_close(struct usb_serial_port *port);
Alan Cox80359a92008-07-22 11:09:16 +010098static int whiteheat_write(struct tty_struct *tty,
99 struct usb_serial_port *port,
100 const unsigned char *buf, int count);
101static int whiteheat_write_room(struct tty_struct *tty);
Alan Cox00a0d0d2011-02-14 16:27:06 +0000102static int whiteheat_ioctl(struct tty_struct *tty,
Alan Cox80359a92008-07-22 11:09:16 +0100103 unsigned int cmd, unsigned long arg);
104static void whiteheat_set_termios(struct tty_struct *tty,
105 struct usb_serial_port *port, struct ktermios *old);
Alan Cox60b33c12011-02-14 16:26:14 +0000106static int whiteheat_tiocmget(struct tty_struct *tty);
Alan Cox20b9d172011-02-14 16:26:50 +0000107static int whiteheat_tiocmset(struct tty_struct *tty,
Alan Cox80359a92008-07-22 11:09:16 +0100108 unsigned int set, unsigned int clear);
109static void whiteheat_break_ctl(struct tty_struct *tty, int break_state);
110static int whiteheat_chars_in_buffer(struct tty_struct *tty);
111static void whiteheat_throttle(struct tty_struct *tty);
112static void whiteheat_unthrottle(struct tty_struct *tty);
113static void whiteheat_read_callback(struct urb *urb);
114static void whiteheat_write_callback(struct urb *urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -0700116static struct usb_serial_driver whiteheat_fake_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700117 .driver = {
118 .owner = THIS_MODULE,
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700119 .name = "whiteheatnofirm",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700120 },
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700121 .description = "Connect Tech - WhiteHEAT - (prerenumeration)",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 .id_table = id_table_prerenumeration,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 .num_ports = 1,
124 .probe = whiteheat_firmware_download,
125 .attach = whiteheat_firmware_attach,
126};
127
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -0700128static struct usb_serial_driver whiteheat_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700129 .driver = {
130 .owner = THIS_MODULE,
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700131 .name = "whiteheat",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700132 },
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700133 .description = "Connect Tech - WhiteHEAT",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 .id_table = id_table_std,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 .num_ports = 4,
136 .attach = whiteheat_attach,
Alan Sternf9c99bb2009-06-02 11:53:55 -0400137 .release = whiteheat_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 .open = whiteheat_open,
139 .close = whiteheat_close,
140 .write = whiteheat_write,
141 .write_room = whiteheat_write_room,
142 .ioctl = whiteheat_ioctl,
143 .set_termios = whiteheat_set_termios,
144 .break_ctl = whiteheat_break_ctl,
145 .tiocmget = whiteheat_tiocmget,
146 .tiocmset = whiteheat_tiocmset,
147 .chars_in_buffer = whiteheat_chars_in_buffer,
148 .throttle = whiteheat_throttle,
149 .unthrottle = whiteheat_unthrottle,
150 .read_bulk_callback = whiteheat_read_callback,
151 .write_bulk_callback = whiteheat_write_callback,
152};
153
Alan Stern29618e92012-02-23 14:57:32 -0500154static struct usb_serial_driver * const serial_drivers[] = {
155 &whiteheat_fake_device, &whiteheat_device, NULL
156};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158struct whiteheat_command_private {
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200159 struct mutex mutex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 __u8 port_running;
161 __u8 command_finished;
Alan Cox80359a92008-07-22 11:09:16 +0100162 wait_queue_head_t wait_command; /* for handling sleeping whilst
163 waiting for a command to
164 finish */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 __u8 result_buffer[64];
166};
167
168
169#define THROTTLED 0x01
170#define ACTUALLY_THROTTLED 0x02
171
172static int urb_pool_size = 8;
173
174struct whiteheat_urb_wrap {
175 struct list_head list;
176 struct urb *urb;
177};
178
179struct whiteheat_private {
180 spinlock_t lock;
181 __u8 flags;
Alan Coxa5b6f602008-04-08 17:16:06 +0100182 __u8 mcr; /* FIXME: no locking on mcr */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 struct list_head rx_urbs_free;
184 struct list_head rx_urbs_submitted;
185 struct list_head rx_urb_q;
186 struct work_struct rx_work;
David Howellsc4028952006-11-22 14:57:56 +0000187 struct usb_serial_port *port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 struct list_head tx_urbs_free;
189 struct list_head tx_urbs_submitted;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200190 struct mutex deathwarrant;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191};
192
193
194/* local function prototypes */
195static int start_command_port(struct usb_serial *serial);
196static void stop_command_port(struct usb_serial *serial);
David Howells7d12e782006-10-05 14:55:46 +0100197static void command_port_write_callback(struct urb *urb);
198static void command_port_read_callback(struct urb *urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
200static int start_port_read(struct usb_serial_port *port);
Alan Cox80359a92008-07-22 11:09:16 +0100201static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb,
202 struct list_head *head);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203static struct list_head *list_first(struct list_head *head);
David Howellsc4028952006-11-22 14:57:56 +0000204static void rx_data_softint(struct work_struct *work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
Alan Cox80359a92008-07-22 11:09:16 +0100206static int firm_send_command(struct usb_serial_port *port, __u8 command,
207 __u8 *data, __u8 datasize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208static int firm_open(struct usb_serial_port *port);
209static int firm_close(struct usb_serial_port *port);
Alan Coxfe1ae7f2009-09-19 13:13:33 -0700210static void firm_setup_port(struct tty_struct *tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211static int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
212static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
213static int firm_set_break(struct usb_serial_port *port, __u8 onoff);
214static int firm_purge(struct usb_serial_port *port, __u8 rxtx);
215static int firm_get_dtr_rts(struct usb_serial_port *port);
216static int firm_report_tx_done(struct usb_serial_port *port);
217
218
219#define COMMAND_PORT 4
220#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */
221#define COMMAND_TIMEOUT_MS 2000
222#define CLOSING_DELAY (30 * HZ)
223
224
225/*****************************************************************************
226 * Connect Tech's White Heat prerenumeration driver functions
227 *****************************************************************************/
228
229/* steps to download the firmware to the WhiteHEAT device:
230 - hold the reset (by writing to the reset bit of the CPUCS register)
231 - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
232 - release the reset (by writing to the CPUCS register)
233 - download the WH.HEX file for all addresses greater than 0x1b3f using
234 VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
235 - hold the reset
236 - download the WH.HEX file for all addresses less than 0x1b40 using
237 VENDOR_REQUEST_ANCHOR_LOAD
238 - release the reset
239 - device renumerated itself and comes up as new device id with all
240 firmware download completed.
241*/
Alan Cox80359a92008-07-22 11:09:16 +0100242static int whiteheat_firmware_download(struct usb_serial *serial,
243 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244{
David Woodhouseec6752f2008-05-31 01:35:29 +0300245 int response, ret = -ENOENT;
246 const struct firmware *loader_fw = NULL, *firmware_fw = NULL;
247 const struct ihex_binrec *record;
248
Harvey Harrison441b62c2008-03-03 16:08:34 -0800249 dbg("%s", __func__);
David Woodhouseec6752f2008-05-31 01:35:29 +0300250
251 if (request_ihex_firmware(&firmware_fw, "whiteheat.fw",
252 &serial->dev->dev)) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700253 dev_err(&serial->dev->dev,
254 "%s - request \"whiteheat.fw\" failed\n", __func__);
David Woodhouseec6752f2008-05-31 01:35:29 +0300255 goto out;
256 }
257 if (request_ihex_firmware(&loader_fw, "whiteheat_loader.fw",
258 &serial->dev->dev)) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700259 dev_err(&serial->dev->dev,
260 "%s - request \"whiteheat_loader.fw\" failed\n",
261 __func__);
David Woodhouseec6752f2008-05-31 01:35:29 +0300262 goto out;
263 }
264 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 response = ezusb_set_reset (serial, 1);
266
David Woodhouseec6752f2008-05-31 01:35:29 +0300267 record = (const struct ihex_binrec *)loader_fw->data;
268 while (record) {
269 response = ezusb_writememory (serial, be32_to_cpu(record->addr),
270 (unsigned char *)record->data,
271 be16_to_cpu(record->len), 0xa0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 if (response < 0) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700273 dev_err(&serial->dev->dev, "%s - ezusb_writememory "
274 "failed for loader (%d %04X %p %d)\n",
275 __func__, response, be32_to_cpu(record->addr),
276 record->data, be16_to_cpu(record->len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 break;
278 }
David Woodhouseec6752f2008-05-31 01:35:29 +0300279 record = ihex_next_binrec(record);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 }
281
Alan Cox80359a92008-07-22 11:09:16 +0100282 response = ezusb_set_reset(serial, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
David Woodhouseec6752f2008-05-31 01:35:29 +0300284 record = (const struct ihex_binrec *)firmware_fw->data;
285 while (record && be32_to_cpu(record->addr) < 0x1b40)
286 record = ihex_next_binrec(record);
287 while (record) {
288 response = ezusb_writememory (serial, be32_to_cpu(record->addr),
289 (unsigned char *)record->data,
290 be16_to_cpu(record->len), 0xa3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 if (response < 0) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700292 dev_err(&serial->dev->dev, "%s - ezusb_writememory "
293 "failed for first firmware step "
294 "(%d %04X %p %d)\n", __func__, response,
295 be32_to_cpu(record->addr), record->data,
296 be16_to_cpu(record->len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 break;
298 }
299 ++record;
300 }
Alan Cox80359a92008-07-22 11:09:16 +0100301
302 response = ezusb_set_reset(serial, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
David Woodhouseec6752f2008-05-31 01:35:29 +0300304 record = (const struct ihex_binrec *)firmware_fw->data;
305 while (record && be32_to_cpu(record->addr) < 0x1b40) {
306 response = ezusb_writememory (serial, be32_to_cpu(record->addr),
307 (unsigned char *)record->data,
308 be16_to_cpu(record->len), 0xa0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 if (response < 0) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700310 dev_err(&serial->dev->dev, "%s - ezusb_writememory "
311 "failed for second firmware step "
312 "(%d %04X %p %d)\n", __func__, response,
313 be32_to_cpu(record->addr), record->data,
314 be16_to_cpu(record->len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 break;
316 }
317 ++record;
318 }
David Woodhouseec6752f2008-05-31 01:35:29 +0300319 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 response = ezusb_set_reset (serial, 0);
David Woodhouseec6752f2008-05-31 01:35:29 +0300321 out:
322 release_firmware(loader_fw);
323 release_firmware(firmware_fw);
324 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325}
326
327
Alan Cox80359a92008-07-22 11:09:16 +0100328static int whiteheat_firmware_attach(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
330 /* We want this device to fail to have a driver assigned to it */
331 return 1;
332}
333
334
335/*****************************************************************************
336 * Connect Tech's White Heat serial driver functions
337 *****************************************************************************/
Alan Cox80359a92008-07-22 11:09:16 +0100338static int whiteheat_attach(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
340 struct usb_serial_port *command_port;
341 struct whiteheat_command_private *command_info;
342 struct usb_serial_port *port;
343 struct whiteheat_private *info;
344 struct whiteheat_hw_info *hw_info;
345 int pipe;
346 int ret;
347 int alen;
348 __u8 *command;
349 __u8 *result;
350 int i;
351 int j;
352 struct urb *urb;
353 int buf_size;
354 struct whiteheat_urb_wrap *wrap;
355 struct list_head *tmp;
356
357 command_port = serial->port[COMMAND_PORT];
358
Alan Cox80359a92008-07-22 11:09:16 +0100359 pipe = usb_sndbulkpipe(serial->dev,
360 command_port->bulk_out_endpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 command = kmalloc(2, GFP_KERNEL);
362 if (!command)
363 goto no_command_buffer;
364 command[0] = WHITEHEAT_GET_HW_INFO;
365 command[1] = 0;
Alan Cox80359a92008-07-22 11:09:16 +0100366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL);
368 if (!result)
369 goto no_result_buffer;
370 /*
371 * When the module is reloaded the firmware is still there and
372 * the endpoints are still in the usb core unchanged. This is the
Alan Cox80359a92008-07-22 11:09:16 +0100373 * unlinking bug in disguise. Same for the call below.
374 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 usb_clear_halt(serial->dev, pipe);
Alan Cox80359a92008-07-22 11:09:16 +0100376 ret = usb_bulk_msg(serial->dev, pipe, command, 2,
377 &alen, COMMAND_TIMEOUT_MS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 if (ret) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700379 dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n",
380 serial->type->description, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 goto no_firmware;
Stuart MacDonald09fd6bc2006-05-31 13:28:40 -0400382 } else if (alen != 2) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700383 dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n",
384 serial->type->description, alen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 goto no_firmware;
386 }
387
Alan Cox80359a92008-07-22 11:09:16 +0100388 pipe = usb_rcvbulkpipe(serial->dev,
389 command_port->bulk_in_endpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 /* See the comment on the usb_clear_halt() above */
391 usb_clear_halt(serial->dev, pipe);
Alan Cox80359a92008-07-22 11:09:16 +0100392 ret = usb_bulk_msg(serial->dev, pipe, result,
393 sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 if (ret) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700395 dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n",
396 serial->type->description, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 goto no_firmware;
Stuart MacDonald09fd6bc2006-05-31 13:28:40 -0400398 } else if (alen != sizeof(*hw_info) + 1) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700399 dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n",
400 serial->type->description, alen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 goto no_firmware;
402 } else if (result[0] != command[0]) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700403 dev_err(&serial->dev->dev, "%s: Command failed [%d]\n",
404 serial->type->description, result[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 goto no_firmware;
406 }
407
408 hw_info = (struct whiteheat_hw_info *)&result[1];
409
Johan Hovolda6765cb2012-03-22 16:50:54 +0100410 dev_info(&serial->dev->dev, "%s: Firmware v%d.%02d\n",
411 serial->type->description,
Greg Kroah-Hartmanc197a8d2008-08-18 13:21:04 -0700412 hw_info->sw_major_rev, hw_info->sw_minor_rev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
414 for (i = 0; i < serial->num_ports; i++) {
415 port = serial->port[i];
416
Robert P. J. Day5cbded52006-12-13 00:35:56 -0800417 info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 if (info == NULL) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700419 dev_err(&port->dev,
420 "%s: Out of memory for port structures\n",
421 serial->type->description);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 goto no_private;
423 }
424
425 spin_lock_init(&info->lock);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200426 mutex_init(&info->deathwarrant);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 info->flags = 0;
428 info->mcr = 0;
David Howellsc4028952006-11-22 14:57:56 +0000429 INIT_WORK(&info->rx_work, rx_data_softint);
430 info->port = port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
432 INIT_LIST_HEAD(&info->rx_urbs_free);
433 INIT_LIST_HEAD(&info->rx_urbs_submitted);
434 INIT_LIST_HEAD(&info->rx_urb_q);
435 INIT_LIST_HEAD(&info->tx_urbs_free);
436 INIT_LIST_HEAD(&info->tx_urbs_submitted);
437
438 for (j = 0; j < urb_pool_size; j++) {
439 urb = usb_alloc_urb(0, GFP_KERNEL);
440 if (!urb) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700441 dev_err(&port->dev, "No free urbs available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 goto no_rx_urb;
443 }
444 buf_size = port->read_urb->transfer_buffer_length;
445 urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
446 if (!urb->transfer_buffer) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700447 dev_err(&port->dev,
448 "Couldn't allocate urb buffer\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 goto no_rx_buf;
450 }
451 wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
452 if (!wrap) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700453 dev_err(&port->dev,
454 "Couldn't allocate urb wrapper\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 goto no_rx_wrap;
456 }
457 usb_fill_bulk_urb(urb, serial->dev,
458 usb_rcvbulkpipe(serial->dev,
459 port->bulk_in_endpointAddress),
460 urb->transfer_buffer, buf_size,
461 whiteheat_read_callback, port);
462 wrap->urb = urb;
463 list_add(&wrap->list, &info->rx_urbs_free);
464
465 urb = usb_alloc_urb(0, GFP_KERNEL);
466 if (!urb) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700467 dev_err(&port->dev, "No free urbs available\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 goto no_tx_urb;
469 }
470 buf_size = port->write_urb->transfer_buffer_length;
471 urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);
472 if (!urb->transfer_buffer) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700473 dev_err(&port->dev,
474 "Couldn't allocate urb buffer\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 goto no_tx_buf;
476 }
477 wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);
478 if (!wrap) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700479 dev_err(&port->dev,
480 "Couldn't allocate urb wrapper\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 goto no_tx_wrap;
482 }
483 usb_fill_bulk_urb(urb, serial->dev,
484 usb_sndbulkpipe(serial->dev,
485 port->bulk_out_endpointAddress),
486 urb->transfer_buffer, buf_size,
487 whiteheat_write_callback, port);
488 wrap->urb = urb;
489 list_add(&wrap->list, &info->tx_urbs_free);
490 }
491
492 usb_set_serial_port_data(port, info);
493 }
494
Alan Cox80359a92008-07-22 11:09:16 +0100495 command_info = kmalloc(sizeof(struct whiteheat_command_private),
496 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 if (command_info == NULL) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700498 dev_err(&serial->dev->dev,
499 "%s: Out of memory for port structures\n",
500 serial->type->description);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 goto no_command_private;
502 }
503
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200504 mutex_init(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 command_info->port_running = 0;
506 init_waitqueue_head(&command_info->wait_command);
507 usb_set_serial_port_data(command_port, command_info);
508 command_port->write_urb->complete = command_port_write_callback;
509 command_port->read_urb->complete = command_port_read_callback;
510 kfree(result);
511 kfree(command);
512
513 return 0;
514
515no_firmware:
516 /* Firmware likely not running */
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700517 dev_err(&serial->dev->dev,
518 "%s: Unable to retrieve firmware version, try replugging\n",
519 serial->type->description);
520 dev_err(&serial->dev->dev,
521 "%s: If the firmware is not running (status led not blinking)\n",
522 serial->type->description);
523 dev_err(&serial->dev->dev,
524 "%s: please contact support@connecttech.com\n",
525 serial->type->description);
Jesper Juhl67ca0282006-04-23 19:59:23 +0200526 kfree(result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 return -ENODEV;
528
529no_command_private:
530 for (i = serial->num_ports - 1; i >= 0; i--) {
531 port = serial->port[i];
532 info = usb_get_serial_port_data(port);
533 for (j = urb_pool_size - 1; j >= 0; j--) {
534 tmp = list_first(&info->tx_urbs_free);
535 list_del(tmp);
536 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
537 urb = wrap->urb;
538 kfree(wrap);
539no_tx_wrap:
540 kfree(urb->transfer_buffer);
541no_tx_buf:
542 usb_free_urb(urb);
543no_tx_urb:
544 tmp = list_first(&info->rx_urbs_free);
545 list_del(tmp);
546 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
547 urb = wrap->urb;
548 kfree(wrap);
549no_rx_wrap:
550 kfree(urb->transfer_buffer);
551no_rx_buf:
552 usb_free_urb(urb);
553no_rx_urb:
554 ;
555 }
556 kfree(info);
557no_private:
558 ;
559 }
560 kfree(result);
561no_result_buffer:
562 kfree(command);
563no_command_buffer:
564 return -ENOMEM;
565}
566
567
Alan Sternf9c99bb2009-06-02 11:53:55 -0400568static void whiteheat_release(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
570 struct usb_serial_port *command_port;
571 struct usb_serial_port *port;
572 struct whiteheat_private *info;
573 struct whiteheat_urb_wrap *wrap;
574 struct urb *urb;
575 struct list_head *tmp;
576 struct list_head *tmp2;
577 int i;
578
Harvey Harrison441b62c2008-03-03 16:08:34 -0800579 dbg("%s", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
581 /* free up our private data for our command port */
582 command_port = serial->port[COMMAND_PORT];
Alan Cox80359a92008-07-22 11:09:16 +0100583 kfree(usb_get_serial_port_data(command_port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
585 for (i = 0; i < serial->num_ports; i++) {
586 port = serial->port[i];
587 info = usb_get_serial_port_data(port);
588 list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
589 list_del(tmp);
590 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
591 urb = wrap->urb;
592 kfree(wrap);
593 kfree(urb->transfer_buffer);
594 usb_free_urb(urb);
595 }
596 list_for_each_safe(tmp, tmp2, &info->tx_urbs_free) {
597 list_del(tmp);
598 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
599 urb = wrap->urb;
600 kfree(wrap);
601 kfree(urb->transfer_buffer);
602 usb_free_urb(urb);
603 }
604 kfree(info);
605 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606}
607
Alan Coxa509a7e2009-09-19 13:13:26 -0700608static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
Harvey Harrison441b62c2008-03-03 16:08:34 -0800612 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614 retval = start_command_port(port->serial);
615 if (retval)
616 goto exit;
617
Alan Cox95da3102008-07-22 11:09:07 +0100618 if (tty)
619 tty->low_latency = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
621 /* send an open port command */
622 retval = firm_open(port);
623 if (retval) {
624 stop_command_port(port->serial);
625 goto exit;
626 }
627
628 retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX);
629 if (retval) {
630 firm_close(port);
631 stop_command_port(port->serial);
632 goto exit;
633 }
634
Alan Cox72e27412008-07-22 11:09:29 +0100635 if (tty)
636 firm_setup_port(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
638 /* Work around HCD bugs */
639 usb_clear_halt(port->serial->dev, port->read_urb->pipe);
640 usb_clear_halt(port->serial->dev, port->write_urb->pipe);
641
642 /* Start reading from the device */
643 retval = start_port_read(port);
644 if (retval) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700645 dev_err(&port->dev,
646 "%s - failed submitting read urb, error %d\n",
647 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 firm_close(port);
649 stop_command_port(port->serial);
650 goto exit;
651 }
652
653exit:
Harvey Harrison441b62c2008-03-03 16:08:34 -0800654 dbg("%s - exit, retval = %d", __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 return retval;
656}
657
658
Alan Cox335f8512009-06-11 12:26:29 +0100659static void whiteheat_close(struct usb_serial_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
661 struct whiteheat_private *info = usb_get_serial_port_data(port);
662 struct whiteheat_urb_wrap *wrap;
663 struct urb *urb;
664 struct list_head *tmp;
665 struct list_head *tmp2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
Harvey Harrison441b62c2008-03-03 16:08:34 -0800667 dbg("%s - port %d", __func__, port->number);
Oliver Neukume33fe4d2008-01-21 17:44:10 +0100668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 firm_report_tx_done(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 firm_close(port);
671
672 /* shutdown our bulk reads and writes */
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200673 mutex_lock(&info->deathwarrant);
674 spin_lock_irq(&info->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
676 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
677 urb = wrap->urb;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200678 list_del(tmp);
679 spin_unlock_irq(&info->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 usb_kill_urb(urb);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200681 spin_lock_irq(&info->lock);
682 list_add(tmp, &info->rx_urbs_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 }
Akinobu Mita179e0912006-06-26 00:24:41 -0700684 list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
685 list_move(tmp, &info->rx_urbs_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
687 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
688 urb = wrap->urb;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200689 list_del(tmp);
690 spin_unlock_irq(&info->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 usb_kill_urb(urb);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200692 spin_lock_irq(&info->lock);
693 list_add(tmp, &info->tx_urbs_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 }
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200695 spin_unlock_irq(&info->lock);
696 mutex_unlock(&info->deathwarrant);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 stop_command_port(port->serial);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698}
699
700
Alan Cox95da3102008-07-22 11:09:07 +0100701static int whiteheat_write(struct tty_struct *tty,
702 struct usb_serial_port *port, const unsigned char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 struct whiteheat_private *info = usb_get_serial_port_data(port);
705 struct whiteheat_urb_wrap *wrap;
706 struct urb *urb;
707 int result;
708 int bytes;
709 int sent = 0;
710 unsigned long flags;
711 struct list_head *tmp;
712
Harvey Harrison441b62c2008-03-03 16:08:34 -0800713 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
715 if (count == 0) {
Harvey Harrison441b62c2008-03-03 16:08:34 -0800716 dbg("%s - write request of 0 bytes", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 return (0);
718 }
719
720 while (count) {
721 spin_lock_irqsave(&info->lock, flags);
722 if (list_empty(&info->tx_urbs_free)) {
723 spin_unlock_irqrestore(&info->lock, flags);
724 break;
725 }
726 tmp = list_first(&info->tx_urbs_free);
727 list_del(tmp);
728 spin_unlock_irqrestore(&info->lock, flags);
729
730 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
731 urb = wrap->urb;
Alan Cox80359a92008-07-22 11:09:16 +0100732 bytes = (count > port->bulk_out_size) ?
733 port->bulk_out_size : count;
734 memcpy(urb->transfer_buffer, buf + sent, bytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Alan Cox80359a92008-07-22 11:09:16 +0100736 usb_serial_debug_data(debug, &port->dev,
737 __func__, bytes, urb->transfer_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 urb->transfer_buffer_length = bytes;
740 result = usb_submit_urb(urb, GFP_ATOMIC);
741 if (result) {
Johan Hovold22a416c2012-02-10 13:20:51 +0100742 dev_err_console(port,
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700743 "%s - failed submitting write urb, error %d\n",
744 __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 sent = result;
746 spin_lock_irqsave(&info->lock, flags);
747 list_add(tmp, &info->tx_urbs_free);
748 spin_unlock_irqrestore(&info->lock, flags);
749 break;
750 } else {
751 sent += bytes;
752 count -= bytes;
753 spin_lock_irqsave(&info->lock, flags);
754 list_add(tmp, &info->tx_urbs_submitted);
755 spin_unlock_irqrestore(&info->lock, flags);
756 }
757 }
758
759 return sent;
760}
761
Alan Cox95da3102008-07-22 11:09:07 +0100762static int whiteheat_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763{
Alan Cox95da3102008-07-22 11:09:07 +0100764 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 struct whiteheat_private *info = usb_get_serial_port_data(port);
766 struct list_head *tmp;
767 int room = 0;
768 unsigned long flags;
769
Harvey Harrison441b62c2008-03-03 16:08:34 -0800770 dbg("%s - port %d", __func__, port->number);
Alan Cox80359a92008-07-22 11:09:16 +0100771
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 spin_lock_irqsave(&info->lock, flags);
773 list_for_each(tmp, &info->tx_urbs_free)
774 room++;
775 spin_unlock_irqrestore(&info->lock, flags);
776 room *= port->bulk_out_size;
777
Harvey Harrison441b62c2008-03-03 16:08:34 -0800778 dbg("%s - returns %d", __func__, room);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 return (room);
780}
781
Alan Cox60b33c12011-02-14 16:26:14 +0000782static int whiteheat_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783{
Alan Cox95da3102008-07-22 11:09:07 +0100784 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 struct whiteheat_private *info = usb_get_serial_port_data(port);
786 unsigned int modem_signals = 0;
787
Harvey Harrison441b62c2008-03-03 16:08:34 -0800788 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
790 firm_get_dtr_rts(port);
791 if (info->mcr & UART_MCR_DTR)
792 modem_signals |= TIOCM_DTR;
793 if (info->mcr & UART_MCR_RTS)
794 modem_signals |= TIOCM_RTS;
795
796 return modem_signals;
797}
798
Alan Cox20b9d172011-02-14 16:26:50 +0000799static int whiteheat_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 unsigned int set, unsigned int clear)
801{
Alan Cox95da3102008-07-22 11:09:07 +0100802 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 struct whiteheat_private *info = usb_get_serial_port_data(port);
804
Harvey Harrison441b62c2008-03-03 16:08:34 -0800805 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
807 if (set & TIOCM_RTS)
808 info->mcr |= UART_MCR_RTS;
809 if (set & TIOCM_DTR)
810 info->mcr |= UART_MCR_DTR;
811
812 if (clear & TIOCM_RTS)
813 info->mcr &= ~UART_MCR_RTS;
814 if (clear & TIOCM_DTR)
815 info->mcr &= ~UART_MCR_DTR;
816
817 firm_set_dtr(port, info->mcr & UART_MCR_DTR);
818 firm_set_rts(port, info->mcr & UART_MCR_RTS);
819 return 0;
820}
821
822
Alan Cox00a0d0d2011-02-14 16:27:06 +0000823static int whiteheat_ioctl(struct tty_struct *tty,
Alan Cox80359a92008-07-22 11:09:16 +0100824 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825{
Alan Cox95da3102008-07-22 11:09:07 +0100826 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 struct serial_struct serstruct;
828 void __user *user_arg = (void __user *)arg;
829
Harvey Harrison441b62c2008-03-03 16:08:34 -0800830 dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
832 switch (cmd) {
Alan Cox80359a92008-07-22 11:09:16 +0100833 case TIOCGSERIAL:
834 memset(&serstruct, 0, sizeof(serstruct));
835 serstruct.type = PORT_16654;
836 serstruct.line = port->serial->minor;
837 serstruct.port = port->number;
838 serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
839 serstruct.xmit_fifo_size = port->bulk_out_size;
840 serstruct.custom_divisor = 0;
841 serstruct.baud_base = 460800;
842 serstruct.close_delay = CLOSING_DELAY;
843 serstruct.closing_wait = CLOSING_DELAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Alan Cox80359a92008-07-22 11:09:16 +0100845 if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
846 return -EFAULT;
847 break;
848 default:
849 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 }
851
852 return -ENOIOCTLCMD;
853}
854
855
Alan Cox80359a92008-07-22 11:09:16 +0100856static void whiteheat_set_termios(struct tty_struct *tty,
857 struct usb_serial_port *port, struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858{
Alan Cox95da3102008-07-22 11:09:07 +0100859 firm_setup_port(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860}
861
Alan Cox80359a92008-07-22 11:09:16 +0100862static void whiteheat_break_ctl(struct tty_struct *tty, int break_state)
863{
Alan Cox95da3102008-07-22 11:09:07 +0100864 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 firm_set_break(port, break_state);
866}
867
868
Alan Cox95da3102008-07-22 11:09:07 +0100869static int whiteheat_chars_in_buffer(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870{
Alan Cox95da3102008-07-22 11:09:07 +0100871 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 struct whiteheat_private *info = usb_get_serial_port_data(port);
873 struct list_head *tmp;
874 struct whiteheat_urb_wrap *wrap;
875 int chars = 0;
876 unsigned long flags;
877
Harvey Harrison441b62c2008-03-03 16:08:34 -0800878 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
880 spin_lock_irqsave(&info->lock, flags);
881 list_for_each(tmp, &info->tx_urbs_submitted) {
882 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
883 chars += wrap->urb->transfer_buffer_length;
884 }
885 spin_unlock_irqrestore(&info->lock, flags);
886
Alan Cox80359a92008-07-22 11:09:16 +0100887 dbg("%s - returns %d", __func__, chars);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200888 return chars;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889}
890
891
Alan Cox80359a92008-07-22 11:09:16 +0100892static void whiteheat_throttle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893{
Alan Cox95da3102008-07-22 11:09:07 +0100894 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 struct whiteheat_private *info = usb_get_serial_port_data(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
Harvey Harrison441b62c2008-03-03 16:08:34 -0800897 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Oliver Neukum63832512009-10-07 10:50:23 +0200899 spin_lock_irq(&info->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 info->flags |= THROTTLED;
Oliver Neukum63832512009-10-07 10:50:23 +0200901 spin_unlock_irq(&info->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902}
903
904
Alan Cox80359a92008-07-22 11:09:16 +0100905static void whiteheat_unthrottle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906{
Alan Cox95da3102008-07-22 11:09:07 +0100907 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 struct whiteheat_private *info = usb_get_serial_port_data(port);
909 int actually_throttled;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Harvey Harrison441b62c2008-03-03 16:08:34 -0800911 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
Oliver Neukum63832512009-10-07 10:50:23 +0200913 spin_lock_irq(&info->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 actually_throttled = info->flags & ACTUALLY_THROTTLED;
915 info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
Oliver Neukum63832512009-10-07 10:50:23 +0200916 spin_unlock_irq(&info->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
918 if (actually_throttled)
David Howellsc4028952006-11-22 14:57:56 +0000919 rx_data_softint(&info->rx_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920}
921
922
923/*****************************************************************************
924 * Connect Tech's White Heat callback routines
925 *****************************************************************************/
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200926static void command_port_write_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927{
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700928 int status = urb->status;
929
Harvey Harrison441b62c2008-03-03 16:08:34 -0800930 dbg("%s", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700932 if (status) {
933 dbg("nonzero urb status: %d", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 return;
935 }
936}
937
938
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200939static void command_port_read_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940{
Ming Leicdc97792008-02-24 18:41:47 +0800941 struct usb_serial_port *command_port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 struct whiteheat_command_private *command_info;
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700943 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 unsigned char *data = urb->transfer_buffer;
945 int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
Harvey Harrison441b62c2008-03-03 16:08:34 -0800947 dbg("%s", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 command_info = usb_get_serial_port_data(command_port);
950 if (!command_info) {
Alan Cox80359a92008-07-22 11:09:16 +0100951 dbg("%s - command_info is NULL, exiting.", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 return;
953 }
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700954 if (status) {
Harvey Harrison441b62c2008-03-03 16:08:34 -0800955 dbg("%s - nonzero urb status: %d", __func__, status);
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700956 if (status != -ENOENT)
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200957 command_info->command_finished = WHITEHEAT_CMD_FAILURE;
958 wake_up(&command_info->wait_command);
959 return;
960 }
961
Alan Cox80359a92008-07-22 11:09:16 +0100962 usb_serial_debug_data(debug, &command_port->dev,
963 __func__, urb->actual_length, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
965 if (data[0] == WHITEHEAT_CMD_COMPLETE) {
966 command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200967 wake_up(&command_info->wait_command);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 } else if (data[0] == WHITEHEAT_CMD_FAILURE) {
969 command_info->command_finished = WHITEHEAT_CMD_FAILURE;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200970 wake_up(&command_info->wait_command);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 } else if (data[0] == WHITEHEAT_EVENT) {
Alan Cox80359a92008-07-22 11:09:16 +0100972 /* These are unsolicited reports from the firmware, hence no
973 waiting command to wakeup */
Harvey Harrison441b62c2008-03-03 16:08:34 -0800974 dbg("%s - event received", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 } else if (data[0] == WHITEHEAT_GET_DTR_RTS) {
Alan Cox80359a92008-07-22 11:09:16 +0100976 memcpy(command_info->result_buffer, &data[1],
977 urb->actual_length - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200979 wake_up(&command_info->wait_command);
Alan Cox80359a92008-07-22 11:09:16 +0100980 } else
Harvey Harrison441b62c2008-03-03 16:08:34 -0800981 dbg("%s - bad reply from firmware", __func__);
Alan Cox80359a92008-07-22 11:09:16 +0100982
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 /* Continue trying to always read */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 if (result)
Alan Cox80359a92008-07-22 11:09:16 +0100986 dbg("%s - failed resubmitting read urb, error %d",
987 __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988}
989
990
David Howells7d12e782006-10-05 14:55:46 +0100991static void whiteheat_read_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992{
Ming Leicdc97792008-02-24 18:41:47 +0800993 struct usb_serial_port *port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 struct whiteheat_urb_wrap *wrap;
995 unsigned char *data = urb->transfer_buffer;
996 struct whiteheat_private *info = usb_get_serial_port_data(port);
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700997 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
Harvey Harrison441b62c2008-03-03 16:08:34 -0800999 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
1001 spin_lock(&info->lock);
1002 wrap = urb_to_wrap(urb, &info->rx_urbs_submitted);
1003 if (!wrap) {
1004 spin_unlock(&info->lock);
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -07001005 dev_err(&port->dev, "%s - Not my urb!\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 return;
1007 }
1008 list_del(&wrap->list);
1009 spin_unlock(&info->lock);
1010
Greg Kroah-Hartman05400012007-06-15 15:44:13 -07001011 if (status) {
1012 dbg("%s - nonzero read bulk status received: %d",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001013 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 spin_lock(&info->lock);
1015 list_add(&wrap->list, &info->rx_urbs_free);
1016 spin_unlock(&info->lock);
1017 return;
1018 }
1019
Alan Cox80359a92008-07-22 11:09:16 +01001020 usb_serial_debug_data(debug, &port->dev,
1021 __func__, urb->actual_length, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022
1023 spin_lock(&info->lock);
1024 list_add_tail(&wrap->list, &info->rx_urb_q);
1025 if (info->flags & THROTTLED) {
1026 info->flags |= ACTUALLY_THROTTLED;
1027 spin_unlock(&info->lock);
1028 return;
1029 }
1030 spin_unlock(&info->lock);
1031
1032 schedule_work(&info->rx_work);
1033}
1034
1035
David Howells7d12e782006-10-05 14:55:46 +01001036static void whiteheat_write_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037{
Ming Leicdc97792008-02-24 18:41:47 +08001038 struct usb_serial_port *port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 struct whiteheat_private *info = usb_get_serial_port_data(port);
1040 struct whiteheat_urb_wrap *wrap;
Greg Kroah-Hartman05400012007-06-15 15:44:13 -07001041 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042
Harvey Harrison441b62c2008-03-03 16:08:34 -08001043 dbg("%s - port %d", __func__, port->number);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
1045 spin_lock(&info->lock);
1046 wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);
1047 if (!wrap) {
1048 spin_unlock(&info->lock);
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -07001049 dev_err(&port->dev, "%s - Not my urb!\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 return;
1051 }
Akinobu Mita179e0912006-06-26 00:24:41 -07001052 list_move(&wrap->list, &info->tx_urbs_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 spin_unlock(&info->lock);
1054
Greg Kroah-Hartman05400012007-06-15 15:44:13 -07001055 if (status) {
1056 dbg("%s - nonzero write bulk status received: %d",
Harvey Harrison441b62c2008-03-03 16:08:34 -08001057 __func__, status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 return;
1059 }
1060
Pete Zaitcevcf2c7482006-05-22 21:58:49 -07001061 usb_serial_port_softint(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062}
1063
1064
1065/*****************************************************************************
1066 * Connect Tech's White Heat firmware interface
1067 *****************************************************************************/
Alan Cox80359a92008-07-22 11:09:16 +01001068static int firm_send_command(struct usb_serial_port *port, __u8 command,
1069 __u8 *data, __u8 datasize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
1071 struct usb_serial_port *command_port;
1072 struct whiteheat_command_private *command_info;
1073 struct whiteheat_private *info;
1074 __u8 *transfer_buffer;
1075 int retval = 0;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001076 int t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
Harvey Harrison441b62c2008-03-03 16:08:34 -08001078 dbg("%s - command %d", __func__, command);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079
1080 command_port = port->serial->port[COMMAND_PORT];
1081 command_info = usb_get_serial_port_data(command_port);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001082 mutex_lock(&command_info->mutex);
Richard Knutsson38c3cb52007-03-26 22:00:28 -08001083 command_info->command_finished = false;
Alan Cox80359a92008-07-22 11:09:16 +01001084
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
1086 transfer_buffer[0] = command;
Alan Cox80359a92008-07-22 11:09:16 +01001087 memcpy(&transfer_buffer[1], data, datasize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 command_port->write_urb->transfer_buffer_length = datasize + 1;
Alan Cox80359a92008-07-22 11:09:16 +01001089 retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 if (retval) {
Harvey Harrison441b62c2008-03-03 16:08:34 -08001091 dbg("%s - submit urb failed", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 goto exit;
1093 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094
1095 /* wait for the command to complete */
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001096 t = wait_event_timeout(command_info->wait_command,
Richard Knutsson38c3cb52007-03-26 22:00:28 -08001097 (bool)command_info->command_finished, COMMAND_TIMEOUT);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001098 if (!t)
1099 usb_kill_urb(command_port->write_urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100
Richard Knutsson38c3cb52007-03-26 22:00:28 -08001101 if (command_info->command_finished == false) {
Harvey Harrison441b62c2008-03-03 16:08:34 -08001102 dbg("%s - command timed out.", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 retval = -ETIMEDOUT;
1104 goto exit;
1105 }
1106
1107 if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
Harvey Harrison441b62c2008-03-03 16:08:34 -08001108 dbg("%s - command failed.", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 retval = -EIO;
1110 goto exit;
1111 }
1112
1113 if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
Harvey Harrison441b62c2008-03-03 16:08:34 -08001114 dbg("%s - command completed.", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 switch (command) {
Alan Cox80359a92008-07-22 11:09:16 +01001116 case WHITEHEAT_GET_DTR_RTS:
1117 info = usb_get_serial_port_data(port);
1118 memcpy(&info->mcr, command_info->result_buffer,
1119 sizeof(struct whiteheat_dr_info));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 break;
1121 }
1122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123exit:
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001124 mutex_unlock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 return retval;
1126}
1127
1128
Alan Cox80359a92008-07-22 11:09:16 +01001129static int firm_open(struct usb_serial_port *port)
1130{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 struct whiteheat_simple open_command;
1132
1133 open_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +01001134 return firm_send_command(port, WHITEHEAT_OPEN,
1135 (__u8 *)&open_command, sizeof(open_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136}
1137
1138
Alan Cox80359a92008-07-22 11:09:16 +01001139static int firm_close(struct usb_serial_port *port)
1140{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 struct whiteheat_simple close_command;
1142
1143 close_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +01001144 return firm_send_command(port, WHITEHEAT_CLOSE,
1145 (__u8 *)&close_command, sizeof(close_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146}
1147
1148
Alan Coxfe1ae7f2009-09-19 13:13:33 -07001149static void firm_setup_port(struct tty_struct *tty)
Alan Cox80359a92008-07-22 11:09:16 +01001150{
Alan Cox95da3102008-07-22 11:09:07 +01001151 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 struct whiteheat_port_settings port_settings;
Alan Cox95da3102008-07-22 11:09:07 +01001153 unsigned int cflag = tty->termios->c_cflag;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154
1155 port_settings.port = port->number + 1;
1156
1157 /* get the byte size */
1158 switch (cflag & CSIZE) {
Alan Cox80359a92008-07-22 11:09:16 +01001159 case CS5: port_settings.bits = 5; break;
1160 case CS6: port_settings.bits = 6; break;
1161 case CS7: port_settings.bits = 7; break;
1162 default:
1163 case CS8: port_settings.bits = 8; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 }
Harvey Harrison441b62c2008-03-03 16:08:34 -08001165 dbg("%s - data bits = %d", __func__, port_settings.bits);
Alan Cox80359a92008-07-22 11:09:16 +01001166
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 /* determine the parity */
1168 if (cflag & PARENB)
1169 if (cflag & CMSPAR)
1170 if (cflag & PARODD)
1171 port_settings.parity = WHITEHEAT_PAR_MARK;
1172 else
1173 port_settings.parity = WHITEHEAT_PAR_SPACE;
1174 else
1175 if (cflag & PARODD)
1176 port_settings.parity = WHITEHEAT_PAR_ODD;
1177 else
1178 port_settings.parity = WHITEHEAT_PAR_EVEN;
1179 else
1180 port_settings.parity = WHITEHEAT_PAR_NONE;
Harvey Harrison441b62c2008-03-03 16:08:34 -08001181 dbg("%s - parity = %c", __func__, port_settings.parity);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182
1183 /* figure out the stop bits requested */
1184 if (cflag & CSTOPB)
1185 port_settings.stop = 2;
1186 else
1187 port_settings.stop = 1;
Harvey Harrison441b62c2008-03-03 16:08:34 -08001188 dbg("%s - stop bits = %d", __func__, port_settings.stop);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
1190 /* figure out the flow control settings */
1191 if (cflag & CRTSCTS)
Alan Cox80359a92008-07-22 11:09:16 +01001192 port_settings.hflow = (WHITEHEAT_HFLOW_CTS |
1193 WHITEHEAT_HFLOW_RTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 else
1195 port_settings.hflow = WHITEHEAT_HFLOW_NONE;
Harvey Harrison441b62c2008-03-03 16:08:34 -08001196 dbg("%s - hardware flow control = %s %s %s %s", __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
1198 (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
1199 (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
1200 (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : "");
Alan Cox80359a92008-07-22 11:09:16 +01001201
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 /* determine software flow control */
Alan Cox95da3102008-07-22 11:09:07 +01001203 if (I_IXOFF(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
1205 else
1206 port_settings.sflow = WHITEHEAT_SFLOW_NONE;
Harvey Harrison441b62c2008-03-03 16:08:34 -08001207 dbg("%s - software flow control = %c", __func__, port_settings.sflow);
Alan Cox80359a92008-07-22 11:09:16 +01001208
Alan Cox95da3102008-07-22 11:09:07 +01001209 port_settings.xon = START_CHAR(tty);
1210 port_settings.xoff = STOP_CHAR(tty);
Alan Cox80359a92008-07-22 11:09:16 +01001211 dbg("%s - XON = %2x, XOFF = %2x",
1212 __func__, port_settings.xon, port_settings.xoff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213
1214 /* get the baud rate wanted */
Alan Cox95da3102008-07-22 11:09:07 +01001215 port_settings.baud = tty_get_baud_rate(tty);
Harvey Harrison441b62c2008-03-03 16:08:34 -08001216 dbg("%s - baud rate = %d", __func__, port_settings.baud);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Alan Cox01d1df22007-10-18 01:24:23 -07001218 /* fixme: should set validated settings */
Alan Cox95da3102008-07-22 11:09:07 +01001219 tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 /* handle any settings that aren't specified in the tty structure */
1221 port_settings.lloop = 0;
Alan Cox80359a92008-07-22 11:09:16 +01001222
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 /* now send the message to the device */
Alan Coxfe1ae7f2009-09-19 13:13:33 -07001224 firm_send_command(port, WHITEHEAT_SETUP_PORT,
Alan Cox80359a92008-07-22 11:09:16 +01001225 (__u8 *)&port_settings, sizeof(port_settings));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226}
1227
1228
Alan Cox80359a92008-07-22 11:09:16 +01001229static int firm_set_rts(struct usb_serial_port *port, __u8 onoff)
1230{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 struct whiteheat_set_rdb rts_command;
1232
1233 rts_command.port = port->number - port->serial->minor + 1;
1234 rts_command.state = onoff;
Alan Cox80359a92008-07-22 11:09:16 +01001235 return firm_send_command(port, WHITEHEAT_SET_RTS,
1236 (__u8 *)&rts_command, sizeof(rts_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237}
1238
1239
Alan Cox80359a92008-07-22 11:09:16 +01001240static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff)
1241{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 struct whiteheat_set_rdb dtr_command;
1243
1244 dtr_command.port = port->number - port->serial->minor + 1;
1245 dtr_command.state = onoff;
Alan Cox72e27412008-07-22 11:09:29 +01001246 return firm_send_command(port, WHITEHEAT_SET_DTR,
Alan Cox80359a92008-07-22 11:09:16 +01001247 (__u8 *)&dtr_command, sizeof(dtr_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248}
1249
1250
Alan Cox80359a92008-07-22 11:09:16 +01001251static int firm_set_break(struct usb_serial_port *port, __u8 onoff)
1252{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 struct whiteheat_set_rdb break_command;
1254
1255 break_command.port = port->number - port->serial->minor + 1;
1256 break_command.state = onoff;
Alan Cox72e27412008-07-22 11:09:29 +01001257 return firm_send_command(port, WHITEHEAT_SET_BREAK,
Alan Cox80359a92008-07-22 11:09:16 +01001258 (__u8 *)&break_command, sizeof(break_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259}
1260
1261
Alan Cox80359a92008-07-22 11:09:16 +01001262static int firm_purge(struct usb_serial_port *port, __u8 rxtx)
1263{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 struct whiteheat_purge purge_command;
1265
1266 purge_command.port = port->number - port->serial->minor + 1;
1267 purge_command.what = rxtx;
Alan Cox80359a92008-07-22 11:09:16 +01001268 return firm_send_command(port, WHITEHEAT_PURGE,
1269 (__u8 *)&purge_command, sizeof(purge_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270}
1271
1272
Alan Cox80359a92008-07-22 11:09:16 +01001273static int firm_get_dtr_rts(struct usb_serial_port *port)
1274{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 struct whiteheat_simple get_dr_command;
1276
1277 get_dr_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +01001278 return firm_send_command(port, WHITEHEAT_GET_DTR_RTS,
1279 (__u8 *)&get_dr_command, sizeof(get_dr_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280}
1281
1282
Alan Cox80359a92008-07-22 11:09:16 +01001283static int firm_report_tx_done(struct usb_serial_port *port)
1284{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 struct whiteheat_simple close_command;
1286
1287 close_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +01001288 return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE,
1289 (__u8 *)&close_command, sizeof(close_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290}
1291
1292
1293/*****************************************************************************
1294 * Connect Tech's White Heat utility functions
1295 *****************************************************************************/
1296static int start_command_port(struct usb_serial *serial)
1297{
1298 struct usb_serial_port *command_port;
1299 struct whiteheat_command_private *command_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 int retval = 0;
Alan Cox80359a92008-07-22 11:09:16 +01001301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 command_port = serial->port[COMMAND_PORT];
1303 command_info = usb_get_serial_port_data(command_port);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001304 mutex_lock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 if (!command_info->port_running) {
1306 /* Work around HCD bugs */
1307 usb_clear_halt(serial->dev, command_port->read_urb->pipe);
1308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);
1310 if (retval) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -07001311 dev_err(&serial->dev->dev,
1312 "%s - failed submitting read urb, error %d\n",
1313 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 goto exit;
1315 }
1316 }
1317 command_info->port_running++;
1318
1319exit:
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001320 mutex_unlock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 return retval;
1322}
1323
1324
1325static void stop_command_port(struct usb_serial *serial)
1326{
1327 struct usb_serial_port *command_port;
1328 struct whiteheat_command_private *command_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 command_port = serial->port[COMMAND_PORT];
1331 command_info = usb_get_serial_port_data(command_port);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001332 mutex_lock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 command_info->port_running--;
1334 if (!command_info->port_running)
1335 usb_kill_urb(command_port->read_urb);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001336 mutex_unlock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337}
1338
1339
1340static int start_port_read(struct usb_serial_port *port)
1341{
1342 struct whiteheat_private *info = usb_get_serial_port_data(port);
1343 struct whiteheat_urb_wrap *wrap;
1344 struct urb *urb;
1345 int retval = 0;
1346 unsigned long flags;
1347 struct list_head *tmp;
1348 struct list_head *tmp2;
1349
1350 spin_lock_irqsave(&info->lock, flags);
1351
1352 list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) {
1353 list_del(tmp);
1354 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
1355 urb = wrap->urb;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001356 spin_unlock_irqrestore(&info->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 retval = usb_submit_urb(urb, GFP_KERNEL);
1358 if (retval) {
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001359 spin_lock_irqsave(&info->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 list_add(tmp, &info->rx_urbs_free);
1361 list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) {
1362 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
1363 urb = wrap->urb;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001364 list_del(tmp);
1365 spin_unlock_irqrestore(&info->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 usb_kill_urb(urb);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001367 spin_lock_irqsave(&info->lock, flags);
1368 list_add(tmp, &info->rx_urbs_free);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 }
1370 break;
1371 }
Oliver Neukum08a2b3b2007-05-24 13:52:51 +02001372 spin_lock_irqsave(&info->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 list_add(tmp, &info->rx_urbs_submitted);
1374 }
1375
1376 spin_unlock_irqrestore(&info->lock, flags);
1377
1378 return retval;
1379}
1380
1381
Alan Cox80359a92008-07-22 11:09:16 +01001382static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb,
1383 struct list_head *head)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384{
1385 struct whiteheat_urb_wrap *wrap;
1386 struct list_head *tmp;
1387
1388 list_for_each(tmp, head) {
1389 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
1390 if (wrap->urb == urb)
1391 return wrap;
1392 }
1393
1394 return NULL;
1395}
1396
1397
1398static struct list_head *list_first(struct list_head *head)
1399{
1400 return head->next;
1401}
1402
1403
David Howellsc4028952006-11-22 14:57:56 +00001404static void rx_data_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405{
David Howellsc4028952006-11-22 14:57:56 +00001406 struct whiteheat_private *info =
1407 container_of(work, struct whiteheat_private, rx_work);
1408 struct usb_serial_port *port = info->port;
Alan Cox4a90f092008-10-13 10:39:46 +01001409 struct tty_struct *tty = tty_port_tty_get(&port->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 struct whiteheat_urb_wrap *wrap;
1411 struct urb *urb;
1412 unsigned long flags;
1413 struct list_head *tmp;
1414 struct list_head *tmp2;
1415 int result;
1416 int sent = 0;
1417
1418 spin_lock_irqsave(&info->lock, flags);
1419 if (info->flags & THROTTLED) {
1420 spin_unlock_irqrestore(&info->lock, flags);
Alan Cox4a90f092008-10-13 10:39:46 +01001421 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 }
1423
1424 list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
1425 list_del(tmp);
1426 spin_unlock_irqrestore(&info->lock, flags);
1427
1428 wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
1429 urb = wrap->urb;
1430
Alan Cox67ccbd62010-02-17 13:06:57 +00001431 if (tty && urb->actual_length)
1432 sent += tty_insert_flip_string(tty,
1433 urb->transfer_buffer, urb->actual_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 result = usb_submit_urb(urb, GFP_ATOMIC);
1436 if (result) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -07001437 dev_err(&port->dev,
1438 "%s - failed resubmitting read urb, error %d\n",
Alan Cox80359a92008-07-22 11:09:16 +01001439 __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 spin_lock_irqsave(&info->lock, flags);
1441 list_add(tmp, &info->rx_urbs_free);
1442 continue;
1443 }
1444
1445 spin_lock_irqsave(&info->lock, flags);
1446 list_add(tmp, &info->rx_urbs_submitted);
1447 }
1448 spin_unlock_irqrestore(&info->lock, flags);
1449
1450 if (sent)
1451 tty_flip_buffer_push(tty);
Alan Cox4a90f092008-10-13 10:39:46 +01001452out:
1453 tty_kref_put(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454}
1455
Greg Kroah-Hartman421f3882012-02-28 13:12:44 -08001456module_usb_serial_driver(whiteheat_driver, serial_drivers);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457
Alan Cox80359a92008-07-22 11:09:16 +01001458MODULE_AUTHOR(DRIVER_AUTHOR);
1459MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460MODULE_LICENSE("GPL");
1461
David Woodhouseec6752f2008-05-31 01:35:29 +03001462MODULE_FIRMWARE("whiteheat.fw");
1463MODULE_FIRMWARE("whiteheat_loader.fw");
1464
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465module_param(urb_pool_size, int, 0);
1466MODULE_PARM_DESC(urb_pool_size, "Number of urbs to use for buffering");
1467
1468module_param(debug, bool, S_IRUGO | S_IWUSR);
1469MODULE_PARM_DESC(debug, "Debug enabled or not");