2 * USB ConnectTech WhiteHEAT driver
4 * Copyright (C) 1999, 2000
5 * Greg Kroah-Hartman (greg@kroah.com)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * See Documentation/usb/usb-serial.txt for more information on using this driver
15 * Fixed bug with urb->dev not being set properly, now that the usb
19 * firmware is improved to guard against crap sent to device
20 * firmware now replies CMD_FAILURE on bad things
21 * read_callback fix you provided for private info struct
22 * command_finished now indicates success or fail
23 * setup_port struct now packed to avoid gcc padding
24 * firmware uses 1 based port numbering, driver now handles that
27 * Removed DEBUG #ifdefs with call to usb_serial_debug_data
30 * Added module_init and module_exit functions to handle the fact that this
31 * driver is a loadable module now.
32 * Fixed bug with port->minor that was found by Al Borchers
35 * Added support for port settings. Baud rate can now be changed. Line signals
36 * are not transferred to and from the tty layer yet, but things seem to be
40 * First cut at open and close commands. Data can flow through the ports at
44 * Split driver up into device specific pieces.
48 #include <linux/config.h>
49 #include <linux/kernel.h>
50 #include <linux/sched.h>
51 #include <linux/signal.h>
52 #include <linux/errno.h>
53 #include <linux/poll.h>
54 #include <linux/init.h>
55 #include <linux/malloc.h>
56 #include <linux/fcntl.h>
57 #include <linux/tty_driver.h>
58 #include <linux/tty_flip.h>
59 #include <linux/tty.h>
60 #include <linux/module.h>
61 #include <linux/spinlock.h>
63 #ifdef CONFIG_USB_SERIAL_DEBUG
68 #include <linux/usb.h>
70 #include "usb-serial.h"
72 #include "whiteheat_fw.h" /* firmware for the ConnectTech WhiteHEAT device */
74 #include "whiteheat.h" /* WhiteHEAT specific commands */
76 #define CONNECT_TECH_VENDOR_ID 0x0710
77 #define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001
78 #define CONNECT_TECH_WHITE_HEAT_ID 0x8001
80 /* function prototypes for the Connect Tech WhiteHEAT serial converter */
81 static int whiteheat_open (struct usb_serial_port
*port
, struct file
*filp
);
82 static void whiteheat_close (struct usb_serial_port
*port
, struct file
*filp
);
83 static int whiteheat_ioctl (struct usb_serial_port
*port
, struct file
* file
, unsigned int cmd
, unsigned long arg
);
84 static void whiteheat_set_termios (struct usb_serial_port
*port
, struct termios
* old
);
85 static void whiteheat_throttle (struct usb_serial_port
*port
);
86 static void whiteheat_unthrottle (struct usb_serial_port
*port
);
87 static int whiteheat_startup (struct usb_serial
*serial
);
88 static void whiteheat_shutdown (struct usb_serial
*serial
);
90 /* All of the device info needed for the Connect Tech WhiteHEAT */
91 static __u16 connecttech_vendor_id
= CONNECT_TECH_VENDOR_ID
;
92 static __u16 connecttech_whiteheat_fake_product_id
= CONNECT_TECH_FAKE_WHITE_HEAT_ID
;
93 static __u16 connecttech_whiteheat_product_id
= CONNECT_TECH_WHITE_HEAT_ID
;
94 struct usb_serial_device_type whiteheat_fake_device
= {
95 name
: "Connect Tech - WhiteHEAT - (prerenumeration)",
96 idVendor
: &connecttech_vendor_id
, /* the Connect Tech vendor id */
97 idProduct
: &connecttech_whiteheat_fake_product_id
, /* the White Heat initial product id */
98 needs_interrupt_in
: DONT_CARE
, /* don't have to have an interrupt in endpoint */
99 needs_bulk_in
: DONT_CARE
, /* don't have to have a bulk in endpoint */
100 needs_bulk_out
: DONT_CARE
, /* don't have to have a bulk out endpoint */
101 num_interrupt_in
: NUM_DONT_CARE
,
102 num_bulk_in
: NUM_DONT_CARE
,
103 num_bulk_out
: NUM_DONT_CARE
,
105 startup
: whiteheat_startup
107 struct usb_serial_device_type whiteheat_device
= {
108 name
: "Connect Tech - WhiteHEAT",
109 idVendor
: &connecttech_vendor_id
, /* the Connect Tech vendor id */
110 idProduct
: &connecttech_whiteheat_product_id
, /* the White Heat real product id */
111 needs_interrupt_in
: DONT_CARE
, /* don't have to have an interrupt in endpoint */
112 needs_bulk_in
: DONT_CARE
, /* don't have to have a bulk in endpoint */
113 needs_bulk_out
: DONT_CARE
, /* don't have to have a bulk out endpoint */
114 num_interrupt_in
: NUM_DONT_CARE
,
115 num_bulk_in
: NUM_DONT_CARE
,
116 num_bulk_out
: NUM_DONT_CARE
,
118 open
: whiteheat_open
,
119 close
: whiteheat_close
,
120 throttle
: whiteheat_throttle
,
121 unthrottle
: whiteheat_unthrottle
,
122 ioctl
: whiteheat_ioctl
,
123 set_termios
: whiteheat_set_termios
,
124 shutdown
: whiteheat_shutdown
,
127 struct whiteheat_private
{
128 __u8 command_finished
;
129 wait_queue_head_t wait_command
; /* for handling sleeping while waiting for a command to finish */
133 /* local function prototypes */
134 static inline void set_rts (struct usb_serial_port
*port
, unsigned char rts
);
135 static inline void set_dtr (struct usb_serial_port
*port
, unsigned char dtr
);
136 static inline void set_break (struct usb_serial_port
*port
, unsigned char brk
);
140 #define COMMAND_PORT 4
141 #define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */
143 /*****************************************************************************
144 * Connect Tech's White Heat specific driver functions
145 *****************************************************************************/
146 static void command_port_write_callback (struct urb
*urb
)
151 dbg ("nonzero urb status: %d", urb
->status
);
155 usb_serial_debug_data (__FILE__
, __FUNCTION__
, urb
->actual_length
, urb
->transfer_buffer
);
161 static void command_port_read_callback (struct urb
*urb
)
163 struct usb_serial_port
*port
= (struct usb_serial_port
*)urb
->context
;
164 struct usb_serial
*serial
= get_usb_serial (port
, __FUNCTION__
);
165 struct whiteheat_private
*info
;
166 unsigned char *data
= urb
->transfer_buffer
;
172 dbg (__FUNCTION__
" - nonzero urb status: %d", urb
->status
);
177 dbg(__FUNCTION__
" - bad serial pointer, exiting");
181 usb_serial_debug_data (__FILE__
, __FUNCTION__
, urb
->actual_length
, data
);
183 info
= (struct whiteheat_private
*)port
->private;
185 dbg (__FUNCTION__
" - info is NULL, exiting.");
189 /* right now, if the command is COMMAND_COMPLETE, just flip the bit saying the command finished */
190 /* in the future we're going to have to pay attention to the actual command that completed */
191 if (data
[0] == WHITEHEAT_CMD_COMPLETE
) {
192 info
->command_finished
= WHITEHEAT_CMD_COMPLETE
;
193 wake_up_interruptible(&info
->wait_command
);
196 if (data
[0] == WHITEHEAT_CMD_FAILURE
) {
197 info
->command_finished
= WHITEHEAT_CMD_FAILURE
;
198 wake_up_interruptible(&info
->wait_command
);
201 /* Continue trying to always read */
202 FILL_BULK_URB(port
->read_urb
, serial
->dev
,
203 usb_rcvbulkpipe(serial
->dev
, port
->bulk_in_endpointAddress
),
204 port
->read_urb
->transfer_buffer
, port
->read_urb
->transfer_buffer_length
,
205 command_port_read_callback
, port
);
206 result
= usb_submit_urb(port
->read_urb
);
208 dbg(__FUNCTION__
" - failed resubmitting read urb, error %d", result
);
212 static int whiteheat_send_cmd (struct usb_serial
*serial
, __u8 command
, __u8
*data
, __u8 datasize
)
214 struct whiteheat_private
*info
;
215 struct usb_serial_port
*port
;
217 __u8
*transfer_buffer
;
219 dbg(__FUNCTION__
" - command %d", command
);
221 port
= &serial
->port
[COMMAND_PORT
];
222 info
= (struct whiteheat_private
*)port
->private;
223 info
->command_finished
= FALSE
;
225 transfer_buffer
= (__u8
*)port
->write_urb
->transfer_buffer
;
226 transfer_buffer
[0] = command
;
227 memcpy (&transfer_buffer
[1], data
, datasize
);
228 port
->write_urb
->transfer_buffer_length
= datasize
+ 1;
229 port
->write_urb
->dev
= serial
->dev
;
230 if (usb_submit_urb (port
->write_urb
)) {
231 dbg (__FUNCTION__
" - submit urb failed");
235 /* wait for the command to complete */
236 timeout
= COMMAND_TIMEOUT
;
237 while (timeout
&& (info
->command_finished
== FALSE
)) {
238 timeout
= interruptible_sleep_on_timeout (&info
->wait_command
, timeout
);
241 if (info
->command_finished
== FALSE
) {
242 dbg (__FUNCTION__
" - command timed out.");
246 if (info
->command_finished
== WHITEHEAT_CMD_FAILURE
) {
247 dbg (__FUNCTION__
" - command failed.");
251 if (info
->command_finished
== WHITEHEAT_CMD_COMPLETE
) {
252 dbg (__FUNCTION__
" - command completed.");
260 static int whiteheat_open (struct usb_serial_port
*port
, struct file
*filp
)
262 struct whiteheat_min_set open_command
;
263 struct usb_serial_port
*command_port
;
264 struct whiteheat_private
*info
;
267 dbg(__FUNCTION__
" - port %d", port
->number
);
270 dbg (__FUNCTION__
" - device already open");
275 /* set up some stuff for our command port */
276 command_port
= &port
->serial
->port
[COMMAND_PORT
];
277 if (command_port
->private == NULL
) {
278 info
= (struct whiteheat_private
*)kmalloc (sizeof(struct whiteheat_private
), GFP_KERNEL
);
280 err(__FUNCTION__
" - out of memory");
284 init_waitqueue_head(&info
->wait_command
);
285 command_port
->private = info
;
286 command_port
->write_urb
->complete
= command_port_write_callback
;
287 command_port
->read_urb
->complete
= command_port_read_callback
;
288 command_port
->read_urb
->dev
= port
->serial
->dev
;
289 command_port
->tty
= port
->tty
; /* need this to "fake" our our sanity check macros */
290 usb_submit_urb (command_port
->read_urb
);
293 /* Start reading from the device */
294 port
->read_urb
->dev
= port
->serial
->dev
;
295 result
= usb_submit_urb(port
->read_urb
);
297 err(__FUNCTION__
" - failed submitting read urb, error %d", result
);
299 /* send an open port command */
300 /* firmware uses 1 based port numbering */
301 open_command
.port
= port
->number
- port
->serial
->minor
+ 1;
302 whiteheat_send_cmd (port
->serial
, WHITEHEAT_OPEN
, (__u8
*)&open_command
, sizeof(open_command
));
304 /* Need to do device specific setup here (control lines, baud rate, etc.) */
307 dbg(__FUNCTION__
" - exit");
313 static void whiteheat_close(struct usb_serial_port
*port
, struct file
* filp
)
315 struct whiteheat_min_set close_command
;
317 dbg(__FUNCTION__
" - port %d", port
->number
);
319 /* send a close command to the port */
320 /* firmware uses 1 based port numbering */
321 close_command
.port
= port
->number
- port
->serial
->minor
+ 1;
322 whiteheat_send_cmd (port
->serial
, WHITEHEAT_CLOSE
, (__u8
*)&close_command
, sizeof(close_command
));
324 /* Need to change the control lines here */
327 /* shutdown our bulk reads and writes */
328 usb_unlink_urb (port
->write_urb
);
329 usb_unlink_urb (port
->read_urb
);
334 static int whiteheat_ioctl (struct usb_serial_port
*port
, struct file
* file
, unsigned int cmd
, unsigned long arg
)
336 dbg(__FUNCTION__
" - port %d, cmd 0x%.4x", port
->number
, cmd
);
342 static void whiteheat_set_termios (struct usb_serial_port
*port
, struct termios
*old_termios
)
344 unsigned int cflag
= port
->tty
->termios
->c_cflag
;
345 struct whiteheat_port_settings port_settings
;
347 dbg(__FUNCTION__
" -port %d", port
->number
);
349 /* check that they really want us to change something */
351 if ((cflag
== old_termios
->c_cflag
) &&
352 (RELEVANT_IFLAG(port
->tty
->termios
->c_iflag
) == RELEVANT_IFLAG(old_termios
->c_iflag
))) {
353 dbg(__FUNCTION__
" - nothing to change...");
358 if ((!port
->tty
) || (!port
->tty
->termios
)) {
359 dbg(__FUNCTION__
" - no tty structures");
363 /* set the port number */
364 /* firmware uses 1 based port numbering */
365 port_settings
.port
= port
->number
+ 1;
367 /* get the byte size */
368 switch (cflag
& CSIZE
) {
369 case CS5
: port_settings
.bits
= 5; break;
370 case CS6
: port_settings
.bits
= 6; break;
371 case CS7
: port_settings
.bits
= 7; break;
373 case CS8
: port_settings
.bits
= 8; break;
375 dbg(__FUNCTION__
" - data bits = %d", port_settings
.bits
);
377 /* determine the parity */
380 port_settings
.parity
= 'o';
382 port_settings
.parity
= 'e';
384 port_settings
.parity
= 'n';
385 dbg(__FUNCTION__
" - parity = %c", port_settings
.parity
);
387 /* figure out the stop bits requested */
389 port_settings
.stop
= 2;
391 port_settings
.stop
= 1;
392 dbg(__FUNCTION__
" - stop bits = %d", port_settings
.stop
);
395 /* figure out the flow control settings */
397 port_settings
.hflow
= (WHITEHEAT_CTS_FLOW
| WHITEHEAT_RTS_FLOW
);
399 port_settings
.hflow
= 0;
400 dbg(__FUNCTION__
" - hardware flow control = %s %s %s %s",
401 (port_settings
.hflow
& WHITEHEAT_CTS_FLOW
) ? "CTS" : "",
402 (port_settings
.hflow
& WHITEHEAT_RTS_FLOW
) ? "RTS" : "",
403 (port_settings
.hflow
& WHITEHEAT_DSR_FLOW
) ? "DSR" : "",
404 (port_settings
.hflow
& WHITEHEAT_DTR_FLOW
) ? "DTR" : "");
406 /* determine software flow control */
407 if (I_IXOFF(port
->tty
))
408 port_settings
.sflow
= 'b';
410 port_settings
.sflow
= 'n';
411 dbg(__FUNCTION__
" - software flow control = %c", port_settings
.sflow
);
413 port_settings
.xon
= START_CHAR(port
->tty
);
414 port_settings
.xoff
= STOP_CHAR(port
->tty
);
415 dbg(__FUNCTION__
" - XON = %2x, XOFF = %2x", port_settings
.xon
, port_settings
.xoff
);
417 /* get the baud rate wanted */
418 port_settings
.baud
= tty_get_baud_rate(port
->tty
);
419 dbg(__FUNCTION__
" - baud rate = %d", port_settings
.baud
);
421 /* handle any settings that aren't specified in the tty structure */
422 port_settings
.lloop
= 0;
424 /* now send the message to the device */
425 whiteheat_send_cmd (port
->serial
, WHITEHEAT_SETUP_PORT
, (__u8
*)&port_settings
, sizeof(port_settings
));
431 static void whiteheat_throttle (struct usb_serial_port
*port
)
433 dbg(__FUNCTION__
" - port %d", port
->number
);
435 /* Change the control signals */
442 static void whiteheat_unthrottle (struct usb_serial_port
*port
)
444 dbg(__FUNCTION__
" - port %d", port
->number
);
446 /* Change the control signals */
453 /* steps to download the firmware to the WhiteHEAT device:
454 - hold the reset (by writing to the reset bit of the CPUCS register)
455 - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
456 - release the reset (by writing to the CPUCS register)
457 - download the WH.HEX file for all addresses greater than 0x1b3f using
458 VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
460 - download the WH.HEX file for all addresses less than 0x1b40 using
461 VENDOR_REQUEST_ANCHOR_LOAD
463 - device renumerated itself and comes up as new device id with all
464 firmware download completed.
466 static int whiteheat_startup (struct usb_serial
*serial
)
469 const struct whiteheat_hex_record
*record
;
473 response
= ezusb_set_reset (serial
, 1);
475 record
= &whiteheat_loader
[0];
476 while (record
->address
!= 0xffff) {
477 response
= ezusb_writememory (serial
, record
->address
,
478 (unsigned char *)record
->data
, record
->data_size
, 0xa0);
480 err(__FUNCTION__
" - ezusb_writememory failed for loader (%d %04X %p %d)",
481 response
, record
->address
, record
->data
, record
->data_size
);
487 response
= ezusb_set_reset (serial
, 0);
489 record
= &whiteheat_firmware
[0];
490 while (record
->address
< 0x1b40) {
493 while (record
->address
!= 0xffff) {
494 response
= ezusb_writememory (serial
, record
->address
,
495 (unsigned char *)record
->data
, record
->data_size
, 0xa3);
497 err(__FUNCTION__
" - ezusb_writememory failed for first firmware step (%d %04X %p %d)",
498 response
, record
->address
, record
->data
, record
->data_size
);
504 response
= ezusb_set_reset (serial
, 1);
506 record
= &whiteheat_firmware
[0];
507 while (record
->address
< 0x1b40) {
508 response
= ezusb_writememory (serial
, record
->address
,
509 (unsigned char *)record
->data
, record
->data_size
, 0xa0);
511 err(__FUNCTION__
" - ezusb_writememory failed for second firmware step (%d %04X %p %d)",
512 response
, record
->address
, record
->data
, record
->data_size
);
518 response
= ezusb_set_reset (serial
, 0);
520 /* we want this device to fail to have a driver assigned to it. */
525 static void whiteheat_shutdown (struct usb_serial
*serial
)
527 struct usb_serial_port
*command_port
;
531 /* free up our private data for our command port */
532 command_port
= &serial
->port
[COMMAND_PORT
];
533 if (command_port
->private != NULL
) {
534 kfree (command_port
->private);
535 command_port
->private = NULL
;
544 static void set_command (struct usb_serial_port
*port
, unsigned char state
, unsigned char command
)
546 struct whiteheat_rdb_set rdb_command
;
548 /* send a set rts command to the port */
549 /* firmware uses 1 based port numbering */
550 rdb_command
.port
= port
->number
- port
->serial
->minor
+ 1;
551 rdb_command
.state
= state
;
553 whiteheat_send_cmd (port
->serial
, command
, (__u8
*)&rdb_command
, sizeof(rdb_command
));
557 static inline void set_rts (struct usb_serial_port
*port
, unsigned char rts
)
559 set_command (port
, rts
, WHITEHEAT_SET_RTS
);
563 static inline void set_dtr (struct usb_serial_port
*port
, unsigned char dtr
)
565 set_command (port
, dtr
, WHITEHEAT_SET_DTR
);
569 static inline void set_break (struct usb_serial_port
*port
, unsigned char brk
)
571 set_command (port
, brk
, WHITEHEAT_SET_BREAK
);
575 static int __init
whiteheat_init (void)
577 usb_serial_register (&whiteheat_fake_device
);
578 usb_serial_register (&whiteheat_device
);
583 static void __exit
whiteheat_exit (void)
585 usb_serial_deregister (&whiteheat_fake_device
);
586 usb_serial_deregister (&whiteheat_device
);
590 module_init(whiteheat_init
);
591 module_exit(whiteheat_exit
);
593 MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
594 MODULE_DESCRIPTION("USB ConnectTech WhiteHEAT driver");