diff options
author | Greg Kroah-Hartman <greg@kroah.com> | 2007-01-18 03:20:19 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 18:44:30 -0500 |
commit | 6e8cf7751f9fb913095d6142d068f41fbf0424bb (patch) | |
tree | 1ce42ebe426fc34ba5e9e8cbb30ef9da82370c60 | |
parent | 20b2e28fc5557cda2cc840f44c6744b61b068ad6 (diff) |
USB: add EPIC support to the io_edgeport driver
This patch adds EPiC support to the io_edgeport driver which adds
support for a number of NCR printers:
- NCR (Axiohm) 7401-K580 printer
- NCR (TEC) 7401-K590 printer, 7402-K592
- NCR (TEC) 7167, 7168 printers
- NCR (TEC) 7197, 7198, F306, F307, F309 printers
- NCR (Axiohm) 7194 printer
- NCR (Axiohm) 7158 printer
and a few more.
It is based on the 2.6.19 kernel.
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
-rw-r--r-- | drivers/usb/serial/io_edgeport.c | 409 | ||||
-rw-r--r-- | drivers/usb/serial/io_edgeport.h | 6 | ||||
-rw-r--r-- | drivers/usb/serial/io_tables.h | 50 | ||||
-rw-r--r-- | drivers/usb/serial/io_usbvend.h | 5 |
4 files changed, 386 insertions, 84 deletions
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index f623d58370a4..cca1607857f7 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c | |||
@@ -146,6 +146,8 @@ struct edgeport_serial { | |||
146 | struct edge_manuf_descriptor manuf_descriptor; /* the manufacturer descriptor */ | 146 | struct edge_manuf_descriptor manuf_descriptor; /* the manufacturer descriptor */ |
147 | struct edge_boot_descriptor boot_descriptor; /* the boot firmware descriptor */ | 147 | struct edge_boot_descriptor boot_descriptor; /* the boot firmware descriptor */ |
148 | struct edgeport_product_info product_info; /* Product Info */ | 148 | struct edgeport_product_info product_info; /* Product Info */ |
149 | struct edge_compatibility_descriptor epic_descriptor; /* Edgeport compatible descriptor */ | ||
150 | int is_epic; /* flag if EPiC device or not */ | ||
149 | 151 | ||
150 | __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ | 152 | __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ |
151 | unsigned char * interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ | 153 | unsigned char * interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ |
@@ -397,6 +399,7 @@ static int get_string (struct usb_device *dev, int Id, char *string, int buflen) | |||
397 | unicode_to_ascii(string, buflen, pStringDesc->wData, pStringDesc->bLength/2); | 399 | unicode_to_ascii(string, buflen, pStringDesc->wData, pStringDesc->bLength/2); |
398 | 400 | ||
399 | kfree(pStringDesc); | 401 | kfree(pStringDesc); |
402 | dbg("%s - USB String %s", __FUNCTION__, string); | ||
400 | return strlen(string); | 403 | return strlen(string); |
401 | } | 404 | } |
402 | 405 | ||
@@ -434,6 +437,34 @@ static int get_string_desc (struct usb_device *dev, int Id, struct usb_string_de | |||
434 | } | 437 | } |
435 | #endif | 438 | #endif |
436 | 439 | ||
440 | static void dump_product_info(struct edgeport_product_info *product_info) | ||
441 | { | ||
442 | // Dump Product Info structure | ||
443 | dbg("**Product Information:"); | ||
444 | dbg(" ProductId %x", product_info->ProductId ); | ||
445 | dbg(" NumPorts %d", product_info->NumPorts ); | ||
446 | dbg(" ProdInfoVer %d", product_info->ProdInfoVer ); | ||
447 | dbg(" IsServer %d", product_info->IsServer); | ||
448 | dbg(" IsRS232 %d", product_info->IsRS232 ); | ||
449 | dbg(" IsRS422 %d", product_info->IsRS422 ); | ||
450 | dbg(" IsRS485 %d", product_info->IsRS485 ); | ||
451 | dbg(" RomSize %d", product_info->RomSize ); | ||
452 | dbg(" RamSize %d", product_info->RamSize ); | ||
453 | dbg(" CpuRev %x", product_info->CpuRev ); | ||
454 | dbg(" BoardRev %x", product_info->BoardRev); | ||
455 | dbg(" BootMajorVersion %d.%d.%d", product_info->BootMajorVersion, | ||
456 | product_info->BootMinorVersion, | ||
457 | le16_to_cpu(product_info->BootBuildNumber)); | ||
458 | dbg(" FirmwareMajorVersion %d.%d.%d", product_info->FirmwareMajorVersion, | ||
459 | product_info->FirmwareMinorVersion, | ||
460 | le16_to_cpu(product_info->FirmwareBuildNumber)); | ||
461 | dbg(" ManufactureDescDate %d/%d/%d", product_info->ManufactureDescDate[0], | ||
462 | product_info->ManufactureDescDate[1], | ||
463 | product_info->ManufactureDescDate[2]+1900); | ||
464 | dbg(" iDownloadFile 0x%x", product_info->iDownloadFile); | ||
465 | dbg(" EpicVer %d", product_info->EpicVer); | ||
466 | } | ||
467 | |||
437 | static void get_product_info(struct edgeport_serial *edge_serial) | 468 | static void get_product_info(struct edgeport_serial *edge_serial) |
438 | { | 469 | { |
439 | struct edgeport_product_info *product_info = &edge_serial->product_info; | 470 | struct edgeport_product_info *product_info = &edge_serial->product_info; |
@@ -495,30 +526,60 @@ static void get_product_info(struct edgeport_serial *edge_serial) | |||
495 | break; | 526 | break; |
496 | } | 527 | } |
497 | 528 | ||
498 | // Dump Product Info structure | 529 | dump_product_info(product_info); |
499 | dbg("**Product Information:"); | 530 | } |
500 | dbg(" ProductId %x", product_info->ProductId ); | ||
501 | dbg(" NumPorts %d", product_info->NumPorts ); | ||
502 | dbg(" ProdInfoVer %d", product_info->ProdInfoVer ); | ||
503 | dbg(" IsServer %d", product_info->IsServer); | ||
504 | dbg(" IsRS232 %d", product_info->IsRS232 ); | ||
505 | dbg(" IsRS422 %d", product_info->IsRS422 ); | ||
506 | dbg(" IsRS485 %d", product_info->IsRS485 ); | ||
507 | dbg(" RomSize %d", product_info->RomSize ); | ||
508 | dbg(" RamSize %d", product_info->RamSize ); | ||
509 | dbg(" CpuRev %x", product_info->CpuRev ); | ||
510 | dbg(" BoardRev %x", product_info->BoardRev); | ||
511 | dbg(" BootMajorVersion %d.%d.%d", product_info->BootMajorVersion, | ||
512 | product_info->BootMinorVersion, | ||
513 | le16_to_cpu(product_info->BootBuildNumber)); | ||
514 | dbg(" FirmwareMajorVersion %d.%d.%d", product_info->FirmwareMajorVersion, | ||
515 | product_info->FirmwareMinorVersion, | ||
516 | le16_to_cpu(product_info->FirmwareBuildNumber)); | ||
517 | dbg(" ManufactureDescDate %d/%d/%d", product_info->ManufactureDescDate[0], | ||
518 | product_info->ManufactureDescDate[1], | ||
519 | product_info->ManufactureDescDate[2]+1900); | ||
520 | dbg(" iDownloadFile 0x%x", product_info->iDownloadFile); | ||
521 | 531 | ||
532 | static int get_epic_descriptor(struct edgeport_serial *ep) | ||
533 | { | ||
534 | int result; | ||
535 | struct usb_serial *serial = ep->serial; | ||
536 | struct edgeport_product_info *product_info = &ep->product_info; | ||
537 | struct edge_compatibility_descriptor *epic = &ep->epic_descriptor; | ||
538 | struct edge_compatibility_bits *bits; | ||
539 | |||
540 | ep->is_epic = 0; | ||
541 | result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), | ||
542 | USB_REQUEST_ION_GET_EPIC_DESC, | ||
543 | 0xC0, 0x00, 0x00, | ||
544 | &ep->epic_descriptor, | ||
545 | sizeof(struct edge_compatibility_descriptor), | ||
546 | 300); | ||
547 | |||
548 | dbg("%s result = %d", __FUNCTION__, result); | ||
549 | |||
550 | if (result > 0) { | ||
551 | ep->is_epic = 1; | ||
552 | memset(product_info, 0, sizeof(struct edgeport_product_info)); | ||
553 | |||
554 | product_info->NumPorts = epic->NumPorts; | ||
555 | product_info->ProdInfoVer = 0; | ||
556 | product_info->FirmwareMajorVersion = epic->MajorVersion; | ||
557 | product_info->FirmwareMinorVersion = epic->MinorVersion; | ||
558 | product_info->FirmwareBuildNumber = epic->BuildNumber; | ||
559 | product_info->iDownloadFile = epic->iDownloadFile; | ||
560 | product_info->EpicVer = epic->EpicVer; | ||
561 | product_info->Epic = epic->Supports; | ||
562 | product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE; | ||
563 | dump_product_info(product_info); | ||
564 | |||
565 | bits = &ep->epic_descriptor.Supports; | ||
566 | dbg("**EPIC descriptor:"); | ||
567 | dbg(" VendEnableSuspend: %s", bits->VendEnableSuspend ? "TRUE": "FALSE"); | ||
568 | dbg(" IOSPOpen : %s", bits->IOSPOpen ? "TRUE": "FALSE" ); | ||
569 | dbg(" IOSPClose : %s", bits->IOSPClose ? "TRUE": "FALSE" ); | ||
570 | dbg(" IOSPChase : %s", bits->IOSPChase ? "TRUE": "FALSE" ); | ||
571 | dbg(" IOSPSetRxFlow : %s", bits->IOSPSetRxFlow ? "TRUE": "FALSE" ); | ||
572 | dbg(" IOSPSetTxFlow : %s", bits->IOSPSetTxFlow ? "TRUE": "FALSE" ); | ||
573 | dbg(" IOSPSetXChar : %s", bits->IOSPSetXChar ? "TRUE": "FALSE" ); | ||
574 | dbg(" IOSPRxCheck : %s", bits->IOSPRxCheck ? "TRUE": "FALSE" ); | ||
575 | dbg(" IOSPSetClrBreak : %s", bits->IOSPSetClrBreak ? "TRUE": "FALSE" ); | ||
576 | dbg(" IOSPWriteMCR : %s", bits->IOSPWriteMCR ? "TRUE": "FALSE" ); | ||
577 | dbg(" IOSPWriteLCR : %s", bits->IOSPWriteLCR ? "TRUE": "FALSE" ); | ||
578 | dbg(" IOSPSetBaudRate : %s", bits->IOSPSetBaudRate ? "TRUE": "FALSE" ); | ||
579 | dbg(" TrueEdgeport : %s", bits->TrueEdgeport ? "TRUE": "FALSE" ); | ||
580 | } | ||
581 | |||
582 | return result; | ||
522 | } | 583 | } |
523 | 584 | ||
524 | 585 | ||
@@ -1017,21 +1078,29 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) | |||
1017 | 1078 | ||
1018 | edge_port->closePending = TRUE; | 1079 | edge_port->closePending = TRUE; |
1019 | 1080 | ||
1020 | /* flush and chase */ | 1081 | if ((!edge_serial->is_epic) || |
1021 | edge_port->chaseResponsePending = TRUE; | 1082 | ((edge_serial->is_epic) && |
1022 | 1083 | (edge_serial->epic_descriptor.Supports.IOSPChase))) { | |
1023 | dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); | 1084 | /* flush and chase */ |
1024 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); | 1085 | edge_port->chaseResponsePending = TRUE; |
1025 | if (status == 0) { | 1086 | |
1026 | // block until chase finished | 1087 | dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); |
1027 | block_until_chase_response(edge_port); | 1088 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); |
1028 | } else { | 1089 | if (status == 0) { |
1029 | edge_port->chaseResponsePending = FALSE; | 1090 | // block until chase finished |
1091 | block_until_chase_response(edge_port); | ||
1092 | } else { | ||
1093 | edge_port->chaseResponsePending = FALSE; | ||
1094 | } | ||
1030 | } | 1095 | } |
1031 | 1096 | ||
1032 | /* close the port */ | 1097 | if ((!edge_serial->is_epic) || |
1033 | dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __FUNCTION__); | 1098 | ((edge_serial->is_epic) && |
1034 | send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); | 1099 | (edge_serial->epic_descriptor.Supports.IOSPClose))) { |
1100 | /* close the port */ | ||
1101 | dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __FUNCTION__); | ||
1102 | send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); | ||
1103 | } | ||
1035 | 1104 | ||
1036 | //port->close = TRUE; | 1105 | //port->close = TRUE; |
1037 | edge_port->closePending = FALSE; | 1106 | edge_port->closePending = FALSE; |
@@ -1694,29 +1763,38 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned | |||
1694 | static void edge_break (struct usb_serial_port *port, int break_state) | 1763 | static void edge_break (struct usb_serial_port *port, int break_state) |
1695 | { | 1764 | { |
1696 | struct edgeport_port *edge_port = usb_get_serial_port_data(port); | 1765 | struct edgeport_port *edge_port = usb_get_serial_port_data(port); |
1766 | struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial); | ||
1697 | int status; | 1767 | int status; |
1698 | 1768 | ||
1699 | /* flush and chase */ | 1769 | if ((!edge_serial->is_epic) || |
1700 | edge_port->chaseResponsePending = TRUE; | 1770 | ((edge_serial->is_epic) && |
1701 | 1771 | (edge_serial->epic_descriptor.Supports.IOSPChase))) { | |
1702 | dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); | 1772 | /* flush and chase */ |
1703 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); | 1773 | edge_port->chaseResponsePending = TRUE; |
1704 | if (status == 0) { | 1774 | |
1705 | // block until chase finished | 1775 | dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); |
1706 | block_until_chase_response(edge_port); | 1776 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); |
1707 | } else { | 1777 | if (status == 0) { |
1708 | edge_port->chaseResponsePending = FALSE; | 1778 | // block until chase finished |
1779 | block_until_chase_response(edge_port); | ||
1780 | } else { | ||
1781 | edge_port->chaseResponsePending = FALSE; | ||
1782 | } | ||
1709 | } | 1783 | } |
1710 | 1784 | ||
1711 | if (break_state == -1) { | 1785 | if ((!edge_serial->is_epic) || |
1712 | dbg("%s - Sending IOSP_CMD_SET_BREAK", __FUNCTION__); | 1786 | ((edge_serial->is_epic) && |
1713 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_BREAK, 0); | 1787 | (edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) { |
1714 | } else { | 1788 | if (break_state == -1) { |
1715 | dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __FUNCTION__); | 1789 | dbg("%s - Sending IOSP_CMD_SET_BREAK", __FUNCTION__); |
1716 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CLEAR_BREAK, 0); | 1790 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_BREAK, 0); |
1717 | } | 1791 | } else { |
1718 | if (status) { | 1792 | dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __FUNCTION__); |
1719 | dbg("%s - error sending break set/clear command.", __FUNCTION__); | 1793 | status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CLEAR_BREAK, 0); |
1794 | } | ||
1795 | if (status) { | ||
1796 | dbg("%s - error sending break set/clear command.", __FUNCTION__); | ||
1797 | } | ||
1720 | } | 1798 | } |
1721 | 1799 | ||
1722 | return; | 1800 | return; |
@@ -2288,6 +2366,7 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer | |||
2288 | *****************************************************************************/ | 2366 | *****************************************************************************/ |
2289 | static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate) | 2367 | static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate) |
2290 | { | 2368 | { |
2369 | struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); | ||
2291 | unsigned char *cmdBuffer; | 2370 | unsigned char *cmdBuffer; |
2292 | unsigned char *currCmd; | 2371 | unsigned char *currCmd; |
2293 | int cmdLen = 0; | 2372 | int cmdLen = 0; |
@@ -2295,6 +2374,14 @@ static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRa | |||
2295 | int status; | 2374 | int status; |
2296 | unsigned char number = edge_port->port->number - edge_port->port->serial->minor; | 2375 | unsigned char number = edge_port->port->number - edge_port->port->serial->minor; |
2297 | 2376 | ||
2377 | if ((!edge_serial->is_epic) || | ||
2378 | ((edge_serial->is_epic) && | ||
2379 | (!edge_serial->epic_descriptor.Supports.IOSPSetBaudRate))) { | ||
2380 | dbg("SendCmdWriteBaudRate - NOT Setting baud rate for port = %d, baud = %d", | ||
2381 | edge_port->port->number, baudRate); | ||
2382 | return 0; | ||
2383 | } | ||
2384 | |||
2298 | dbg("%s - port = %d, baud = %d", __FUNCTION__, edge_port->port->number, baudRate); | 2385 | dbg("%s - port = %d, baud = %d", __FUNCTION__, edge_port->port->number, baudRate); |
2299 | 2386 | ||
2300 | status = calc_baud_rate_divisor (baudRate, &divisor); | 2387 | status = calc_baud_rate_divisor (baudRate, &divisor); |
@@ -2374,6 +2461,7 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor) | |||
2374 | *****************************************************************************/ | 2461 | *****************************************************************************/ |
2375 | static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue) | 2462 | static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue) |
2376 | { | 2463 | { |
2464 | struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); | ||
2377 | unsigned char *cmdBuffer; | 2465 | unsigned char *cmdBuffer; |
2378 | unsigned char *currCmd; | 2466 | unsigned char *currCmd; |
2379 | unsigned long cmdLen = 0; | 2467 | unsigned long cmdLen = 0; |
@@ -2381,6 +2469,22 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r | |||
2381 | 2469 | ||
2382 | dbg("%s - write to %s register 0x%02x", (regNum == MCR) ? "MCR" : "LCR", __FUNCTION__, regValue); | 2470 | dbg("%s - write to %s register 0x%02x", (regNum == MCR) ? "MCR" : "LCR", __FUNCTION__, regValue); |
2383 | 2471 | ||
2472 | if ((!edge_serial->is_epic) || | ||
2473 | ((edge_serial->is_epic) && | ||
2474 | (!edge_serial->epic_descriptor.Supports.IOSPWriteMCR) && | ||
2475 | (regNum == MCR))) { | ||
2476 | dbg("SendCmdWriteUartReg - Not writting to MCR Register"); | ||
2477 | return 0; | ||
2478 | } | ||
2479 | |||
2480 | if ((!edge_serial->is_epic) || | ||
2481 | ((edge_serial->is_epic) && | ||
2482 | (!edge_serial->epic_descriptor.Supports.IOSPWriteLCR) && | ||
2483 | (regNum == LCR))) { | ||
2484 | dbg ("SendCmdWriteUartReg - Not writting to LCR Register"); | ||
2485 | return 0; | ||
2486 | } | ||
2487 | |||
2384 | // Alloc memory for the string of commands. | 2488 | // Alloc memory for the string of commands. |
2385 | cmdBuffer = kmalloc (0x10, GFP_ATOMIC); | 2489 | cmdBuffer = kmalloc (0x10, GFP_ATOMIC); |
2386 | if (cmdBuffer == NULL ) { | 2490 | if (cmdBuffer == NULL ) { |
@@ -2414,6 +2518,7 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r | |||
2414 | #endif | 2518 | #endif |
2415 | static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios) | 2519 | static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios) |
2416 | { | 2520 | { |
2521 | struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); | ||
2417 | struct tty_struct *tty; | 2522 | struct tty_struct *tty; |
2418 | int baud; | 2523 | int baud; |
2419 | unsigned cflag; | 2524 | unsigned cflag; |
@@ -2494,8 +2599,12 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi | |||
2494 | unsigned char stop_char = STOP_CHAR(tty); | 2599 | unsigned char stop_char = STOP_CHAR(tty); |
2495 | unsigned char start_char = START_CHAR(tty); | 2600 | unsigned char start_char = START_CHAR(tty); |
2496 | 2601 | ||
2497 | send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char); | 2602 | if ((!edge_serial->is_epic) || |
2498 | send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char); | 2603 | ((edge_serial->is_epic) && |
2604 | (edge_serial->epic_descriptor.Supports.IOSPSetXChar))) { | ||
2605 | send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XON_CHAR, start_char); | ||
2606 | send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char); | ||
2607 | } | ||
2499 | 2608 | ||
2500 | /* if we are implementing INBOUND XON/XOFF */ | 2609 | /* if we are implementing INBOUND XON/XOFF */ |
2501 | if (I_IXOFF(tty)) { | 2610 | if (I_IXOFF(tty)) { |
@@ -2515,8 +2624,14 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi | |||
2515 | } | 2624 | } |
2516 | 2625 | ||
2517 | /* Set flow control to the configured value */ | 2626 | /* Set flow control to the configured value */ |
2518 | send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow); | 2627 | if ((!edge_serial->is_epic) || |
2519 | send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_TX_FLOW, txFlow); | 2628 | ((edge_serial->is_epic) && |
2629 | (edge_serial->epic_descriptor.Supports.IOSPSetRxFlow))) | ||
2630 | send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow); | ||
2631 | if ((!edge_serial->is_epic) || | ||
2632 | ((edge_serial->is_epic) && | ||
2633 | (edge_serial->epic_descriptor.Supports.IOSPSetTxFlow))) | ||
2634 | send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow); | ||
2520 | 2635 | ||
2521 | 2636 | ||
2522 | edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); | 2637 | edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); |
@@ -2728,6 +2843,13 @@ static int edge_startup (struct usb_serial *serial) | |||
2728 | struct edgeport_port *edge_port; | 2843 | struct edgeport_port *edge_port; |
2729 | struct usb_device *dev; | 2844 | struct usb_device *dev; |
2730 | int i, j; | 2845 | int i, j; |
2846 | int response; | ||
2847 | int interrupt_in_found; | ||
2848 | int bulk_in_found; | ||
2849 | int bulk_out_found; | ||
2850 | static __u32 descriptor[3] = { EDGE_COMPATIBILITY_MASK0, | ||
2851 | EDGE_COMPATIBILITY_MASK1, | ||
2852 | EDGE_COMPATIBILITY_MASK2 }; | ||
2731 | 2853 | ||
2732 | dev = serial->dev; | 2854 | dev = serial->dev; |
2733 | 2855 | ||
@@ -2750,38 +2872,50 @@ static int edge_startup (struct usb_serial *serial) | |||
2750 | 2872 | ||
2751 | dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name); | 2873 | dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name); |
2752 | 2874 | ||
2753 | /* get the manufacturing descriptor for this device */ | 2875 | /* Read the epic descriptor */ |
2754 | get_manufacturing_desc (edge_serial); | 2876 | if (get_epic_descriptor(edge_serial) <= 0) { |
2877 | /* memcpy descriptor to Supports structures */ | ||
2878 | memcpy(&edge_serial->epic_descriptor.Supports, descriptor, | ||
2879 | sizeof(struct edge_compatibility_bits)); | ||
2755 | 2880 | ||
2756 | /* get the boot descriptor */ | 2881 | /* get the manufacturing descriptor for this device */ |
2757 | get_boot_desc (edge_serial); | 2882 | get_manufacturing_desc (edge_serial); |
2758 | 2883 | ||
2759 | get_product_info(edge_serial); | 2884 | /* get the boot descriptor */ |
2885 | get_boot_desc (edge_serial); | ||
2886 | |||
2887 | get_product_info(edge_serial); | ||
2888 | } | ||
2760 | 2889 | ||
2761 | /* set the number of ports from the manufacturing description */ | 2890 | /* set the number of ports from the manufacturing description */ |
2762 | /* serial->num_ports = serial->product_info.NumPorts; */ | 2891 | /* serial->num_ports = serial->product_info.NumPorts; */ |
2763 | if (edge_serial->product_info.NumPorts != serial->num_ports) { | 2892 | if ((!edge_serial->is_epic) && |
2764 | warn("%s - Device Reported %d serial ports vs core " | 2893 | (edge_serial->product_info.NumPorts != serial->num_ports)) { |
2765 | "thinking we have %d ports, email greg@kroah.com this info.", | 2894 | dev_warn(&serial->dev->dev, "Device Reported %d serial ports " |
2766 | __FUNCTION__, edge_serial->product_info.NumPorts, | 2895 | "vs. core thinking we have %d ports, email " |
2767 | serial->num_ports); | 2896 | "greg@kroah.com this information.", |
2897 | edge_serial->product_info.NumPorts, | ||
2898 | serial->num_ports); | ||
2768 | } | 2899 | } |
2769 | 2900 | ||
2770 | dbg("%s - time 1 %ld", __FUNCTION__, jiffies); | 2901 | dbg("%s - time 1 %ld", __FUNCTION__, jiffies); |
2771 | 2902 | ||
2772 | /* now load the application firmware into this device */ | 2903 | /* If not an EPiC device */ |
2773 | load_application_firmware (edge_serial); | 2904 | if (!edge_serial->is_epic) { |
2905 | /* now load the application firmware into this device */ | ||
2906 | load_application_firmware (edge_serial); | ||
2774 | 2907 | ||
2775 | dbg("%s - time 2 %ld", __FUNCTION__, jiffies); | 2908 | dbg("%s - time 2 %ld", __FUNCTION__, jiffies); |
2776 | 2909 | ||
2777 | /* Check current Edgeport EEPROM and update if necessary */ | 2910 | /* Check current Edgeport EEPROM and update if necessary */ |
2778 | update_edgeport_E2PROM (edge_serial); | 2911 | update_edgeport_E2PROM (edge_serial); |
2779 | |||
2780 | dbg("%s - time 3 %ld", __FUNCTION__, jiffies); | ||
2781 | 2912 | ||
2782 | /* set the configuration to use #1 */ | 2913 | dbg("%s - time 3 %ld", __FUNCTION__, jiffies); |
2783 | // dbg("set_configuration 1"); | 2914 | |
2784 | // usb_set_configuration (dev, 1); | 2915 | /* set the configuration to use #1 */ |
2916 | // dbg("set_configuration 1"); | ||
2917 | // usb_set_configuration (dev, 1); | ||
2918 | } | ||
2785 | 2919 | ||
2786 | /* we set up the pointers to the endpoints in the edge_open function, | 2920 | /* we set up the pointers to the endpoints in the edge_open function, |
2787 | * as the structures aren't created yet. */ | 2921 | * as the structures aren't created yet. */ |
@@ -2804,8 +2938,101 @@ static int edge_startup (struct usb_serial *serial) | |||
2804 | edge_port->port = serial->port[i]; | 2938 | edge_port->port = serial->port[i]; |
2805 | usb_set_serial_port_data(serial->port[i], edge_port); | 2939 | usb_set_serial_port_data(serial->port[i], edge_port); |
2806 | } | 2940 | } |
2807 | 2941 | ||
2808 | return 0; | 2942 | response = 0; |
2943 | |||
2944 | if (edge_serial->is_epic) { | ||
2945 | /* EPIC thing, set up our interrupt polling now and our read urb, so | ||
2946 | * that the device knows it really is connected. */ | ||
2947 | interrupt_in_found = bulk_in_found = bulk_out_found = FALSE; | ||
2948 | for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) { | ||
2949 | struct usb_endpoint_descriptor *endpoint; | ||
2950 | int buffer_size; | ||
2951 | |||
2952 | endpoint = &serial->interface->altsetting[0].endpoint[i].desc; | ||
2953 | buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); | ||
2954 | if ((!interrupt_in_found) && | ||
2955 | (usb_endpoint_is_int_in(endpoint))) { | ||
2956 | /* we found a interrupt in endpoint */ | ||
2957 | dbg("found interrupt in"); | ||
2958 | |||
2959 | /* not set up yet, so do it now */ | ||
2960 | edge_serial->interrupt_read_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
2961 | if (!edge_serial->interrupt_read_urb) { | ||
2962 | err("out of memory"); | ||
2963 | return -ENOMEM; | ||
2964 | } | ||
2965 | edge_serial->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL); | ||
2966 | if (!edge_serial->interrupt_in_buffer) { | ||
2967 | err("out of memory"); | ||
2968 | usb_free_urb(edge_serial->interrupt_read_urb); | ||
2969 | return -ENOMEM; | ||
2970 | } | ||
2971 | edge_serial->interrupt_in_endpoint = endpoint->bEndpointAddress; | ||
2972 | |||
2973 | /* set up our interrupt urb */ | ||
2974 | usb_fill_int_urb(edge_serial->interrupt_read_urb, | ||
2975 | dev, | ||
2976 | usb_rcvintpipe(dev, endpoint->bEndpointAddress), | ||
2977 | edge_serial->interrupt_in_buffer, | ||
2978 | buffer_size, | ||
2979 | edge_interrupt_callback, | ||
2980 | edge_serial, | ||
2981 | endpoint->bInterval); | ||
2982 | |||
2983 | interrupt_in_found = TRUE; | ||
2984 | } | ||
2985 | |||
2986 | if ((!bulk_in_found) && | ||
2987 | (usb_endpoint_is_bulk_in(endpoint))) { | ||
2988 | /* we found a bulk in endpoint */ | ||
2989 | dbg("found bulk in"); | ||
2990 | |||
2991 | /* not set up yet, so do it now */ | ||
2992 | edge_serial->read_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
2993 | if (!edge_serial->read_urb) { | ||
2994 | err("out of memory"); | ||
2995 | return -ENOMEM; | ||
2996 | } | ||
2997 | edge_serial->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); | ||
2998 | if (!edge_serial->bulk_in_buffer) { | ||
2999 | err ("out of memory"); | ||
3000 | usb_free_urb(edge_serial->read_urb); | ||
3001 | return -ENOMEM; | ||
3002 | } | ||
3003 | edge_serial->bulk_in_endpoint = endpoint->bEndpointAddress; | ||
3004 | |||
3005 | /* set up our bulk in urb */ | ||
3006 | usb_fill_bulk_urb(edge_serial->read_urb, dev, | ||
3007 | usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), | ||
3008 | edge_serial->bulk_in_buffer, | ||
3009 | endpoint->wMaxPacketSize, | ||
3010 | edge_bulk_in_callback, | ||
3011 | edge_serial); | ||
3012 | bulk_in_found = TRUE; | ||
3013 | } | ||
3014 | |||
3015 | if ((!bulk_out_found) && | ||
3016 | (usb_endpoint_is_bulk_out(endpoint))) { | ||
3017 | /* we found a bulk out endpoint */ | ||
3018 | dbg("found bulk out"); | ||
3019 | edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress; | ||
3020 | bulk_out_found = TRUE; | ||
3021 | } | ||
3022 | } | ||
3023 | |||
3024 | if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) { | ||
3025 | err ("Error - the proper endpoints were not found!"); | ||
3026 | return -ENODEV; | ||
3027 | } | ||
3028 | |||
3029 | /* start interrupt read for this edgeport this interrupt will | ||
3030 | * continue as long as the edgeport is connected */ | ||
3031 | response = usb_submit_urb(edge_serial->interrupt_read_urb, GFP_KERNEL); | ||
3032 | if (response) | ||
3033 | err("%s - Error %d submitting control urb", __FUNCTION__, response); | ||
3034 | } | ||
3035 | return response; | ||
2809 | } | 3036 | } |
2810 | 3037 | ||
2811 | 3038 | ||
@@ -2815,6 +3042,7 @@ static int edge_startup (struct usb_serial *serial) | |||
2815 | ****************************************************************************/ | 3042 | ****************************************************************************/ |
2816 | static void edge_shutdown (struct usb_serial *serial) | 3043 | static void edge_shutdown (struct usb_serial *serial) |
2817 | { | 3044 | { |
3045 | struct edgeport_serial *edge_serial = usb_get_serial_data(serial); | ||
2818 | int i; | 3046 | int i; |
2819 | 3047 | ||
2820 | dbg("%s", __FUNCTION__); | 3048 | dbg("%s", __FUNCTION__); |
@@ -2824,7 +3052,18 @@ static void edge_shutdown (struct usb_serial *serial) | |||
2824 | kfree (usb_get_serial_port_data(serial->port[i])); | 3052 | kfree (usb_get_serial_port_data(serial->port[i])); |
2825 | usb_set_serial_port_data(serial->port[i], NULL); | 3053 | usb_set_serial_port_data(serial->port[i], NULL); |
2826 | } | 3054 | } |
2827 | kfree (usb_get_serial_data(serial)); | 3055 | /* free up our endpoint stuff */ |
3056 | if (edge_serial->is_epic) { | ||
3057 | usb_unlink_urb(edge_serial->interrupt_read_urb); | ||
3058 | usb_free_urb(edge_serial->interrupt_read_urb); | ||
3059 | kfree(edge_serial->interrupt_in_buffer); | ||
3060 | |||
3061 | usb_unlink_urb(edge_serial->read_urb); | ||
3062 | usb_free_urb(edge_serial->read_urb); | ||
3063 | kfree(edge_serial->bulk_in_buffer); | ||
3064 | } | ||
3065 | |||
3066 | kfree(edge_serial); | ||
2828 | usb_set_serial_data(serial, NULL); | 3067 | usb_set_serial_data(serial, NULL); |
2829 | } | 3068 | } |
2830 | 3069 | ||
@@ -2846,6 +3085,9 @@ static int __init edgeport_init(void) | |||
2846 | retval = usb_serial_register(&edgeport_8port_device); | 3085 | retval = usb_serial_register(&edgeport_8port_device); |
2847 | if (retval) | 3086 | if (retval) |
2848 | goto failed_8port_device_register; | 3087 | goto failed_8port_device_register; |
3088 | retval = usb_serial_register(&epic_device); | ||
3089 | if (retval) | ||
3090 | goto failed_epic_device_register; | ||
2849 | retval = usb_register(&io_driver); | 3091 | retval = usb_register(&io_driver); |
2850 | if (retval) | 3092 | if (retval) |
2851 | goto failed_usb_register; | 3093 | goto failed_usb_register; |
@@ -2853,6 +3095,8 @@ static int __init edgeport_init(void) | |||
2853 | return 0; | 3095 | return 0; |
2854 | 3096 | ||
2855 | failed_usb_register: | 3097 | failed_usb_register: |
3098 | usb_serial_deregister(&epic_device); | ||
3099 | failed_epic_device_register: | ||
2856 | usb_serial_deregister(&edgeport_8port_device); | 3100 | usb_serial_deregister(&edgeport_8port_device); |
2857 | failed_8port_device_register: | 3101 | failed_8port_device_register: |
2858 | usb_serial_deregister(&edgeport_4port_device); | 3102 | usb_serial_deregister(&edgeport_4port_device); |
@@ -2873,6 +3117,7 @@ static void __exit edgeport_exit (void) | |||
2873 | usb_serial_deregister (&edgeport_2port_device); | 3117 | usb_serial_deregister (&edgeport_2port_device); |
2874 | usb_serial_deregister (&edgeport_4port_device); | 3118 | usb_serial_deregister (&edgeport_4port_device); |
2875 | usb_serial_deregister (&edgeport_8port_device); | 3119 | usb_serial_deregister (&edgeport_8port_device); |
3120 | usb_serial_deregister (&epic_device); | ||
2876 | } | 3121 | } |
2877 | 3122 | ||
2878 | module_init(edgeport_init); | 3123 | module_init(edgeport_init); |
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 123fa8a904e6..29a913a6daca 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h | |||
@@ -111,10 +111,12 @@ struct edgeport_product_info { | |||
111 | __le16 FirmwareBuildNumber; /* zzzz (LE format) */ | 111 | __le16 FirmwareBuildNumber; /* zzzz (LE format) */ |
112 | 112 | ||
113 | __u8 ManufactureDescDate[3]; /* MM/DD/YY when descriptor template was compiled */ | 113 | __u8 ManufactureDescDate[3]; /* MM/DD/YY when descriptor template was compiled */ |
114 | __u8 Unused1[1]; /* Available */ | 114 | __u8 HardwareType; |
115 | 115 | ||
116 | __u8 iDownloadFile; /* What to download to EPiC device */ | 116 | __u8 iDownloadFile; /* What to download to EPiC device */ |
117 | __u8 Unused2[2]; /* Available */ | 117 | __u8 EpicVer; /* What version of EPiC spec this device supports */ |
118 | |||
119 | struct edge_compatibility_bits Epic; | ||
118 | }; | 120 | }; |
119 | 121 | ||
120 | /* | 122 | /* |
diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index fad561c04c76..3cbb8c19d925 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h | |||
@@ -47,6 +47,18 @@ static struct usb_device_id edgeport_8port_id_table [] = { | |||
47 | { } | 47 | { } |
48 | }; | 48 | }; |
49 | 49 | ||
50 | static struct usb_device_id Epic_port_id_table [] = { | ||
51 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, | ||
52 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, | ||
53 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, | ||
54 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) }, | ||
55 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) }, | ||
56 | { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) }, | ||
57 | { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) }, | ||
58 | { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) }, | ||
59 | { } | ||
60 | }; | ||
61 | |||
50 | /* Devices that this driver supports */ | 62 | /* Devices that this driver supports */ |
51 | static struct usb_device_id id_table_combined [] = { | 63 | static struct usb_device_id id_table_combined [] = { |
52 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) }, | 64 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) }, |
@@ -70,6 +82,14 @@ static struct usb_device_id id_table_combined [] = { | |||
70 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, | 82 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, |
71 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, | 83 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, |
72 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, | 84 | { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, |
85 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, | ||
86 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, | ||
87 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, | ||
88 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) }, | ||
89 | { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) }, | ||
90 | { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) }, | ||
91 | { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) }, | ||
92 | { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) }, | ||
73 | { } /* Terminating entry */ | 93 | { } /* Terminating entry */ |
74 | }; | 94 | }; |
75 | 95 | ||
@@ -165,5 +185,35 @@ static struct usb_serial_driver edgeport_8port_device = { | |||
165 | .write_bulk_callback = edge_bulk_out_data_callback, | 185 | .write_bulk_callback = edge_bulk_out_data_callback, |
166 | }; | 186 | }; |
167 | 187 | ||
188 | static struct usb_serial_driver epic_device = { | ||
189 | .driver = { | ||
190 | .owner = THIS_MODULE, | ||
191 | .name = "epic", | ||
192 | }, | ||
193 | .description = "EPiC device", | ||
194 | .id_table = Epic_port_id_table, | ||
195 | .num_interrupt_in = 1, | ||
196 | .num_bulk_in = 1, | ||
197 | .num_bulk_out = 1, | ||
198 | .num_ports = 1, | ||
199 | .open = edge_open, | ||
200 | .close = edge_close, | ||
201 | .throttle = edge_throttle, | ||
202 | .unthrottle = edge_unthrottle, | ||
203 | .attach = edge_startup, | ||
204 | .shutdown = edge_shutdown, | ||
205 | .ioctl = edge_ioctl, | ||
206 | .set_termios = edge_set_termios, | ||
207 | .tiocmget = edge_tiocmget, | ||
208 | .tiocmset = edge_tiocmset, | ||
209 | .write = edge_write, | ||
210 | .write_room = edge_write_room, | ||
211 | .chars_in_buffer = edge_chars_in_buffer, | ||
212 | .break_ctl = edge_break, | ||
213 | .read_int_callback = edge_interrupt_callback, | ||
214 | .read_bulk_callback = edge_bulk_in_callback, | ||
215 | .write_bulk_callback = edge_bulk_out_data_callback, | ||
216 | }; | ||
217 | |||
168 | #endif | 218 | #endif |
169 | 219 | ||
diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index f1804fd5a3dd..e57fa117e486 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #define USB_VENDOR_ID_ION 0x1608 // Our VID | 31 | #define USB_VENDOR_ID_ION 0x1608 // Our VID |
32 | #define USB_VENDOR_ID_TI 0x0451 // TI VID | 32 | #define USB_VENDOR_ID_TI 0x0451 // TI VID |
33 | #define USB_VENDOR_ID_AXIOHM 0x05D9 /* Axiohm VID */ | ||
33 | 34 | ||
34 | // | 35 | // |
35 | // Definitions of USB product IDs (PID) | 36 | // Definitions of USB product IDs (PID) |
@@ -334,6 +335,10 @@ struct edge_compatibility_bits | |||
334 | 335 | ||
335 | }; | 336 | }; |
336 | 337 | ||
338 | #define EDGE_COMPATIBILITY_MASK0 0x0001 | ||
339 | #define EDGE_COMPATIBILITY_MASK1 0x3FFF | ||
340 | #define EDGE_COMPATIBILITY_MASK2 0x0001 | ||
341 | |||
337 | struct edge_compatibility_descriptor | 342 | struct edge_compatibility_descriptor |
338 | { | 343 | { |
339 | __u8 Length; // Descriptor Length (per USB spec) | 344 | __u8 Length; // Descriptor Length (per USB spec) |