blob: 8172ea3aead0d7b3ac42e99164a6fe7ff6a94f31 [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>
Rene Buergelcc183e22012-09-18 09:00:41 +020035#include <linux/usb/ezusb.h>
David Woodhouseec6752f2008-05-31 01:35:29 +030036#include <linux/firmware.h>
37#include <linux/ihex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "whiteheat.h" /* WhiteHEAT specific commands */
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#ifndef CMSPAR
41#define CMSPAR 0
42#endif
43
44/*
45 * Version Information
46 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>"
48#define DRIVER_DESC "USB ConnectTech WhiteHEAT driver"
49
50#define CONNECT_TECH_VENDOR_ID 0x0710
51#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001
52#define CONNECT_TECH_WHITE_HEAT_ID 0x8001
53
54/*
55 ID tables for whiteheat are unusual, because we want to different
56 things for different versions of the device. Eventually, this
57 will be doable from a single table. But, for now, we define two
58 separate ID tables, and then a third table that combines them
59 just for the purpose of exporting the autoloading information.
60*/
Németh Márton7d40d7e2010-01-10 15:34:24 +010061static const struct usb_device_id id_table_std[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
63 { } /* Terminating entry */
64};
65
Németh Márton7d40d7e2010-01-10 15:34:24 +010066static const struct usb_device_id id_table_prerenumeration[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
68 { } /* Terminating entry */
69};
70
Németh Márton7d40d7e2010-01-10 15:34:24 +010071static const struct usb_device_id id_table_combined[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) },
73 { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) },
74 { } /* Terminating entry */
75};
76
Alan Cox80359a92008-07-22 11:09:16 +010077MODULE_DEVICE_TABLE(usb, id_table_combined);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
80/* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */
Alan Cox80359a92008-07-22 11:09:16 +010081static int whiteheat_firmware_download(struct usb_serial *serial,
82 const struct usb_device_id *id);
83static int whiteheat_firmware_attach(struct usb_serial *serial);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85/* function prototypes for the Connect Tech WhiteHEAT serial converter */
Alan Cox80359a92008-07-22 11:09:16 +010086static int whiteheat_attach(struct usb_serial *serial);
Alan Sternf9c99bb2009-06-02 11:53:55 -040087static void whiteheat_release(struct usb_serial *serial);
Alan Cox80359a92008-07-22 11:09:16 +010088static int whiteheat_open(struct tty_struct *tty,
Alan Coxa509a7e2009-09-19 13:13:26 -070089 struct usb_serial_port *port);
Alan Cox335f8512009-06-11 12:26:29 +010090static void whiteheat_close(struct usb_serial_port *port);
Alan Cox00a0d0d2011-02-14 16:27:06 +000091static int whiteheat_ioctl(struct tty_struct *tty,
Alan Cox80359a92008-07-22 11:09:16 +010092 unsigned int cmd, unsigned long arg);
93static void whiteheat_set_termios(struct tty_struct *tty,
94 struct usb_serial_port *port, struct ktermios *old);
Alan Cox60b33c12011-02-14 16:26:14 +000095static int whiteheat_tiocmget(struct tty_struct *tty);
Alan Cox20b9d172011-02-14 16:26:50 +000096static int whiteheat_tiocmset(struct tty_struct *tty,
Alan Cox80359a92008-07-22 11:09:16 +010097 unsigned int set, unsigned int clear);
98static void whiteheat_break_ctl(struct tty_struct *tty, int break_state);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -0700100static struct usb_serial_driver whiteheat_fake_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700101 .driver = {
102 .owner = THIS_MODULE,
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700103 .name = "whiteheatnofirm",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700104 },
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700105 .description = "Connect Tech - WhiteHEAT - (prerenumeration)",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 .id_table = id_table_prerenumeration,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 .num_ports = 1,
108 .probe = whiteheat_firmware_download,
109 .attach = whiteheat_firmware_attach,
110};
111
Greg Kroah-Hartmanea653702005-06-20 21:15:16 -0700112static struct usb_serial_driver whiteheat_device = {
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700113 .driver = {
114 .owner = THIS_MODULE,
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700115 .name = "whiteheat",
Greg Kroah-Hartman18fcac32005-06-20 21:15:16 -0700116 },
Greg Kroah-Hartman269bda12005-06-20 21:15:16 -0700117 .description = "Connect Tech - WhiteHEAT",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 .id_table = id_table_std,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 .num_ports = 4,
120 .attach = whiteheat_attach,
Alan Sternf9c99bb2009-06-02 11:53:55 -0400121 .release = whiteheat_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 .open = whiteheat_open,
123 .close = whiteheat_close,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 .ioctl = whiteheat_ioctl,
125 .set_termios = whiteheat_set_termios,
126 .break_ctl = whiteheat_break_ctl,
127 .tiocmget = whiteheat_tiocmget,
128 .tiocmset = whiteheat_tiocmset,
Johan Hovold5c0c7582012-03-22 16:50:55 +0100129 .throttle = usb_serial_generic_throttle,
130 .unthrottle = usb_serial_generic_unthrottle,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131};
132
Alan Stern29618e92012-02-23 14:57:32 -0500133static struct usb_serial_driver * const serial_drivers[] = {
134 &whiteheat_fake_device, &whiteheat_device, NULL
135};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137struct whiteheat_command_private {
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200138 struct mutex mutex;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 __u8 port_running;
140 __u8 command_finished;
Alan Cox80359a92008-07-22 11:09:16 +0100141 wait_queue_head_t wait_command; /* for handling sleeping whilst
142 waiting for a command to
143 finish */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 __u8 result_buffer[64];
145};
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147struct whiteheat_private {
Alan Coxa5b6f602008-04-08 17:16:06 +0100148 __u8 mcr; /* FIXME: no locking on mcr */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149};
150
151
152/* local function prototypes */
153static int start_command_port(struct usb_serial *serial);
154static void stop_command_port(struct usb_serial *serial);
David Howells7d12e782006-10-05 14:55:46 +0100155static void command_port_write_callback(struct urb *urb);
156static void command_port_read_callback(struct urb *urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
Alan Cox80359a92008-07-22 11:09:16 +0100158static int firm_send_command(struct usb_serial_port *port, __u8 command,
159 __u8 *data, __u8 datasize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160static int firm_open(struct usb_serial_port *port);
161static int firm_close(struct usb_serial_port *port);
Alan Coxfe1ae7f2009-09-19 13:13:33 -0700162static void firm_setup_port(struct tty_struct *tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163static int firm_set_rts(struct usb_serial_port *port, __u8 onoff);
164static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff);
165static int firm_set_break(struct usb_serial_port *port, __u8 onoff);
166static int firm_purge(struct usb_serial_port *port, __u8 rxtx);
167static int firm_get_dtr_rts(struct usb_serial_port *port);
168static int firm_report_tx_done(struct usb_serial_port *port);
169
170
171#define COMMAND_PORT 4
172#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */
173#define COMMAND_TIMEOUT_MS 2000
174#define CLOSING_DELAY (30 * HZ)
175
176
177/*****************************************************************************
178 * Connect Tech's White Heat prerenumeration driver functions
179 *****************************************************************************/
180
181/* steps to download the firmware to the WhiteHEAT device:
182 - hold the reset (by writing to the reset bit of the CPUCS register)
183 - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
184 - release the reset (by writing to the CPUCS register)
185 - download the WH.HEX file for all addresses greater than 0x1b3f using
186 VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
187 - hold the reset
188 - download the WH.HEX file for all addresses less than 0x1b40 using
189 VENDOR_REQUEST_ANCHOR_LOAD
190 - release the reset
191 - device renumerated itself and comes up as new device id with all
192 firmware download completed.
193*/
Alan Cox80359a92008-07-22 11:09:16 +0100194static int whiteheat_firmware_download(struct usb_serial *serial,
195 const struct usb_device_id *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
David Woodhouseec6752f2008-05-31 01:35:29 +0300197 int response, ret = -ENOENT;
198 const struct firmware *loader_fw = NULL, *firmware_fw = NULL;
199 const struct ihex_binrec *record;
200
David Woodhouseec6752f2008-05-31 01:35:29 +0300201 if (request_ihex_firmware(&firmware_fw, "whiteheat.fw",
202 &serial->dev->dev)) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700203 dev_err(&serial->dev->dev,
204 "%s - request \"whiteheat.fw\" failed\n", __func__);
David Woodhouseec6752f2008-05-31 01:35:29 +0300205 goto out;
206 }
207 if (request_ihex_firmware(&loader_fw, "whiteheat_loader.fw",
208 &serial->dev->dev)) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700209 dev_err(&serial->dev->dev,
210 "%s - request \"whiteheat_loader.fw\" failed\n",
211 __func__);
David Woodhouseec6752f2008-05-31 01:35:29 +0300212 goto out;
213 }
214 ret = 0;
Rene Buergelcc183e22012-09-18 09:00:41 +0200215 response = ezusb_fx1_set_reset(serial->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
David Woodhouseec6752f2008-05-31 01:35:29 +0300217 record = (const struct ihex_binrec *)loader_fw->data;
218 while (record) {
Rene Buergel99495c72012-09-13 22:14:38 +0200219 response = ezusb_writememory(serial->dev, be32_to_cpu(record->addr),
220 (unsigned char *)record->data,
221 be16_to_cpu(record->len), 0xa0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 if (response < 0) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700223 dev_err(&serial->dev->dev, "%s - ezusb_writememory "
224 "failed for loader (%d %04X %p %d)\n",
225 __func__, response, be32_to_cpu(record->addr),
226 record->data, be16_to_cpu(record->len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 break;
228 }
David Woodhouseec6752f2008-05-31 01:35:29 +0300229 record = ihex_next_binrec(record);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 }
231
Rene Buergelcc183e22012-09-18 09:00:41 +0200232 response = ezusb_fx1_set_reset(serial->dev, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
David Woodhouseec6752f2008-05-31 01:35:29 +0300234 record = (const struct ihex_binrec *)firmware_fw->data;
235 while (record && be32_to_cpu(record->addr) < 0x1b40)
236 record = ihex_next_binrec(record);
237 while (record) {
Rene Buergel99495c72012-09-13 22:14:38 +0200238 response = ezusb_writememory(serial->dev, be32_to_cpu(record->addr),
239 (unsigned char *)record->data,
240 be16_to_cpu(record->len), 0xa3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 if (response < 0) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700242 dev_err(&serial->dev->dev, "%s - ezusb_writememory "
243 "failed for first firmware step "
244 "(%d %04X %p %d)\n", __func__, response,
245 be32_to_cpu(record->addr), record->data,
246 be16_to_cpu(record->len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 break;
248 }
249 ++record;
250 }
Alan Cox80359a92008-07-22 11:09:16 +0100251
Rene Buergelcc183e22012-09-18 09:00:41 +0200252 response = ezusb_fx1_set_reset(serial->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
David Woodhouseec6752f2008-05-31 01:35:29 +0300254 record = (const struct ihex_binrec *)firmware_fw->data;
255 while (record && be32_to_cpu(record->addr) < 0x1b40) {
Rene Buergel99495c72012-09-13 22:14:38 +0200256 response = ezusb_writememory(serial->dev, be32_to_cpu(record->addr),
257 (unsigned char *)record->data,
258 be16_to_cpu(record->len), 0xa0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 if (response < 0) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700260 dev_err(&serial->dev->dev, "%s - ezusb_writememory "
261 "failed for second firmware step "
262 "(%d %04X %p %d)\n", __func__, response,
263 be32_to_cpu(record->addr), record->data,
264 be16_to_cpu(record->len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 break;
266 }
267 ++record;
268 }
David Woodhouseec6752f2008-05-31 01:35:29 +0300269 ret = 0;
Rene Buergelcc183e22012-09-18 09:00:41 +0200270 response = ezusb_fx1_set_reset(serial->dev, 0);
David Woodhouseec6752f2008-05-31 01:35:29 +0300271 out:
272 release_firmware(loader_fw);
273 release_firmware(firmware_fw);
274 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275}
276
277
Alan Cox80359a92008-07-22 11:09:16 +0100278static int whiteheat_firmware_attach(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
280 /* We want this device to fail to have a driver assigned to it */
281 return 1;
282}
283
284
285/*****************************************************************************
286 * Connect Tech's White Heat serial driver functions
287 *****************************************************************************/
Alan Cox80359a92008-07-22 11:09:16 +0100288static int whiteheat_attach(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
290 struct usb_serial_port *command_port;
291 struct whiteheat_command_private *command_info;
292 struct usb_serial_port *port;
293 struct whiteheat_private *info;
294 struct whiteheat_hw_info *hw_info;
295 int pipe;
296 int ret;
297 int alen;
298 __u8 *command;
299 __u8 *result;
300 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
302 command_port = serial->port[COMMAND_PORT];
303
Alan Cox80359a92008-07-22 11:09:16 +0100304 pipe = usb_sndbulkpipe(serial->dev,
305 command_port->bulk_out_endpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 command = kmalloc(2, GFP_KERNEL);
307 if (!command)
308 goto no_command_buffer;
309 command[0] = WHITEHEAT_GET_HW_INFO;
310 command[1] = 0;
Alan Cox80359a92008-07-22 11:09:16 +0100311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL);
313 if (!result)
314 goto no_result_buffer;
315 /*
316 * When the module is reloaded the firmware is still there and
317 * the endpoints are still in the usb core unchanged. This is the
Alan Cox80359a92008-07-22 11:09:16 +0100318 * unlinking bug in disguise. Same for the call below.
319 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 usb_clear_halt(serial->dev, pipe);
Alan Cox80359a92008-07-22 11:09:16 +0100321 ret = usb_bulk_msg(serial->dev, pipe, command, 2,
322 &alen, COMMAND_TIMEOUT_MS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 if (ret) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700324 dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n",
325 serial->type->description, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 goto no_firmware;
Stuart MacDonald09fd6bc2006-05-31 13:28:40 -0400327 } else if (alen != 2) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700328 dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n",
329 serial->type->description, alen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 goto no_firmware;
331 }
332
Alan Cox80359a92008-07-22 11:09:16 +0100333 pipe = usb_rcvbulkpipe(serial->dev,
334 command_port->bulk_in_endpointAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 /* See the comment on the usb_clear_halt() above */
336 usb_clear_halt(serial->dev, pipe);
Alan Cox80359a92008-07-22 11:09:16 +0100337 ret = usb_bulk_msg(serial->dev, pipe, result,
338 sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 if (ret) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700340 dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n",
341 serial->type->description, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 goto no_firmware;
Stuart MacDonald09fd6bc2006-05-31 13:28:40 -0400343 } else if (alen != sizeof(*hw_info) + 1) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700344 dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n",
345 serial->type->description, alen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 goto no_firmware;
347 } else if (result[0] != command[0]) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700348 dev_err(&serial->dev->dev, "%s: Command failed [%d]\n",
349 serial->type->description, result[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 goto no_firmware;
351 }
352
353 hw_info = (struct whiteheat_hw_info *)&result[1];
354
Johan Hovolda6765cb2012-03-22 16:50:54 +0100355 dev_info(&serial->dev->dev, "%s: Firmware v%d.%02d\n",
356 serial->type->description,
Greg Kroah-Hartmanc197a8d2008-08-18 13:21:04 -0700357 hw_info->sw_major_rev, hw_info->sw_minor_rev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
359 for (i = 0; i < serial->num_ports; i++) {
360 port = serial->port[i];
361
Robert P. J. Day5cbded52006-12-13 00:35:56 -0800362 info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 if (info == NULL) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700364 dev_err(&port->dev,
365 "%s: Out of memory for port structures\n",
366 serial->type->description);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 goto no_private;
368 }
369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 info->mcr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372 usb_set_serial_port_data(port, info);
373 }
374
Alan Cox80359a92008-07-22 11:09:16 +0100375 command_info = kmalloc(sizeof(struct whiteheat_command_private),
376 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 if (command_info == NULL) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700378 dev_err(&serial->dev->dev,
379 "%s: Out of memory for port structures\n",
380 serial->type->description);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 goto no_command_private;
382 }
383
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200384 mutex_init(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 command_info->port_running = 0;
386 init_waitqueue_head(&command_info->wait_command);
387 usb_set_serial_port_data(command_port, command_info);
388 command_port->write_urb->complete = command_port_write_callback;
389 command_port->read_urb->complete = command_port_read_callback;
390 kfree(result);
391 kfree(command);
392
393 return 0;
394
395no_firmware:
396 /* Firmware likely not running */
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700397 dev_err(&serial->dev->dev,
398 "%s: Unable to retrieve firmware version, try replugging\n",
399 serial->type->description);
400 dev_err(&serial->dev->dev,
401 "%s: If the firmware is not running (status led not blinking)\n",
402 serial->type->description);
403 dev_err(&serial->dev->dev,
404 "%s: please contact support@connecttech.com\n",
405 serial->type->description);
Jesper Juhl67ca0282006-04-23 19:59:23 +0200406 kfree(result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 return -ENODEV;
408
409no_command_private:
410 for (i = serial->num_ports - 1; i >= 0; i--) {
411 port = serial->port[i];
412 info = usb_get_serial_port_data(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 kfree(info);
414no_private:
415 ;
416 }
417 kfree(result);
418no_result_buffer:
419 kfree(command);
420no_command_buffer:
421 return -ENOMEM;
422}
423
424
Alan Sternf9c99bb2009-06-02 11:53:55 -0400425static void whiteheat_release(struct usb_serial *serial)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426{
427 struct usb_serial_port *command_port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 struct whiteheat_private *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 int i;
430
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 /* free up our private data for our command port */
432 command_port = serial->port[COMMAND_PORT];
Alan Cox80359a92008-07-22 11:09:16 +0100433 kfree(usb_get_serial_port_data(command_port));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
435 for (i = 0; i < serial->num_ports; i++) {
Johan Hovold5c0c7582012-03-22 16:50:55 +0100436 info = usb_get_serial_port_data(serial->port[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 kfree(info);
438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439}
440
Alan Coxa509a7e2009-09-19 13:13:26 -0700441static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442{
Johan Hovold5c0c7582012-03-22 16:50:55 +0100443 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 retval = start_command_port(port->serial);
446 if (retval)
447 goto exit;
448
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 /* send an open port command */
450 retval = firm_open(port);
451 if (retval) {
452 stop_command_port(port->serial);
453 goto exit;
454 }
455
456 retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX);
457 if (retval) {
458 firm_close(port);
459 stop_command_port(port->serial);
460 goto exit;
461 }
462
Alan Cox72e27412008-07-22 11:09:29 +0100463 if (tty)
464 firm_setup_port(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
466 /* Work around HCD bugs */
467 usb_clear_halt(port->serial->dev, port->read_urb->pipe);
468 usb_clear_halt(port->serial->dev, port->write_urb->pipe);
469
Johan Hovold5c0c7582012-03-22 16:50:55 +0100470 retval = usb_serial_generic_open(tty, port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 if (retval) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 firm_close(port);
473 stop_command_port(port->serial);
474 goto exit;
475 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476exit:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 return retval;
478}
479
480
Alan Cox335f8512009-06-11 12:26:29 +0100481static void whiteheat_close(struct usb_serial_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 firm_report_tx_done(port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 firm_close(port);
485
Johan Hovold5c0c7582012-03-22 16:50:55 +0100486 usb_serial_generic_close(port);
487
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 stop_command_port(port->serial);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
Alan Cox60b33c12011-02-14 16:26:14 +0000491static int whiteheat_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Alan Cox95da3102008-07-22 11:09:07 +0100493 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 struct whiteheat_private *info = usb_get_serial_port_data(port);
495 unsigned int modem_signals = 0;
496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 firm_get_dtr_rts(port);
498 if (info->mcr & UART_MCR_DTR)
499 modem_signals |= TIOCM_DTR;
500 if (info->mcr & UART_MCR_RTS)
501 modem_signals |= TIOCM_RTS;
502
503 return modem_signals;
504}
505
Alan Cox20b9d172011-02-14 16:26:50 +0000506static int whiteheat_tiocmset(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 unsigned int set, unsigned int clear)
508{
Alan Cox95da3102008-07-22 11:09:07 +0100509 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 struct whiteheat_private *info = usb_get_serial_port_data(port);
511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 if (set & TIOCM_RTS)
513 info->mcr |= UART_MCR_RTS;
514 if (set & TIOCM_DTR)
515 info->mcr |= UART_MCR_DTR;
516
517 if (clear & TIOCM_RTS)
518 info->mcr &= ~UART_MCR_RTS;
519 if (clear & TIOCM_DTR)
520 info->mcr &= ~UART_MCR_DTR;
521
522 firm_set_dtr(port, info->mcr & UART_MCR_DTR);
523 firm_set_rts(port, info->mcr & UART_MCR_RTS);
524 return 0;
525}
526
527
Alan Cox00a0d0d2011-02-14 16:27:06 +0000528static int whiteheat_ioctl(struct tty_struct *tty,
Alan Cox80359a92008-07-22 11:09:16 +0100529 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
Alan Cox95da3102008-07-22 11:09:07 +0100531 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 struct serial_struct serstruct;
533 void __user *user_arg = (void __user *)arg;
534
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700535 dev_dbg(&port->dev, "%s - cmd 0x%.4x\n", __func__, cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537 switch (cmd) {
Alan Cox80359a92008-07-22 11:09:16 +0100538 case TIOCGSERIAL:
539 memset(&serstruct, 0, sizeof(serstruct));
540 serstruct.type = PORT_16654;
541 serstruct.line = port->serial->minor;
542 serstruct.port = port->number;
543 serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
Johan Hovold5c0c7582012-03-22 16:50:55 +0100544 serstruct.xmit_fifo_size = kfifo_size(&port->write_fifo);
Alan Cox80359a92008-07-22 11:09:16 +0100545 serstruct.custom_divisor = 0;
546 serstruct.baud_base = 460800;
547 serstruct.close_delay = CLOSING_DELAY;
548 serstruct.closing_wait = CLOSING_DELAY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Alan Cox80359a92008-07-22 11:09:16 +0100550 if (copy_to_user(user_arg, &serstruct, sizeof(serstruct)))
551 return -EFAULT;
552 break;
553 default:
554 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
556
557 return -ENOIOCTLCMD;
558}
559
560
Alan Cox80359a92008-07-22 11:09:16 +0100561static void whiteheat_set_termios(struct tty_struct *tty,
562 struct usb_serial_port *port, struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563{
Alan Cox95da3102008-07-22 11:09:07 +0100564 firm_setup_port(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Alan Cox80359a92008-07-22 11:09:16 +0100567static void whiteheat_break_ctl(struct tty_struct *tty, int break_state)
568{
Alan Cox95da3102008-07-22 11:09:07 +0100569 struct usb_serial_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 firm_set_break(port, break_state);
571}
572
573
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574/*****************************************************************************
575 * Connect Tech's White Heat callback routines
576 *****************************************************************************/
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200577static void command_port_write_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700579 int status = urb->status;
580
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700581 if (status) {
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700582 dev_dbg(&urb->dev->dev, "nonzero urb status: %d\n", status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 return;
584 }
585}
586
587
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200588static void command_port_read_callback(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
Ming Leicdc97792008-02-24 18:41:47 +0800590 struct usb_serial_port *command_port = urb->context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 struct whiteheat_command_private *command_info;
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700592 int status = urb->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 unsigned char *data = urb->transfer_buffer;
594 int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 command_info = usb_get_serial_port_data(command_port);
597 if (!command_info) {
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700598 dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 return;
600 }
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700601 if (status) {
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700602 dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status);
Greg Kroah-Hartman05400012007-06-15 15:44:13 -0700603 if (status != -ENOENT)
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200604 command_info->command_finished = WHITEHEAT_CMD_FAILURE;
605 wake_up(&command_info->wait_command);
606 return;
607 }
608
Greg Kroah-Hartman59d33f22012-09-18 09:58:57 +0100609 usb_serial_debug_data(&command_port->dev, __func__, urb->actual_length, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
611 if (data[0] == WHITEHEAT_CMD_COMPLETE) {
612 command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200613 wake_up(&command_info->wait_command);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 } else if (data[0] == WHITEHEAT_CMD_FAILURE) {
615 command_info->command_finished = WHITEHEAT_CMD_FAILURE;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200616 wake_up(&command_info->wait_command);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 } else if (data[0] == WHITEHEAT_EVENT) {
Alan Cox80359a92008-07-22 11:09:16 +0100618 /* These are unsolicited reports from the firmware, hence no
619 waiting command to wakeup */
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700620 dev_dbg(&urb->dev->dev, "%s - event received\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 } else if (data[0] == WHITEHEAT_GET_DTR_RTS) {
Alan Cox80359a92008-07-22 11:09:16 +0100622 memcpy(command_info->result_buffer, &data[1],
623 urb->actual_length - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200625 wake_up(&command_info->wait_command);
Alan Cox80359a92008-07-22 11:09:16 +0100626 } else
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700627 dev_dbg(&urb->dev->dev, "%s - bad reply from firmware\n", __func__);
Alan Cox80359a92008-07-22 11:09:16 +0100628
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 /* Continue trying to always read */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 if (result)
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700632 dev_dbg(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n",
Alan Cox80359a92008-07-22 11:09:16 +0100633 __func__, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
635
636
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637/*****************************************************************************
638 * Connect Tech's White Heat firmware interface
639 *****************************************************************************/
Alan Cox80359a92008-07-22 11:09:16 +0100640static int firm_send_command(struct usb_serial_port *port, __u8 command,
641 __u8 *data, __u8 datasize)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642{
643 struct usb_serial_port *command_port;
644 struct whiteheat_command_private *command_info;
645 struct whiteheat_private *info;
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700646 struct device *dev = &port->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 __u8 *transfer_buffer;
648 int retval = 0;
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200649 int t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700651 dev_dbg(dev, "%s - command %d\n", __func__, command);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 command_port = port->serial->port[COMMAND_PORT];
654 command_info = usb_get_serial_port_data(command_port);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200655 mutex_lock(&command_info->mutex);
Richard Knutsson38c3cb52007-03-26 22:00:28 -0800656 command_info->command_finished = false;
Alan Cox80359a92008-07-22 11:09:16 +0100657
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer;
659 transfer_buffer[0] = command;
Alan Cox80359a92008-07-22 11:09:16 +0100660 memcpy(&transfer_buffer[1], data, datasize);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 command_port->write_urb->transfer_buffer_length = datasize + 1;
Alan Cox80359a92008-07-22 11:09:16 +0100662 retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 if (retval) {
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700664 dev_dbg(dev, "%s - submit urb failed\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 goto exit;
666 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
668 /* wait for the command to complete */
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200669 t = wait_event_timeout(command_info->wait_command,
Richard Knutsson38c3cb52007-03-26 22:00:28 -0800670 (bool)command_info->command_finished, COMMAND_TIMEOUT);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200671 if (!t)
672 usb_kill_urb(command_port->write_urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Richard Knutsson38c3cb52007-03-26 22:00:28 -0800674 if (command_info->command_finished == false) {
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700675 dev_dbg(dev, "%s - command timed out.\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 retval = -ETIMEDOUT;
677 goto exit;
678 }
679
680 if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700681 dev_dbg(dev, "%s - command failed.\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 retval = -EIO;
683 goto exit;
684 }
685
686 if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700687 dev_dbg(dev, "%s - command completed.\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 switch (command) {
Alan Cox80359a92008-07-22 11:09:16 +0100689 case WHITEHEAT_GET_DTR_RTS:
690 info = usb_get_serial_port_data(port);
691 memcpy(&info->mcr, command_info->result_buffer,
692 sizeof(struct whiteheat_dr_info));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 break;
694 }
695 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696exit:
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200697 mutex_unlock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 return retval;
699}
700
701
Alan Cox80359a92008-07-22 11:09:16 +0100702static int firm_open(struct usb_serial_port *port)
703{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 struct whiteheat_simple open_command;
705
706 open_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +0100707 return firm_send_command(port, WHITEHEAT_OPEN,
708 (__u8 *)&open_command, sizeof(open_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709}
710
711
Alan Cox80359a92008-07-22 11:09:16 +0100712static int firm_close(struct usb_serial_port *port)
713{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 struct whiteheat_simple close_command;
715
716 close_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +0100717 return firm_send_command(port, WHITEHEAT_CLOSE,
718 (__u8 *)&close_command, sizeof(close_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719}
720
721
Alan Coxfe1ae7f2009-09-19 13:13:33 -0700722static void firm_setup_port(struct tty_struct *tty)
Alan Cox80359a92008-07-22 11:09:16 +0100723{
Alan Cox95da3102008-07-22 11:09:07 +0100724 struct usb_serial_port *port = tty->driver_data;
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700725 struct device *dev = &port->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 struct whiteheat_port_settings port_settings;
Alan Cox95da3102008-07-22 11:09:07 +0100727 unsigned int cflag = tty->termios->c_cflag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 port_settings.port = port->number + 1;
730
731 /* get the byte size */
732 switch (cflag & CSIZE) {
Alan Cox80359a92008-07-22 11:09:16 +0100733 case CS5: port_settings.bits = 5; break;
734 case CS6: port_settings.bits = 6; break;
735 case CS7: port_settings.bits = 7; break;
736 default:
737 case CS8: port_settings.bits = 8; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 }
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700739 dev_dbg(dev, "%s - data bits = %d\n", __func__, port_settings.bits);
Alan Cox80359a92008-07-22 11:09:16 +0100740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 /* determine the parity */
742 if (cflag & PARENB)
743 if (cflag & CMSPAR)
744 if (cflag & PARODD)
745 port_settings.parity = WHITEHEAT_PAR_MARK;
746 else
747 port_settings.parity = WHITEHEAT_PAR_SPACE;
748 else
749 if (cflag & PARODD)
750 port_settings.parity = WHITEHEAT_PAR_ODD;
751 else
752 port_settings.parity = WHITEHEAT_PAR_EVEN;
753 else
754 port_settings.parity = WHITEHEAT_PAR_NONE;
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700755 dev_dbg(dev, "%s - parity = %c\n", __func__, port_settings.parity);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757 /* figure out the stop bits requested */
758 if (cflag & CSTOPB)
759 port_settings.stop = 2;
760 else
761 port_settings.stop = 1;
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700762 dev_dbg(dev, "%s - stop bits = %d\n", __func__, port_settings.stop);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
764 /* figure out the flow control settings */
765 if (cflag & CRTSCTS)
Alan Cox80359a92008-07-22 11:09:16 +0100766 port_settings.hflow = (WHITEHEAT_HFLOW_CTS |
767 WHITEHEAT_HFLOW_RTS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 else
769 port_settings.hflow = WHITEHEAT_HFLOW_NONE;
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700770 dev_dbg(dev, "%s - hardware flow control = %s %s %s %s\n", __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
772 (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
773 (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
774 (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : "");
Alan Cox80359a92008-07-22 11:09:16 +0100775
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 /* determine software flow control */
Alan Cox95da3102008-07-22 11:09:07 +0100777 if (I_IXOFF(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
779 else
780 port_settings.sflow = WHITEHEAT_SFLOW_NONE;
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700781 dev_dbg(dev, "%s - software flow control = %c\n", __func__, port_settings.sflow);
Alan Cox80359a92008-07-22 11:09:16 +0100782
Alan Cox95da3102008-07-22 11:09:07 +0100783 port_settings.xon = START_CHAR(tty);
784 port_settings.xoff = STOP_CHAR(tty);
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700785 dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
787 /* get the baud rate wanted */
Alan Cox95da3102008-07-22 11:09:07 +0100788 port_settings.baud = tty_get_baud_rate(tty);
Greg Kroah-Hartman255b7032012-09-14 11:50:35 -0700789 dev_dbg(dev, "%s - baud rate = %d\n", __func__, port_settings.baud);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Alan Cox01d1df22007-10-18 01:24:23 -0700791 /* fixme: should set validated settings */
Alan Cox95da3102008-07-22 11:09:07 +0100792 tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 /* handle any settings that aren't specified in the tty structure */
794 port_settings.lloop = 0;
Alan Cox80359a92008-07-22 11:09:16 +0100795
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 /* now send the message to the device */
Alan Coxfe1ae7f2009-09-19 13:13:33 -0700797 firm_send_command(port, WHITEHEAT_SETUP_PORT,
Alan Cox80359a92008-07-22 11:09:16 +0100798 (__u8 *)&port_settings, sizeof(port_settings));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799}
800
801
Alan Cox80359a92008-07-22 11:09:16 +0100802static int firm_set_rts(struct usb_serial_port *port, __u8 onoff)
803{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 struct whiteheat_set_rdb rts_command;
805
806 rts_command.port = port->number - port->serial->minor + 1;
807 rts_command.state = onoff;
Alan Cox80359a92008-07-22 11:09:16 +0100808 return firm_send_command(port, WHITEHEAT_SET_RTS,
809 (__u8 *)&rts_command, sizeof(rts_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810}
811
812
Alan Cox80359a92008-07-22 11:09:16 +0100813static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff)
814{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 struct whiteheat_set_rdb dtr_command;
816
817 dtr_command.port = port->number - port->serial->minor + 1;
818 dtr_command.state = onoff;
Alan Cox72e27412008-07-22 11:09:29 +0100819 return firm_send_command(port, WHITEHEAT_SET_DTR,
Alan Cox80359a92008-07-22 11:09:16 +0100820 (__u8 *)&dtr_command, sizeof(dtr_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821}
822
823
Alan Cox80359a92008-07-22 11:09:16 +0100824static int firm_set_break(struct usb_serial_port *port, __u8 onoff)
825{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 struct whiteheat_set_rdb break_command;
827
828 break_command.port = port->number - port->serial->minor + 1;
829 break_command.state = onoff;
Alan Cox72e27412008-07-22 11:09:29 +0100830 return firm_send_command(port, WHITEHEAT_SET_BREAK,
Alan Cox80359a92008-07-22 11:09:16 +0100831 (__u8 *)&break_command, sizeof(break_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832}
833
834
Alan Cox80359a92008-07-22 11:09:16 +0100835static int firm_purge(struct usb_serial_port *port, __u8 rxtx)
836{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 struct whiteheat_purge purge_command;
838
839 purge_command.port = port->number - port->serial->minor + 1;
840 purge_command.what = rxtx;
Alan Cox80359a92008-07-22 11:09:16 +0100841 return firm_send_command(port, WHITEHEAT_PURGE,
842 (__u8 *)&purge_command, sizeof(purge_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843}
844
845
Alan Cox80359a92008-07-22 11:09:16 +0100846static int firm_get_dtr_rts(struct usb_serial_port *port)
847{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 struct whiteheat_simple get_dr_command;
849
850 get_dr_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +0100851 return firm_send_command(port, WHITEHEAT_GET_DTR_RTS,
852 (__u8 *)&get_dr_command, sizeof(get_dr_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853}
854
855
Alan Cox80359a92008-07-22 11:09:16 +0100856static int firm_report_tx_done(struct usb_serial_port *port)
857{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 struct whiteheat_simple close_command;
859
860 close_command.port = port->number - port->serial->minor + 1;
Alan Cox80359a92008-07-22 11:09:16 +0100861 return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE,
862 (__u8 *)&close_command, sizeof(close_command));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863}
864
865
866/*****************************************************************************
867 * Connect Tech's White Heat utility functions
868 *****************************************************************************/
869static int start_command_port(struct usb_serial *serial)
870{
871 struct usb_serial_port *command_port;
872 struct whiteheat_command_private *command_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 int retval = 0;
Alan Cox80359a92008-07-22 11:09:16 +0100874
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 command_port = serial->port[COMMAND_PORT];
876 command_info = usb_get_serial_port_data(command_port);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200877 mutex_lock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 if (!command_info->port_running) {
879 /* Work around HCD bugs */
880 usb_clear_halt(serial->dev, command_port->read_urb->pipe);
881
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);
883 if (retval) {
Greg Kroah-Hartman194343d2008-08-20 16:56:34 -0700884 dev_err(&serial->dev->dev,
885 "%s - failed submitting read urb, error %d\n",
886 __func__, retval);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 goto exit;
888 }
889 }
890 command_info->port_running++;
891
892exit:
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200893 mutex_unlock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 return retval;
895}
896
897
898static void stop_command_port(struct usb_serial *serial)
899{
900 struct usb_serial_port *command_port;
901 struct whiteheat_command_private *command_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
903 command_port = serial->port[COMMAND_PORT];
904 command_info = usb_get_serial_port_data(command_port);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200905 mutex_lock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 command_info->port_running--;
907 if (!command_info->port_running)
908 usb_kill_urb(command_port->read_urb);
Oliver Neukum08a2b3b2007-05-24 13:52:51 +0200909 mutex_unlock(&command_info->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910}
911
Greg Kroah-Hartman68e24112012-05-08 15:46:14 -0700912module_usb_serial_driver(serial_drivers, id_table_combined);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Alan Cox80359a92008-07-22 11:09:16 +0100914MODULE_AUTHOR(DRIVER_AUTHOR);
915MODULE_DESCRIPTION(DRIVER_DESC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916MODULE_LICENSE("GPL");
917
David Woodhouseec6752f2008-05-31 01:35:29 +0300918MODULE_FIRMWARE("whiteheat.fw");
919MODULE_FIRMWARE("whiteheat_loader.fw");