diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/serial/whiteheat.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/serial/whiteheat.c')
-rw-r--r-- | drivers/usb/serial/whiteheat.c | 1512 |
1 files changed, 1512 insertions, 0 deletions
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c new file mode 100644 index 000000000000..cf3bc30675a1 --- /dev/null +++ b/drivers/usb/serial/whiteheat.c | |||
@@ -0,0 +1,1512 @@ | |||
1 | /* | ||
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 | * | ||
15 | * See Documentation/usb/usb-serial.txt for more information on using this driver | ||
16 | * | ||
17 | * (10/09/2002) Stuart MacDonald (stuartm@connecttech.com) | ||
18 | * Upgrade to full working driver | ||
19 | * | ||
20 | * (05/30/2001) gkh | ||
21 | * switched from using spinlock to a semaphore, which fixes lots of problems. | ||
22 | * | ||
23 | * (04/08/2001) gb | ||
24 | * Identify version on module load. | ||
25 | * | ||
26 | * 2001_Mar_19 gkh | ||
27 | * Fixed MOD_INC and MOD_DEC logic, the ability to open a port more | ||
28 | * than once, and the got the proper usb_device_id table entries so | ||
29 | * the driver works again. | ||
30 | * | ||
31 | * (11/01/2000) Adam J. Richter | ||
32 | * usb_device_id table support | ||
33 | * | ||
34 | * (10/05/2000) gkh | ||
35 | * Fixed bug with urb->dev not being set properly, now that the usb | ||
36 | * core needs it. | ||
37 | * | ||
38 | * (10/03/2000) smd | ||
39 | * firmware is improved to guard against crap sent to device | ||
40 | * firmware now replies CMD_FAILURE on bad things | ||
41 | * read_callback fix you provided for private info struct | ||
42 | * command_finished now indicates success or fail | ||
43 | * setup_port struct now packed to avoid gcc padding | ||
44 | * firmware uses 1 based port numbering, driver now handles that | ||
45 | * | ||
46 | * (09/11/2000) gkh | ||
47 | * Removed DEBUG #ifdefs with call to usb_serial_debug_data | ||
48 | * | ||
49 | * (07/19/2000) gkh | ||
50 | * Added module_init and module_exit functions to handle the fact that this | ||
51 | * driver is a loadable module now. | ||
52 | * Fixed bug with port->minor that was found by Al Borchers | ||
53 | * | ||
54 | * (07/04/2000) gkh | ||
55 | * Added support for port settings. Baud rate can now be changed. Line signals | ||
56 | * are not transferred to and from the tty layer yet, but things seem to be | ||
57 | * working well now. | ||
58 | * | ||
59 | * (05/04/2000) gkh | ||
60 | * First cut at open and close commands. Data can flow through the ports at | ||
61 | * default speeds now. | ||
62 | * | ||
63 | * (03/26/2000) gkh | ||
64 | * Split driver up into device specific pieces. | ||
65 | * | ||
66 | */ | ||
67 | |||
68 | #include <linux/config.h> | ||
69 | #include <linux/kernel.h> | ||
70 | #include <linux/errno.h> | ||
71 | #include <linux/init.h> | ||
72 | #include <linux/slab.h> | ||
73 | #include <linux/tty.h> | ||
74 | #include <linux/tty_driver.h> | ||
75 | #include <linux/tty_flip.h> | ||
76 | #include <linux/module.h> | ||
77 | #include <linux/spinlock.h> | ||
78 | #include <asm/uaccess.h> | ||
79 | #include <asm/termbits.h> | ||
80 | #include <linux/usb.h> | ||
81 | #include <linux/serial_reg.h> | ||
82 | #include <linux/serial.h> | ||
83 | #include "usb-serial.h" | ||
84 | #include "whiteheat_fw.h" /* firmware for the ConnectTech WhiteHEAT device */ | ||
85 | #include "whiteheat.h" /* WhiteHEAT specific commands */ | ||
86 | |||
87 | static int debug; | ||
88 | |||
89 | #ifndef CMSPAR | ||
90 | #define CMSPAR 0 | ||
91 | #endif | ||
92 | |||
93 | /* | ||
94 | * Version Information | ||
95 | */ | ||
96 | #define DRIVER_VERSION "v2.0" | ||
97 | #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Stuart MacDonald <stuartm@connecttech.com>" | ||
98 | #define DRIVER_DESC "USB ConnectTech WhiteHEAT driver" | ||
99 | |||
100 | #define CONNECT_TECH_VENDOR_ID 0x0710 | ||
101 | #define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001 | ||
102 | #define CONNECT_TECH_WHITE_HEAT_ID 0x8001 | ||
103 | |||
104 | /* | ||
105 | ID tables for whiteheat are unusual, because we want to different | ||
106 | things for different versions of the device. Eventually, this | ||
107 | will be doable from a single table. But, for now, we define two | ||
108 | separate ID tables, and then a third table that combines them | ||
109 | just for the purpose of exporting the autoloading information. | ||
110 | */ | ||
111 | static struct usb_device_id id_table_std [] = { | ||
112 | { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) }, | ||
113 | { } /* Terminating entry */ | ||
114 | }; | ||
115 | |||
116 | static struct usb_device_id id_table_prerenumeration [] = { | ||
117 | { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) }, | ||
118 | { } /* Terminating entry */ | ||
119 | }; | ||
120 | |||
121 | static struct usb_device_id id_table_combined [] = { | ||
122 | { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_WHITE_HEAT_ID) }, | ||
123 | { USB_DEVICE(CONNECT_TECH_VENDOR_ID, CONNECT_TECH_FAKE_WHITE_HEAT_ID) }, | ||
124 | { } /* Terminating entry */ | ||
125 | }; | ||
126 | |||
127 | MODULE_DEVICE_TABLE (usb, id_table_combined); | ||
128 | |||
129 | static struct usb_driver whiteheat_driver = { | ||
130 | .owner = THIS_MODULE, | ||
131 | .name = "whiteheat", | ||
132 | .probe = usb_serial_probe, | ||
133 | .disconnect = usb_serial_disconnect, | ||
134 | .id_table = id_table_combined, | ||
135 | }; | ||
136 | |||
137 | /* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */ | ||
138 | static int whiteheat_firmware_download (struct usb_serial *serial, const struct usb_device_id *id); | ||
139 | static int whiteheat_firmware_attach (struct usb_serial *serial); | ||
140 | |||
141 | /* function prototypes for the Connect Tech WhiteHEAT serial converter */ | ||
142 | static int whiteheat_attach (struct usb_serial *serial); | ||
143 | static void whiteheat_shutdown (struct usb_serial *serial); | ||
144 | static int whiteheat_open (struct usb_serial_port *port, struct file *filp); | ||
145 | static void whiteheat_close (struct usb_serial_port *port, struct file *filp); | ||
146 | static int whiteheat_write (struct usb_serial_port *port, const unsigned char *buf, int count); | ||
147 | static int whiteheat_write_room (struct usb_serial_port *port); | ||
148 | static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); | ||
149 | static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old); | ||
150 | static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file); | ||
151 | static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); | ||
152 | static void whiteheat_break_ctl (struct usb_serial_port *port, int break_state); | ||
153 | static int whiteheat_chars_in_buffer (struct usb_serial_port *port); | ||
154 | static void whiteheat_throttle (struct usb_serial_port *port); | ||
155 | static void whiteheat_unthrottle (struct usb_serial_port *port); | ||
156 | static void whiteheat_read_callback (struct urb *urb, struct pt_regs *regs); | ||
157 | static void whiteheat_write_callback (struct urb *urb, struct pt_regs *regs); | ||
158 | |||
159 | static struct usb_serial_device_type whiteheat_fake_device = { | ||
160 | .owner = THIS_MODULE, | ||
161 | .name = "Connect Tech - WhiteHEAT - (prerenumeration)", | ||
162 | .short_name = "whiteheatnofirm", | ||
163 | .id_table = id_table_prerenumeration, | ||
164 | .num_interrupt_in = NUM_DONT_CARE, | ||
165 | .num_bulk_in = NUM_DONT_CARE, | ||
166 | .num_bulk_out = NUM_DONT_CARE, | ||
167 | .num_ports = 1, | ||
168 | .probe = whiteheat_firmware_download, | ||
169 | .attach = whiteheat_firmware_attach, | ||
170 | }; | ||
171 | |||
172 | static struct usb_serial_device_type whiteheat_device = { | ||
173 | .owner = THIS_MODULE, | ||
174 | .name = "Connect Tech - WhiteHEAT", | ||
175 | .short_name = "whiteheat", | ||
176 | .id_table = id_table_std, | ||
177 | .num_interrupt_in = NUM_DONT_CARE, | ||
178 | .num_bulk_in = NUM_DONT_CARE, | ||
179 | .num_bulk_out = NUM_DONT_CARE, | ||
180 | .num_ports = 4, | ||
181 | .attach = whiteheat_attach, | ||
182 | .shutdown = whiteheat_shutdown, | ||
183 | .open = whiteheat_open, | ||
184 | .close = whiteheat_close, | ||
185 | .write = whiteheat_write, | ||
186 | .write_room = whiteheat_write_room, | ||
187 | .ioctl = whiteheat_ioctl, | ||
188 | .set_termios = whiteheat_set_termios, | ||
189 | .break_ctl = whiteheat_break_ctl, | ||
190 | .tiocmget = whiteheat_tiocmget, | ||
191 | .tiocmset = whiteheat_tiocmset, | ||
192 | .chars_in_buffer = whiteheat_chars_in_buffer, | ||
193 | .throttle = whiteheat_throttle, | ||
194 | .unthrottle = whiteheat_unthrottle, | ||
195 | .read_bulk_callback = whiteheat_read_callback, | ||
196 | .write_bulk_callback = whiteheat_write_callback, | ||
197 | }; | ||
198 | |||
199 | |||
200 | struct whiteheat_command_private { | ||
201 | spinlock_t lock; | ||
202 | __u8 port_running; | ||
203 | __u8 command_finished; | ||
204 | wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ | ||
205 | __u8 result_buffer[64]; | ||
206 | }; | ||
207 | |||
208 | |||
209 | #define THROTTLED 0x01 | ||
210 | #define ACTUALLY_THROTTLED 0x02 | ||
211 | |||
212 | static int urb_pool_size = 8; | ||
213 | |||
214 | struct whiteheat_urb_wrap { | ||
215 | struct list_head list; | ||
216 | struct urb *urb; | ||
217 | }; | ||
218 | |||
219 | struct whiteheat_private { | ||
220 | spinlock_t lock; | ||
221 | __u8 flags; | ||
222 | __u8 mcr; | ||
223 | struct list_head rx_urbs_free; | ||
224 | struct list_head rx_urbs_submitted; | ||
225 | struct list_head rx_urb_q; | ||
226 | struct work_struct rx_work; | ||
227 | struct list_head tx_urbs_free; | ||
228 | struct list_head tx_urbs_submitted; | ||
229 | }; | ||
230 | |||
231 | |||
232 | /* local function prototypes */ | ||
233 | static int start_command_port(struct usb_serial *serial); | ||
234 | static void stop_command_port(struct usb_serial *serial); | ||
235 | static void command_port_write_callback(struct urb *urb, struct pt_regs *regs); | ||
236 | static void command_port_read_callback(struct urb *urb, struct pt_regs *regs); | ||
237 | |||
238 | static int start_port_read(struct usb_serial_port *port); | ||
239 | static struct whiteheat_urb_wrap *urb_to_wrap(struct urb *urb, struct list_head *head); | ||
240 | static struct list_head *list_first(struct list_head *head); | ||
241 | static void rx_data_softint(void *private); | ||
242 | |||
243 | static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize); | ||
244 | static int firm_open(struct usb_serial_port *port); | ||
245 | static int firm_close(struct usb_serial_port *port); | ||
246 | static int firm_setup_port(struct usb_serial_port *port); | ||
247 | static int firm_set_rts(struct usb_serial_port *port, __u8 onoff); | ||
248 | static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); | ||
249 | static int firm_set_break(struct usb_serial_port *port, __u8 onoff); | ||
250 | static int firm_purge(struct usb_serial_port *port, __u8 rxtx); | ||
251 | static int firm_get_dtr_rts(struct usb_serial_port *port); | ||
252 | static int firm_report_tx_done(struct usb_serial_port *port); | ||
253 | |||
254 | |||
255 | #define COMMAND_PORT 4 | ||
256 | #define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ | ||
257 | #define COMMAND_TIMEOUT_MS 2000 | ||
258 | #define CLOSING_DELAY (30 * HZ) | ||
259 | |||
260 | |||
261 | /***************************************************************************** | ||
262 | * Connect Tech's White Heat prerenumeration driver functions | ||
263 | *****************************************************************************/ | ||
264 | |||
265 | /* steps to download the firmware to the WhiteHEAT device: | ||
266 | - hold the reset (by writing to the reset bit of the CPUCS register) | ||
267 | - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD | ||
268 | - release the reset (by writing to the CPUCS register) | ||
269 | - download the WH.HEX file for all addresses greater than 0x1b3f using | ||
270 | VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD | ||
271 | - hold the reset | ||
272 | - download the WH.HEX file for all addresses less than 0x1b40 using | ||
273 | VENDOR_REQUEST_ANCHOR_LOAD | ||
274 | - release the reset | ||
275 | - device renumerated itself and comes up as new device id with all | ||
276 | firmware download completed. | ||
277 | */ | ||
278 | static int whiteheat_firmware_download (struct usb_serial *serial, const struct usb_device_id *id) | ||
279 | { | ||
280 | int response; | ||
281 | const struct whiteheat_hex_record *record; | ||
282 | |||
283 | dbg("%s", __FUNCTION__); | ||
284 | |||
285 | response = ezusb_set_reset (serial, 1); | ||
286 | |||
287 | record = &whiteheat_loader[0]; | ||
288 | while (record->address != 0xffff) { | ||
289 | response = ezusb_writememory (serial, record->address, | ||
290 | (unsigned char *)record->data, record->data_size, 0xa0); | ||
291 | if (response < 0) { | ||
292 | err("%s - ezusb_writememory failed for loader (%d %04X %p %d)", | ||
293 | __FUNCTION__, response, record->address, record->data, record->data_size); | ||
294 | break; | ||
295 | } | ||
296 | ++record; | ||
297 | } | ||
298 | |||
299 | response = ezusb_set_reset (serial, 0); | ||
300 | |||
301 | record = &whiteheat_firmware[0]; | ||
302 | while (record->address < 0x1b40) { | ||
303 | ++record; | ||
304 | } | ||
305 | while (record->address != 0xffff) { | ||
306 | response = ezusb_writememory (serial, record->address, | ||
307 | (unsigned char *)record->data, record->data_size, 0xa3); | ||
308 | if (response < 0) { | ||
309 | err("%s - ezusb_writememory failed for first firmware step (%d %04X %p %d)", | ||
310 | __FUNCTION__, response, record->address, record->data, record->data_size); | ||
311 | break; | ||
312 | } | ||
313 | ++record; | ||
314 | } | ||
315 | |||
316 | response = ezusb_set_reset (serial, 1); | ||
317 | |||
318 | record = &whiteheat_firmware[0]; | ||
319 | while (record->address < 0x1b40) { | ||
320 | response = ezusb_writememory (serial, record->address, | ||
321 | (unsigned char *)record->data, record->data_size, 0xa0); | ||
322 | if (response < 0) { | ||
323 | err("%s - ezusb_writememory failed for second firmware step (%d %04X %p %d)", | ||
324 | __FUNCTION__, response, record->address, record->data, record->data_size); | ||
325 | break; | ||
326 | } | ||
327 | ++record; | ||
328 | } | ||
329 | |||
330 | response = ezusb_set_reset (serial, 0); | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | |||
336 | static int whiteheat_firmware_attach (struct usb_serial *serial) | ||
337 | { | ||
338 | /* We want this device to fail to have a driver assigned to it */ | ||
339 | return 1; | ||
340 | } | ||
341 | |||
342 | |||
343 | /***************************************************************************** | ||
344 | * Connect Tech's White Heat serial driver functions | ||
345 | *****************************************************************************/ | ||
346 | static int whiteheat_attach (struct usb_serial *serial) | ||
347 | { | ||
348 | struct usb_serial_port *command_port; | ||
349 | struct whiteheat_command_private *command_info; | ||
350 | struct usb_serial_port *port; | ||
351 | struct whiteheat_private *info; | ||
352 | struct whiteheat_hw_info *hw_info; | ||
353 | int pipe; | ||
354 | int ret; | ||
355 | int alen; | ||
356 | __u8 *command; | ||
357 | __u8 *result; | ||
358 | int i; | ||
359 | int j; | ||
360 | struct urb *urb; | ||
361 | int buf_size; | ||
362 | struct whiteheat_urb_wrap *wrap; | ||
363 | struct list_head *tmp; | ||
364 | |||
365 | command_port = serial->port[COMMAND_PORT]; | ||
366 | |||
367 | pipe = usb_sndbulkpipe (serial->dev, command_port->bulk_out_endpointAddress); | ||
368 | command = kmalloc(2, GFP_KERNEL); | ||
369 | if (!command) | ||
370 | goto no_command_buffer; | ||
371 | command[0] = WHITEHEAT_GET_HW_INFO; | ||
372 | command[1] = 0; | ||
373 | |||
374 | result = kmalloc(sizeof(*hw_info) + 1, GFP_KERNEL); | ||
375 | if (!result) | ||
376 | goto no_result_buffer; | ||
377 | /* | ||
378 | * When the module is reloaded the firmware is still there and | ||
379 | * the endpoints are still in the usb core unchanged. This is the | ||
380 | * unlinking bug in disguise. Same for the call below. | ||
381 | */ | ||
382 | usb_clear_halt(serial->dev, pipe); | ||
383 | ret = usb_bulk_msg (serial->dev, pipe, command, 2, &alen, COMMAND_TIMEOUT_MS); | ||
384 | if (ret) { | ||
385 | err("%s: Couldn't send command [%d]", serial->type->name, ret); | ||
386 | goto no_firmware; | ||
387 | } else if (alen != sizeof(command)) { | ||
388 | err("%s: Send command incomplete [%d]", serial->type->name, alen); | ||
389 | goto no_firmware; | ||
390 | } | ||
391 | |||
392 | pipe = usb_rcvbulkpipe (serial->dev, command_port->bulk_in_endpointAddress); | ||
393 | /* See the comment on the usb_clear_halt() above */ | ||
394 | usb_clear_halt(serial->dev, pipe); | ||
395 | ret = usb_bulk_msg (serial->dev, pipe, result, sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS); | ||
396 | if (ret) { | ||
397 | err("%s: Couldn't get results [%d]", serial->type->name, ret); | ||
398 | goto no_firmware; | ||
399 | } else if (alen != sizeof(result)) { | ||
400 | err("%s: Get results incomplete [%d]", serial->type->name, alen); | ||
401 | goto no_firmware; | ||
402 | } else if (result[0] != command[0]) { | ||
403 | err("%s: Command failed [%d]", serial->type->name, result[0]); | ||
404 | goto no_firmware; | ||
405 | } | ||
406 | |||
407 | hw_info = (struct whiteheat_hw_info *)&result[1]; | ||
408 | |||
409 | info("%s: Driver %s: Firmware v%d.%02d", serial->type->name, | ||
410 | DRIVER_VERSION, hw_info->sw_major_rev, hw_info->sw_minor_rev); | ||
411 | |||
412 | for (i = 0; i < serial->num_ports; i++) { | ||
413 | port = serial->port[i]; | ||
414 | |||
415 | info = (struct whiteheat_private *)kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL); | ||
416 | if (info == NULL) { | ||
417 | err("%s: Out of memory for port structures\n", serial->type->name); | ||
418 | goto no_private; | ||
419 | } | ||
420 | |||
421 | spin_lock_init(&info->lock); | ||
422 | info->flags = 0; | ||
423 | info->mcr = 0; | ||
424 | INIT_WORK(&info->rx_work, rx_data_softint, port); | ||
425 | |||
426 | INIT_LIST_HEAD(&info->rx_urbs_free); | ||
427 | INIT_LIST_HEAD(&info->rx_urbs_submitted); | ||
428 | INIT_LIST_HEAD(&info->rx_urb_q); | ||
429 | INIT_LIST_HEAD(&info->tx_urbs_free); | ||
430 | INIT_LIST_HEAD(&info->tx_urbs_submitted); | ||
431 | |||
432 | for (j = 0; j < urb_pool_size; j++) { | ||
433 | urb = usb_alloc_urb(0, GFP_KERNEL); | ||
434 | if (!urb) { | ||
435 | err("No free urbs available"); | ||
436 | goto no_rx_urb; | ||
437 | } | ||
438 | buf_size = port->read_urb->transfer_buffer_length; | ||
439 | urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL); | ||
440 | if (!urb->transfer_buffer) { | ||
441 | err("Couldn't allocate urb buffer"); | ||
442 | goto no_rx_buf; | ||
443 | } | ||
444 | wrap = kmalloc(sizeof(*wrap), GFP_KERNEL); | ||
445 | if (!wrap) { | ||
446 | err("Couldn't allocate urb wrapper"); | ||
447 | goto no_rx_wrap; | ||
448 | } | ||
449 | usb_fill_bulk_urb(urb, serial->dev, | ||
450 | usb_rcvbulkpipe(serial->dev, | ||
451 | port->bulk_in_endpointAddress), | ||
452 | urb->transfer_buffer, buf_size, | ||
453 | whiteheat_read_callback, port); | ||
454 | wrap->urb = urb; | ||
455 | list_add(&wrap->list, &info->rx_urbs_free); | ||
456 | |||
457 | urb = usb_alloc_urb(0, GFP_KERNEL); | ||
458 | if (!urb) { | ||
459 | err("No free urbs available"); | ||
460 | goto no_tx_urb; | ||
461 | } | ||
462 | buf_size = port->write_urb->transfer_buffer_length; | ||
463 | urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL); | ||
464 | if (!urb->transfer_buffer) { | ||
465 | err("Couldn't allocate urb buffer"); | ||
466 | goto no_tx_buf; | ||
467 | } | ||
468 | wrap = kmalloc(sizeof(*wrap), GFP_KERNEL); | ||
469 | if (!wrap) { | ||
470 | err("Couldn't allocate urb wrapper"); | ||
471 | goto no_tx_wrap; | ||
472 | } | ||
473 | usb_fill_bulk_urb(urb, serial->dev, | ||
474 | usb_sndbulkpipe(serial->dev, | ||
475 | port->bulk_out_endpointAddress), | ||
476 | urb->transfer_buffer, buf_size, | ||
477 | whiteheat_write_callback, port); | ||
478 | wrap->urb = urb; | ||
479 | list_add(&wrap->list, &info->tx_urbs_free); | ||
480 | } | ||
481 | |||
482 | usb_set_serial_port_data(port, info); | ||
483 | } | ||
484 | |||
485 | command_info = (struct whiteheat_command_private *)kmalloc(sizeof(struct whiteheat_command_private), GFP_KERNEL); | ||
486 | if (command_info == NULL) { | ||
487 | err("%s: Out of memory for port structures\n", serial->type->name); | ||
488 | goto no_command_private; | ||
489 | } | ||
490 | |||
491 | spin_lock_init(&command_info->lock); | ||
492 | command_info->port_running = 0; | ||
493 | init_waitqueue_head(&command_info->wait_command); | ||
494 | usb_set_serial_port_data(command_port, command_info); | ||
495 | command_port->write_urb->complete = command_port_write_callback; | ||
496 | command_port->read_urb->complete = command_port_read_callback; | ||
497 | kfree(result); | ||
498 | kfree(command); | ||
499 | |||
500 | return 0; | ||
501 | |||
502 | no_firmware: | ||
503 | /* Firmware likely not running */ | ||
504 | err("%s: Unable to retrieve firmware version, try replugging\n", serial->type->name); | ||
505 | err("%s: If the firmware is not running (status led not blinking)\n", serial->type->name); | ||
506 | err("%s: please contact support@connecttech.com\n", serial->type->name); | ||
507 | return -ENODEV; | ||
508 | |||
509 | no_command_private: | ||
510 | for (i = serial->num_ports - 1; i >= 0; i--) { | ||
511 | port = serial->port[i]; | ||
512 | info = usb_get_serial_port_data(port); | ||
513 | for (j = urb_pool_size - 1; j >= 0; j--) { | ||
514 | tmp = list_first(&info->tx_urbs_free); | ||
515 | list_del(tmp); | ||
516 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
517 | urb = wrap->urb; | ||
518 | kfree(wrap); | ||
519 | no_tx_wrap: | ||
520 | kfree(urb->transfer_buffer); | ||
521 | no_tx_buf: | ||
522 | usb_free_urb(urb); | ||
523 | no_tx_urb: | ||
524 | tmp = list_first(&info->rx_urbs_free); | ||
525 | list_del(tmp); | ||
526 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
527 | urb = wrap->urb; | ||
528 | kfree(wrap); | ||
529 | no_rx_wrap: | ||
530 | kfree(urb->transfer_buffer); | ||
531 | no_rx_buf: | ||
532 | usb_free_urb(urb); | ||
533 | no_rx_urb: | ||
534 | ; | ||
535 | } | ||
536 | kfree(info); | ||
537 | no_private: | ||
538 | ; | ||
539 | } | ||
540 | kfree(result); | ||
541 | no_result_buffer: | ||
542 | kfree(command); | ||
543 | no_command_buffer: | ||
544 | return -ENOMEM; | ||
545 | } | ||
546 | |||
547 | |||
548 | static void whiteheat_shutdown (struct usb_serial *serial) | ||
549 | { | ||
550 | struct usb_serial_port *command_port; | ||
551 | struct usb_serial_port *port; | ||
552 | struct whiteheat_private *info; | ||
553 | struct whiteheat_urb_wrap *wrap; | ||
554 | struct urb *urb; | ||
555 | struct list_head *tmp; | ||
556 | struct list_head *tmp2; | ||
557 | int i; | ||
558 | |||
559 | dbg("%s", __FUNCTION__); | ||
560 | |||
561 | /* free up our private data for our command port */ | ||
562 | command_port = serial->port[COMMAND_PORT]; | ||
563 | kfree (usb_get_serial_port_data(command_port)); | ||
564 | |||
565 | for (i = 0; i < serial->num_ports; i++) { | ||
566 | port = serial->port[i]; | ||
567 | info = usb_get_serial_port_data(port); | ||
568 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) { | ||
569 | list_del(tmp); | ||
570 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
571 | urb = wrap->urb; | ||
572 | kfree(wrap); | ||
573 | kfree(urb->transfer_buffer); | ||
574 | usb_free_urb(urb); | ||
575 | } | ||
576 | list_for_each_safe(tmp, tmp2, &info->tx_urbs_free) { | ||
577 | list_del(tmp); | ||
578 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
579 | urb = wrap->urb; | ||
580 | kfree(wrap); | ||
581 | kfree(urb->transfer_buffer); | ||
582 | usb_free_urb(urb); | ||
583 | } | ||
584 | kfree(info); | ||
585 | } | ||
586 | |||
587 | return; | ||
588 | } | ||
589 | |||
590 | |||
591 | static int whiteheat_open (struct usb_serial_port *port, struct file *filp) | ||
592 | { | ||
593 | int retval = 0; | ||
594 | struct termios old_term; | ||
595 | |||
596 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
597 | |||
598 | retval = start_command_port(port->serial); | ||
599 | if (retval) | ||
600 | goto exit; | ||
601 | |||
602 | if (port->tty) | ||
603 | port->tty->low_latency = 1; | ||
604 | |||
605 | /* send an open port command */ | ||
606 | retval = firm_open(port); | ||
607 | if (retval) { | ||
608 | stop_command_port(port->serial); | ||
609 | goto exit; | ||
610 | } | ||
611 | |||
612 | retval = firm_purge(port, WHITEHEAT_PURGE_RX | WHITEHEAT_PURGE_TX); | ||
613 | if (retval) { | ||
614 | firm_close(port); | ||
615 | stop_command_port(port->serial); | ||
616 | goto exit; | ||
617 | } | ||
618 | |||
619 | old_term.c_cflag = ~port->tty->termios->c_cflag; | ||
620 | old_term.c_iflag = ~port->tty->termios->c_iflag; | ||
621 | whiteheat_set_termios(port, &old_term); | ||
622 | |||
623 | /* Work around HCD bugs */ | ||
624 | usb_clear_halt(port->serial->dev, port->read_urb->pipe); | ||
625 | usb_clear_halt(port->serial->dev, port->write_urb->pipe); | ||
626 | |||
627 | /* Start reading from the device */ | ||
628 | retval = start_port_read(port); | ||
629 | if (retval) { | ||
630 | err("%s - failed submitting read urb, error %d", __FUNCTION__, retval); | ||
631 | firm_close(port); | ||
632 | stop_command_port(port->serial); | ||
633 | goto exit; | ||
634 | } | ||
635 | |||
636 | exit: | ||
637 | dbg("%s - exit, retval = %d", __FUNCTION__, retval); | ||
638 | return retval; | ||
639 | } | ||
640 | |||
641 | |||
642 | static void whiteheat_close(struct usb_serial_port *port, struct file * filp) | ||
643 | { | ||
644 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
645 | struct whiteheat_urb_wrap *wrap; | ||
646 | struct urb *urb; | ||
647 | struct list_head *tmp; | ||
648 | struct list_head *tmp2; | ||
649 | unsigned long flags; | ||
650 | |||
651 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
652 | |||
653 | /* filp is NULL when called from usb_serial_disconnect */ | ||
654 | if (filp && (tty_hung_up_p(filp))) { | ||
655 | return; | ||
656 | } | ||
657 | |||
658 | port->tty->closing = 1; | ||
659 | |||
660 | /* | ||
661 | * Not currently in use; tty_wait_until_sent() calls | ||
662 | * serial_chars_in_buffer() which deadlocks on the second semaphore | ||
663 | * acquisition. This should be fixed at some point. Greg's been | ||
664 | * notified. | ||
665 | if ((filp->f_flags & (O_NDELAY | O_NONBLOCK)) == 0) { | ||
666 | tty_wait_until_sent(port->tty, CLOSING_DELAY); | ||
667 | } | ||
668 | */ | ||
669 | |||
670 | if (port->tty->driver->flush_buffer) | ||
671 | port->tty->driver->flush_buffer(port->tty); | ||
672 | tty_ldisc_flush(port->tty); | ||
673 | |||
674 | firm_report_tx_done(port); | ||
675 | |||
676 | firm_close(port); | ||
677 | |||
678 | /* shutdown our bulk reads and writes */ | ||
679 | spin_lock_irqsave(&info->lock, flags); | ||
680 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { | ||
681 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
682 | urb = wrap->urb; | ||
683 | usb_kill_urb(urb); | ||
684 | list_del(tmp); | ||
685 | list_add(tmp, &info->rx_urbs_free); | ||
686 | } | ||
687 | list_for_each_safe(tmp, tmp2, &info->rx_urb_q) { | ||
688 | list_del(tmp); | ||
689 | list_add(tmp, &info->rx_urbs_free); | ||
690 | } | ||
691 | list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) { | ||
692 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
693 | urb = wrap->urb; | ||
694 | usb_kill_urb(urb); | ||
695 | list_del(tmp); | ||
696 | list_add(tmp, &info->tx_urbs_free); | ||
697 | } | ||
698 | spin_unlock_irqrestore(&info->lock, flags); | ||
699 | |||
700 | stop_command_port(port->serial); | ||
701 | |||
702 | port->tty->closing = 0; | ||
703 | } | ||
704 | |||
705 | |||
706 | static int whiteheat_write(struct usb_serial_port *port, const unsigned char *buf, int count) | ||
707 | { | ||
708 | struct usb_serial *serial = port->serial; | ||
709 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
710 | struct whiteheat_urb_wrap *wrap; | ||
711 | struct urb *urb; | ||
712 | int result; | ||
713 | int bytes; | ||
714 | int sent = 0; | ||
715 | unsigned long flags; | ||
716 | struct list_head *tmp; | ||
717 | |||
718 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
719 | |||
720 | if (count == 0) { | ||
721 | dbg("%s - write request of 0 bytes", __FUNCTION__); | ||
722 | return (0); | ||
723 | } | ||
724 | |||
725 | while (count) { | ||
726 | spin_lock_irqsave(&info->lock, flags); | ||
727 | if (list_empty(&info->tx_urbs_free)) { | ||
728 | spin_unlock_irqrestore(&info->lock, flags); | ||
729 | break; | ||
730 | } | ||
731 | tmp = list_first(&info->tx_urbs_free); | ||
732 | list_del(tmp); | ||
733 | spin_unlock_irqrestore(&info->lock, flags); | ||
734 | |||
735 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
736 | urb = wrap->urb; | ||
737 | bytes = (count > port->bulk_out_size) ? port->bulk_out_size : count; | ||
738 | memcpy (urb->transfer_buffer, buf + sent, bytes); | ||
739 | |||
740 | usb_serial_debug_data(debug, &port->dev, __FUNCTION__, bytes, urb->transfer_buffer); | ||
741 | |||
742 | urb->dev = serial->dev; | ||
743 | urb->transfer_buffer_length = bytes; | ||
744 | result = usb_submit_urb(urb, GFP_ATOMIC); | ||
745 | if (result) { | ||
746 | err("%s - failed submitting write urb, error %d", __FUNCTION__, result); | ||
747 | sent = result; | ||
748 | spin_lock_irqsave(&info->lock, flags); | ||
749 | list_add(tmp, &info->tx_urbs_free); | ||
750 | spin_unlock_irqrestore(&info->lock, flags); | ||
751 | break; | ||
752 | } else { | ||
753 | sent += bytes; | ||
754 | count -= bytes; | ||
755 | spin_lock_irqsave(&info->lock, flags); | ||
756 | list_add(tmp, &info->tx_urbs_submitted); | ||
757 | spin_unlock_irqrestore(&info->lock, flags); | ||
758 | } | ||
759 | } | ||
760 | |||
761 | return sent; | ||
762 | } | ||
763 | |||
764 | |||
765 | static int whiteheat_write_room(struct usb_serial_port *port) | ||
766 | { | ||
767 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
768 | struct list_head *tmp; | ||
769 | int room = 0; | ||
770 | unsigned long flags; | ||
771 | |||
772 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
773 | |||
774 | spin_lock_irqsave(&info->lock, flags); | ||
775 | list_for_each(tmp, &info->tx_urbs_free) | ||
776 | room++; | ||
777 | spin_unlock_irqrestore(&info->lock, flags); | ||
778 | room *= port->bulk_out_size; | ||
779 | |||
780 | dbg("%s - returns %d", __FUNCTION__, room); | ||
781 | return (room); | ||
782 | } | ||
783 | |||
784 | |||
785 | static int whiteheat_tiocmget (struct usb_serial_port *port, struct file *file) | ||
786 | { | ||
787 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
788 | unsigned int modem_signals = 0; | ||
789 | |||
790 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
791 | |||
792 | firm_get_dtr_rts(port); | ||
793 | if (info->mcr & UART_MCR_DTR) | ||
794 | modem_signals |= TIOCM_DTR; | ||
795 | if (info->mcr & UART_MCR_RTS) | ||
796 | modem_signals |= TIOCM_RTS; | ||
797 | |||
798 | return modem_signals; | ||
799 | } | ||
800 | |||
801 | |||
802 | static int whiteheat_tiocmset (struct usb_serial_port *port, struct file *file, | ||
803 | unsigned int set, unsigned int clear) | ||
804 | { | ||
805 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
806 | |||
807 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
808 | |||
809 | if (set & TIOCM_RTS) | ||
810 | info->mcr |= UART_MCR_RTS; | ||
811 | if (set & TIOCM_DTR) | ||
812 | info->mcr |= UART_MCR_DTR; | ||
813 | |||
814 | if (clear & TIOCM_RTS) | ||
815 | info->mcr &= ~UART_MCR_RTS; | ||
816 | if (clear & TIOCM_DTR) | ||
817 | info->mcr &= ~UART_MCR_DTR; | ||
818 | |||
819 | firm_set_dtr(port, info->mcr & UART_MCR_DTR); | ||
820 | firm_set_rts(port, info->mcr & UART_MCR_RTS); | ||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | |||
825 | static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) | ||
826 | { | ||
827 | struct serial_struct serstruct; | ||
828 | void __user *user_arg = (void __user *)arg; | ||
829 | |||
830 | dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd); | ||
831 | |||
832 | switch (cmd) { | ||
833 | 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; | ||
844 | |||
845 | if (copy_to_user(user_arg, &serstruct, sizeof(serstruct))) | ||
846 | return -EFAULT; | ||
847 | |||
848 | break; | ||
849 | |||
850 | case TIOCSSERIAL: | ||
851 | if (copy_from_user(&serstruct, user_arg, sizeof(serstruct))) | ||
852 | return -EFAULT; | ||
853 | |||
854 | /* | ||
855 | * For now this is ignored. dip sets the ASYNC_[V]HI flags | ||
856 | * but this isn't used by us at all. Maybe someone somewhere | ||
857 | * will need the custom_divisor setting. | ||
858 | */ | ||
859 | |||
860 | break; | ||
861 | |||
862 | default: | ||
863 | break; | ||
864 | } | ||
865 | |||
866 | return -ENOIOCTLCMD; | ||
867 | } | ||
868 | |||
869 | |||
870 | static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios) | ||
871 | { | ||
872 | dbg("%s -port %d", __FUNCTION__, port->number); | ||
873 | |||
874 | if ((!port->tty) || (!port->tty->termios)) { | ||
875 | dbg("%s - no tty structures", __FUNCTION__); | ||
876 | goto exit; | ||
877 | } | ||
878 | |||
879 | /* check that they really want us to change something */ | ||
880 | if (old_termios) { | ||
881 | if ((port->tty->termios->c_cflag == old_termios->c_cflag) && | ||
882 | (port->tty->termios->c_iflag == old_termios->c_iflag)) { | ||
883 | dbg("%s - nothing to change...", __FUNCTION__); | ||
884 | goto exit; | ||
885 | } | ||
886 | } | ||
887 | |||
888 | firm_setup_port(port); | ||
889 | |||
890 | exit: | ||
891 | return; | ||
892 | } | ||
893 | |||
894 | |||
895 | static void whiteheat_break_ctl(struct usb_serial_port *port, int break_state) { | ||
896 | firm_set_break(port, break_state); | ||
897 | } | ||
898 | |||
899 | |||
900 | static int whiteheat_chars_in_buffer(struct usb_serial_port *port) | ||
901 | { | ||
902 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
903 | struct list_head *tmp; | ||
904 | struct whiteheat_urb_wrap *wrap; | ||
905 | int chars = 0; | ||
906 | unsigned long flags; | ||
907 | |||
908 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
909 | |||
910 | spin_lock_irqsave(&info->lock, flags); | ||
911 | list_for_each(tmp, &info->tx_urbs_submitted) { | ||
912 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
913 | chars += wrap->urb->transfer_buffer_length; | ||
914 | } | ||
915 | spin_unlock_irqrestore(&info->lock, flags); | ||
916 | |||
917 | dbg ("%s - returns %d", __FUNCTION__, chars); | ||
918 | return (chars); | ||
919 | } | ||
920 | |||
921 | |||
922 | static void whiteheat_throttle (struct usb_serial_port *port) | ||
923 | { | ||
924 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
925 | unsigned long flags; | ||
926 | |||
927 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
928 | |||
929 | spin_lock_irqsave(&info->lock, flags); | ||
930 | info->flags |= THROTTLED; | ||
931 | spin_unlock_irqrestore(&info->lock, flags); | ||
932 | |||
933 | return; | ||
934 | } | ||
935 | |||
936 | |||
937 | static void whiteheat_unthrottle (struct usb_serial_port *port) | ||
938 | { | ||
939 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
940 | int actually_throttled; | ||
941 | unsigned long flags; | ||
942 | |||
943 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
944 | |||
945 | spin_lock_irqsave(&info->lock, flags); | ||
946 | actually_throttled = info->flags & ACTUALLY_THROTTLED; | ||
947 | info->flags &= ~(THROTTLED | ACTUALLY_THROTTLED); | ||
948 | spin_unlock_irqrestore(&info->lock, flags); | ||
949 | |||
950 | if (actually_throttled) | ||
951 | rx_data_softint(port); | ||
952 | |||
953 | return; | ||
954 | } | ||
955 | |||
956 | |||
957 | /***************************************************************************** | ||
958 | * Connect Tech's White Heat callback routines | ||
959 | *****************************************************************************/ | ||
960 | static void command_port_write_callback (struct urb *urb, struct pt_regs *regs) | ||
961 | { | ||
962 | dbg("%s", __FUNCTION__); | ||
963 | |||
964 | if (urb->status) { | ||
965 | dbg ("nonzero urb status: %d", urb->status); | ||
966 | return; | ||
967 | } | ||
968 | } | ||
969 | |||
970 | |||
971 | static void command_port_read_callback (struct urb *urb, struct pt_regs *regs) | ||
972 | { | ||
973 | struct usb_serial_port *command_port = (struct usb_serial_port *)urb->context; | ||
974 | struct whiteheat_command_private *command_info; | ||
975 | unsigned char *data = urb->transfer_buffer; | ||
976 | int result; | ||
977 | unsigned long flags; | ||
978 | |||
979 | dbg("%s", __FUNCTION__); | ||
980 | |||
981 | if (urb->status) { | ||
982 | dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status); | ||
983 | return; | ||
984 | } | ||
985 | |||
986 | usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data); | ||
987 | |||
988 | command_info = usb_get_serial_port_data(command_port); | ||
989 | if (!command_info) { | ||
990 | dbg ("%s - command_info is NULL, exiting.", __FUNCTION__); | ||
991 | return; | ||
992 | } | ||
993 | spin_lock_irqsave(&command_info->lock, flags); | ||
994 | |||
995 | if (data[0] == WHITEHEAT_CMD_COMPLETE) { | ||
996 | command_info->command_finished = WHITEHEAT_CMD_COMPLETE; | ||
997 | wake_up_interruptible(&command_info->wait_command); | ||
998 | } else if (data[0] == WHITEHEAT_CMD_FAILURE) { | ||
999 | command_info->command_finished = WHITEHEAT_CMD_FAILURE; | ||
1000 | wake_up_interruptible(&command_info->wait_command); | ||
1001 | } else if (data[0] == WHITEHEAT_EVENT) { | ||
1002 | /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ | ||
1003 | dbg("%s - event received", __FUNCTION__); | ||
1004 | } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { | ||
1005 | memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); | ||
1006 | command_info->command_finished = WHITEHEAT_CMD_COMPLETE; | ||
1007 | wake_up_interruptible(&command_info->wait_command); | ||
1008 | } else { | ||
1009 | dbg("%s - bad reply from firmware", __FUNCTION__); | ||
1010 | } | ||
1011 | |||
1012 | /* Continue trying to always read */ | ||
1013 | command_port->read_urb->dev = command_port->serial->dev; | ||
1014 | result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC); | ||
1015 | spin_unlock_irqrestore(&command_info->lock, flags); | ||
1016 | if (result) | ||
1017 | dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); | ||
1018 | } | ||
1019 | |||
1020 | |||
1021 | static void whiteheat_read_callback(struct urb *urb, struct pt_regs *regs) | ||
1022 | { | ||
1023 | struct usb_serial_port *port = (struct usb_serial_port *)urb->context; | ||
1024 | struct whiteheat_urb_wrap *wrap; | ||
1025 | unsigned char *data = urb->transfer_buffer; | ||
1026 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
1027 | |||
1028 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
1029 | |||
1030 | spin_lock(&info->lock); | ||
1031 | wrap = urb_to_wrap(urb, &info->rx_urbs_submitted); | ||
1032 | if (!wrap) { | ||
1033 | spin_unlock(&info->lock); | ||
1034 | err("%s - Not my urb!", __FUNCTION__); | ||
1035 | return; | ||
1036 | } | ||
1037 | list_del(&wrap->list); | ||
1038 | spin_unlock(&info->lock); | ||
1039 | |||
1040 | if (urb->status) { | ||
1041 | dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); | ||
1042 | spin_lock(&info->lock); | ||
1043 | list_add(&wrap->list, &info->rx_urbs_free); | ||
1044 | spin_unlock(&info->lock); | ||
1045 | return; | ||
1046 | } | ||
1047 | |||
1048 | usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); | ||
1049 | |||
1050 | spin_lock(&info->lock); | ||
1051 | list_add_tail(&wrap->list, &info->rx_urb_q); | ||
1052 | if (info->flags & THROTTLED) { | ||
1053 | info->flags |= ACTUALLY_THROTTLED; | ||
1054 | spin_unlock(&info->lock); | ||
1055 | return; | ||
1056 | } | ||
1057 | spin_unlock(&info->lock); | ||
1058 | |||
1059 | schedule_work(&info->rx_work); | ||
1060 | } | ||
1061 | |||
1062 | |||
1063 | static void whiteheat_write_callback(struct urb *urb, struct pt_regs *regs) | ||
1064 | { | ||
1065 | struct usb_serial_port *port = (struct usb_serial_port *)urb->context; | ||
1066 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
1067 | struct whiteheat_urb_wrap *wrap; | ||
1068 | |||
1069 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
1070 | |||
1071 | spin_lock(&info->lock); | ||
1072 | wrap = urb_to_wrap(urb, &info->tx_urbs_submitted); | ||
1073 | if (!wrap) { | ||
1074 | spin_unlock(&info->lock); | ||
1075 | err("%s - Not my urb!", __FUNCTION__); | ||
1076 | return; | ||
1077 | } | ||
1078 | list_del(&wrap->list); | ||
1079 | list_add(&wrap->list, &info->tx_urbs_free); | ||
1080 | spin_unlock(&info->lock); | ||
1081 | |||
1082 | if (urb->status) { | ||
1083 | dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); | ||
1084 | return; | ||
1085 | } | ||
1086 | |||
1087 | usb_serial_port_softint((void *)port); | ||
1088 | |||
1089 | schedule_work(&port->work); | ||
1090 | } | ||
1091 | |||
1092 | |||
1093 | /***************************************************************************** | ||
1094 | * Connect Tech's White Heat firmware interface | ||
1095 | *****************************************************************************/ | ||
1096 | static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize) | ||
1097 | { | ||
1098 | struct usb_serial_port *command_port; | ||
1099 | struct whiteheat_command_private *command_info; | ||
1100 | struct whiteheat_private *info; | ||
1101 | __u8 *transfer_buffer; | ||
1102 | int retval = 0; | ||
1103 | unsigned long flags; | ||
1104 | |||
1105 | dbg("%s - command %d", __FUNCTION__, command); | ||
1106 | |||
1107 | command_port = port->serial->port[COMMAND_PORT]; | ||
1108 | command_info = usb_get_serial_port_data(command_port); | ||
1109 | spin_lock_irqsave(&command_info->lock, flags); | ||
1110 | command_info->command_finished = FALSE; | ||
1111 | |||
1112 | transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; | ||
1113 | transfer_buffer[0] = command; | ||
1114 | memcpy (&transfer_buffer[1], data, datasize); | ||
1115 | command_port->write_urb->transfer_buffer_length = datasize + 1; | ||
1116 | command_port->write_urb->dev = port->serial->dev; | ||
1117 | retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL); | ||
1118 | if (retval) { | ||
1119 | dbg("%s - submit urb failed", __FUNCTION__); | ||
1120 | goto exit; | ||
1121 | } | ||
1122 | spin_unlock_irqrestore(&command_info->lock, flags); | ||
1123 | |||
1124 | /* wait for the command to complete */ | ||
1125 | wait_event_interruptible_timeout(command_info->wait_command, | ||
1126 | (command_info->command_finished != FALSE), COMMAND_TIMEOUT); | ||
1127 | |||
1128 | spin_lock_irqsave(&command_info->lock, flags); | ||
1129 | |||
1130 | if (command_info->command_finished == FALSE) { | ||
1131 | dbg("%s - command timed out.", __FUNCTION__); | ||
1132 | retval = -ETIMEDOUT; | ||
1133 | goto exit; | ||
1134 | } | ||
1135 | |||
1136 | if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) { | ||
1137 | dbg("%s - command failed.", __FUNCTION__); | ||
1138 | retval = -EIO; | ||
1139 | goto exit; | ||
1140 | } | ||
1141 | |||
1142 | if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) { | ||
1143 | dbg("%s - command completed.", __FUNCTION__); | ||
1144 | switch (command) { | ||
1145 | case WHITEHEAT_GET_DTR_RTS: | ||
1146 | info = usb_get_serial_port_data(port); | ||
1147 | memcpy(&info->mcr, command_info->result_buffer, sizeof(struct whiteheat_dr_info)); | ||
1148 | break; | ||
1149 | } | ||
1150 | } | ||
1151 | |||
1152 | exit: | ||
1153 | spin_unlock_irqrestore(&command_info->lock, flags); | ||
1154 | return retval; | ||
1155 | } | ||
1156 | |||
1157 | |||
1158 | static int firm_open(struct usb_serial_port *port) { | ||
1159 | struct whiteheat_simple open_command; | ||
1160 | |||
1161 | open_command.port = port->number - port->serial->minor + 1; | ||
1162 | return firm_send_command(port, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); | ||
1163 | } | ||
1164 | |||
1165 | |||
1166 | static int firm_close(struct usb_serial_port *port) { | ||
1167 | struct whiteheat_simple close_command; | ||
1168 | |||
1169 | close_command.port = port->number - port->serial->minor + 1; | ||
1170 | return firm_send_command(port, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); | ||
1171 | } | ||
1172 | |||
1173 | |||
1174 | static int firm_setup_port(struct usb_serial_port *port) { | ||
1175 | struct whiteheat_port_settings port_settings; | ||
1176 | unsigned int cflag = port->tty->termios->c_cflag; | ||
1177 | |||
1178 | port_settings.port = port->number + 1; | ||
1179 | |||
1180 | /* get the byte size */ | ||
1181 | switch (cflag & CSIZE) { | ||
1182 | case CS5: port_settings.bits = 5; break; | ||
1183 | case CS6: port_settings.bits = 6; break; | ||
1184 | case CS7: port_settings.bits = 7; break; | ||
1185 | default: | ||
1186 | case CS8: port_settings.bits = 8; break; | ||
1187 | } | ||
1188 | dbg("%s - data bits = %d", __FUNCTION__, port_settings.bits); | ||
1189 | |||
1190 | /* determine the parity */ | ||
1191 | if (cflag & PARENB) | ||
1192 | if (cflag & CMSPAR) | ||
1193 | if (cflag & PARODD) | ||
1194 | port_settings.parity = WHITEHEAT_PAR_MARK; | ||
1195 | else | ||
1196 | port_settings.parity = WHITEHEAT_PAR_SPACE; | ||
1197 | else | ||
1198 | if (cflag & PARODD) | ||
1199 | port_settings.parity = WHITEHEAT_PAR_ODD; | ||
1200 | else | ||
1201 | port_settings.parity = WHITEHEAT_PAR_EVEN; | ||
1202 | else | ||
1203 | port_settings.parity = WHITEHEAT_PAR_NONE; | ||
1204 | dbg("%s - parity = %c", __FUNCTION__, port_settings.parity); | ||
1205 | |||
1206 | /* figure out the stop bits requested */ | ||
1207 | if (cflag & CSTOPB) | ||
1208 | port_settings.stop = 2; | ||
1209 | else | ||
1210 | port_settings.stop = 1; | ||
1211 | dbg("%s - stop bits = %d", __FUNCTION__, port_settings.stop); | ||
1212 | |||
1213 | /* figure out the flow control settings */ | ||
1214 | if (cflag & CRTSCTS) | ||
1215 | port_settings.hflow = (WHITEHEAT_HFLOW_CTS | WHITEHEAT_HFLOW_RTS); | ||
1216 | else | ||
1217 | port_settings.hflow = WHITEHEAT_HFLOW_NONE; | ||
1218 | dbg("%s - hardware flow control = %s %s %s %s", __FUNCTION__, | ||
1219 | (port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "", | ||
1220 | (port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "", | ||
1221 | (port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "", | ||
1222 | (port_settings.hflow & WHITEHEAT_HFLOW_DTR) ? "DTR" : ""); | ||
1223 | |||
1224 | /* determine software flow control */ | ||
1225 | if (I_IXOFF(port->tty)) | ||
1226 | port_settings.sflow = WHITEHEAT_SFLOW_RXTX; | ||
1227 | else | ||
1228 | port_settings.sflow = WHITEHEAT_SFLOW_NONE; | ||
1229 | dbg("%s - software flow control = %c", __FUNCTION__, port_settings.sflow); | ||
1230 | |||
1231 | port_settings.xon = START_CHAR(port->tty); | ||
1232 | port_settings.xoff = STOP_CHAR(port->tty); | ||
1233 | dbg("%s - XON = %2x, XOFF = %2x", __FUNCTION__, port_settings.xon, port_settings.xoff); | ||
1234 | |||
1235 | /* get the baud rate wanted */ | ||
1236 | port_settings.baud = tty_get_baud_rate(port->tty); | ||
1237 | dbg("%s - baud rate = %d", __FUNCTION__, port_settings.baud); | ||
1238 | |||
1239 | /* handle any settings that aren't specified in the tty structure */ | ||
1240 | port_settings.lloop = 0; | ||
1241 | |||
1242 | /* now send the message to the device */ | ||
1243 | return firm_send_command(port, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); | ||
1244 | } | ||
1245 | |||
1246 | |||
1247 | static int firm_set_rts(struct usb_serial_port *port, __u8 onoff) { | ||
1248 | struct whiteheat_set_rdb rts_command; | ||
1249 | |||
1250 | rts_command.port = port->number - port->serial->minor + 1; | ||
1251 | rts_command.state = onoff; | ||
1252 | return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&rts_command, sizeof(rts_command)); | ||
1253 | } | ||
1254 | |||
1255 | |||
1256 | static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff) { | ||
1257 | struct whiteheat_set_rdb dtr_command; | ||
1258 | |||
1259 | dtr_command.port = port->number - port->serial->minor + 1; | ||
1260 | dtr_command.state = onoff; | ||
1261 | return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&dtr_command, sizeof(dtr_command)); | ||
1262 | } | ||
1263 | |||
1264 | |||
1265 | static int firm_set_break(struct usb_serial_port *port, __u8 onoff) { | ||
1266 | struct whiteheat_set_rdb break_command; | ||
1267 | |||
1268 | break_command.port = port->number - port->serial->minor + 1; | ||
1269 | break_command.state = onoff; | ||
1270 | return firm_send_command(port, WHITEHEAT_SET_RTS, (__u8 *)&break_command, sizeof(break_command)); | ||
1271 | } | ||
1272 | |||
1273 | |||
1274 | static int firm_purge(struct usb_serial_port *port, __u8 rxtx) { | ||
1275 | struct whiteheat_purge purge_command; | ||
1276 | |||
1277 | purge_command.port = port->number - port->serial->minor + 1; | ||
1278 | purge_command.what = rxtx; | ||
1279 | return firm_send_command(port, WHITEHEAT_PURGE, (__u8 *)&purge_command, sizeof(purge_command)); | ||
1280 | } | ||
1281 | |||
1282 | |||
1283 | static int firm_get_dtr_rts(struct usb_serial_port *port) { | ||
1284 | struct whiteheat_simple get_dr_command; | ||
1285 | |||
1286 | get_dr_command.port = port->number - port->serial->minor + 1; | ||
1287 | return firm_send_command(port, WHITEHEAT_GET_DTR_RTS, (__u8 *)&get_dr_command, sizeof(get_dr_command)); | ||
1288 | } | ||
1289 | |||
1290 | |||
1291 | static int firm_report_tx_done(struct usb_serial_port *port) { | ||
1292 | struct whiteheat_simple close_command; | ||
1293 | |||
1294 | close_command.port = port->number - port->serial->minor + 1; | ||
1295 | return firm_send_command(port, WHITEHEAT_REPORT_TX_DONE, (__u8 *)&close_command, sizeof(close_command)); | ||
1296 | } | ||
1297 | |||
1298 | |||
1299 | /***************************************************************************** | ||
1300 | * Connect Tech's White Heat utility functions | ||
1301 | *****************************************************************************/ | ||
1302 | static int start_command_port(struct usb_serial *serial) | ||
1303 | { | ||
1304 | struct usb_serial_port *command_port; | ||
1305 | struct whiteheat_command_private *command_info; | ||
1306 | unsigned long flags; | ||
1307 | int retval = 0; | ||
1308 | |||
1309 | command_port = serial->port[COMMAND_PORT]; | ||
1310 | command_info = usb_get_serial_port_data(command_port); | ||
1311 | spin_lock_irqsave(&command_info->lock, flags); | ||
1312 | if (!command_info->port_running) { | ||
1313 | /* Work around HCD bugs */ | ||
1314 | usb_clear_halt(serial->dev, command_port->read_urb->pipe); | ||
1315 | |||
1316 | command_port->read_urb->dev = serial->dev; | ||
1317 | retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL); | ||
1318 | if (retval) { | ||
1319 | err("%s - failed submitting read urb, error %d", __FUNCTION__, retval); | ||
1320 | goto exit; | ||
1321 | } | ||
1322 | } | ||
1323 | command_info->port_running++; | ||
1324 | |||
1325 | exit: | ||
1326 | spin_unlock_irqrestore(&command_info->lock, flags); | ||
1327 | return retval; | ||
1328 | } | ||
1329 | |||
1330 | |||
1331 | static void stop_command_port(struct usb_serial *serial) | ||
1332 | { | ||
1333 | struct usb_serial_port *command_port; | ||
1334 | struct whiteheat_command_private *command_info; | ||
1335 | unsigned long flags; | ||
1336 | |||
1337 | command_port = serial->port[COMMAND_PORT]; | ||
1338 | command_info = usb_get_serial_port_data(command_port); | ||
1339 | spin_lock_irqsave(&command_info->lock, flags); | ||
1340 | command_info->port_running--; | ||
1341 | if (!command_info->port_running) | ||
1342 | usb_kill_urb(command_port->read_urb); | ||
1343 | spin_unlock_irqrestore(&command_info->lock, flags); | ||
1344 | } | ||
1345 | |||
1346 | |||
1347 | static int start_port_read(struct usb_serial_port *port) | ||
1348 | { | ||
1349 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
1350 | struct whiteheat_urb_wrap *wrap; | ||
1351 | struct urb *urb; | ||
1352 | int retval = 0; | ||
1353 | unsigned long flags; | ||
1354 | struct list_head *tmp; | ||
1355 | struct list_head *tmp2; | ||
1356 | |||
1357 | spin_lock_irqsave(&info->lock, flags); | ||
1358 | |||
1359 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_free) { | ||
1360 | list_del(tmp); | ||
1361 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
1362 | urb = wrap->urb; | ||
1363 | urb->dev = port->serial->dev; | ||
1364 | retval = usb_submit_urb(urb, GFP_KERNEL); | ||
1365 | if (retval) { | ||
1366 | list_add(tmp, &info->rx_urbs_free); | ||
1367 | list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { | ||
1368 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
1369 | urb = wrap->urb; | ||
1370 | usb_kill_urb(urb); | ||
1371 | list_del(tmp); | ||
1372 | list_add(tmp, &info->rx_urbs_free); | ||
1373 | } | ||
1374 | break; | ||
1375 | } | ||
1376 | list_add(tmp, &info->rx_urbs_submitted); | ||
1377 | } | ||
1378 | |||
1379 | spin_unlock_irqrestore(&info->lock, flags); | ||
1380 | |||
1381 | return retval; | ||
1382 | } | ||
1383 | |||
1384 | |||
1385 | static struct whiteheat_urb_wrap *urb_to_wrap(struct urb* urb, struct list_head *head) | ||
1386 | { | ||
1387 | struct whiteheat_urb_wrap *wrap; | ||
1388 | struct list_head *tmp; | ||
1389 | |||
1390 | list_for_each(tmp, head) { | ||
1391 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
1392 | if (wrap->urb == urb) | ||
1393 | return wrap; | ||
1394 | } | ||
1395 | |||
1396 | return NULL; | ||
1397 | } | ||
1398 | |||
1399 | |||
1400 | static struct list_head *list_first(struct list_head *head) | ||
1401 | { | ||
1402 | return head->next; | ||
1403 | } | ||
1404 | |||
1405 | |||
1406 | static void rx_data_softint(void *private) | ||
1407 | { | ||
1408 | struct usb_serial_port *port = (struct usb_serial_port *)private; | ||
1409 | struct whiteheat_private *info = usb_get_serial_port_data(port); | ||
1410 | struct tty_struct *tty = port->tty; | ||
1411 | struct whiteheat_urb_wrap *wrap; | ||
1412 | struct urb *urb; | ||
1413 | unsigned long flags; | ||
1414 | struct list_head *tmp; | ||
1415 | struct list_head *tmp2; | ||
1416 | int result; | ||
1417 | int sent = 0; | ||
1418 | |||
1419 | spin_lock_irqsave(&info->lock, flags); | ||
1420 | if (info->flags & THROTTLED) { | ||
1421 | spin_unlock_irqrestore(&info->lock, flags); | ||
1422 | return; | ||
1423 | } | ||
1424 | |||
1425 | list_for_each_safe(tmp, tmp2, &info->rx_urb_q) { | ||
1426 | list_del(tmp); | ||
1427 | spin_unlock_irqrestore(&info->lock, flags); | ||
1428 | |||
1429 | wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); | ||
1430 | urb = wrap->urb; | ||
1431 | |||
1432 | if (tty && urb->actual_length) { | ||
1433 | if (urb->actual_length > TTY_FLIPBUF_SIZE - tty->flip.count) { | ||
1434 | spin_lock_irqsave(&info->lock, flags); | ||
1435 | list_add(tmp, &info->rx_urb_q); | ||
1436 | spin_unlock_irqrestore(&info->lock, flags); | ||
1437 | tty_flip_buffer_push(tty); | ||
1438 | schedule_work(&info->rx_work); | ||
1439 | return; | ||
1440 | } | ||
1441 | |||
1442 | memcpy(tty->flip.char_buf_ptr, urb->transfer_buffer, urb->actual_length); | ||
1443 | tty->flip.char_buf_ptr += urb->actual_length; | ||
1444 | tty->flip.count += urb->actual_length; | ||
1445 | sent += urb->actual_length; | ||
1446 | } | ||
1447 | |||
1448 | urb->dev = port->serial->dev; | ||
1449 | result = usb_submit_urb(urb, GFP_ATOMIC); | ||
1450 | if (result) { | ||
1451 | err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); | ||
1452 | spin_lock_irqsave(&info->lock, flags); | ||
1453 | list_add(tmp, &info->rx_urbs_free); | ||
1454 | continue; | ||
1455 | } | ||
1456 | |||
1457 | spin_lock_irqsave(&info->lock, flags); | ||
1458 | list_add(tmp, &info->rx_urbs_submitted); | ||
1459 | } | ||
1460 | spin_unlock_irqrestore(&info->lock, flags); | ||
1461 | |||
1462 | if (sent) | ||
1463 | tty_flip_buffer_push(tty); | ||
1464 | } | ||
1465 | |||
1466 | |||
1467 | /***************************************************************************** | ||
1468 | * Connect Tech's White Heat module functions | ||
1469 | *****************************************************************************/ | ||
1470 | static int __init whiteheat_init (void) | ||
1471 | { | ||
1472 | int retval; | ||
1473 | retval = usb_serial_register(&whiteheat_fake_device); | ||
1474 | if (retval) | ||
1475 | goto failed_fake_register; | ||
1476 | retval = usb_serial_register(&whiteheat_device); | ||
1477 | if (retval) | ||
1478 | goto failed_device_register; | ||
1479 | retval = usb_register(&whiteheat_driver); | ||
1480 | if (retval) | ||
1481 | goto failed_usb_register; | ||
1482 | info(DRIVER_DESC " " DRIVER_VERSION); | ||
1483 | return 0; | ||
1484 | failed_usb_register: | ||
1485 | usb_serial_deregister(&whiteheat_device); | ||
1486 | failed_device_register: | ||
1487 | usb_serial_deregister(&whiteheat_fake_device); | ||
1488 | failed_fake_register: | ||
1489 | return retval; | ||
1490 | } | ||
1491 | |||
1492 | |||
1493 | static void __exit whiteheat_exit (void) | ||
1494 | { | ||
1495 | usb_deregister (&whiteheat_driver); | ||
1496 | usb_serial_deregister (&whiteheat_fake_device); | ||
1497 | usb_serial_deregister (&whiteheat_device); | ||
1498 | } | ||
1499 | |||
1500 | |||
1501 | module_init(whiteheat_init); | ||
1502 | module_exit(whiteheat_exit); | ||
1503 | |||
1504 | MODULE_AUTHOR( DRIVER_AUTHOR ); | ||
1505 | MODULE_DESCRIPTION( DRIVER_DESC ); | ||
1506 | MODULE_LICENSE("GPL"); | ||
1507 | |||
1508 | module_param(urb_pool_size, int, 0); | ||
1509 | MODULE_PARM_DESC(urb_pool_size, "Number of urbs to use for buffering"); | ||
1510 | |||
1511 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
1512 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||