1 /* 2 * Copyright (c) 2007-2008 by Michael Lotz 3 * Heavily based on the original usb_serial driver which is: 4 * 5 * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li> 6 * Distributed under the terms of the MIT License. 7 */ 8 #include "ACM.h" 9 #include "Driver.h" 10 11 12 ACMDevice::ACMDevice(usb_device device, uint16 vendorID, uint16 productID, 13 const char *description) 14 : SerialDevice(device, vendorID, productID, description) 15 { 16 } 17 18 19 status_t 20 ACMDevice::AddDevice(const usb_configuration_info *config) 21 { 22 TRACE_FUNCALLS("> ACMDevice::AddDevice(0x%08x, 0x%08x)\n", this, config); 23 24 status_t status = ENODEV; 25 uint8 masterIndex = 0; 26 uint8 slaveIndex = 0; 27 usb_cdc_cm_functional_descriptor* cmDesc = NULL; 28 usb_cdc_union_functional_descriptor* unionDesc = NULL; 29 30 // Search ACM Communication Interface 31 for (size_t i = 0; i < config->interface_count && status < B_OK; i++) { 32 usb_interface_info *interface = config->interface[i].active; 33 if (interface == NULL) 34 continue; 35 usb_interface_descriptor *descriptor = interface->descr; 36 if (descriptor == NULL) 37 continue; 38 if (descriptor->interface_class != USB_CDC_COMMUNICATION_INTERFACE_CLASS 39 || descriptor->interface_subclass != USB_CDC_COMMUNICATION_INTERFACE_ACM_SUBCLASS) 40 continue; 41 42 // Found ACM Communication Interface! 43 // Get functional descriptors of some interest, if any 44 for (size_t j = 0; j < interface->generic_count; j++) { 45 usb_generic_descriptor *generic = &interface->generic[j]->generic; 46 switch (generic->data[0]) { 47 case USB_CDC_CM_FUNCTIONAL_DESCRIPTOR: 48 cmDesc = (usb_cdc_cm_functional_descriptor*)generic; 49 break; 50 51 case USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR: 52 break; 53 54 case USB_CDC_UNION_FUNCTIONAL_DESCRIPTOR: 55 unionDesc = (usb_cdc_union_functional_descriptor*)generic; 56 break; 57 } 58 } 59 60 masterIndex = unionDesc ? unionDesc->master_interface : i; 61 slaveIndex = cmDesc ? cmDesc->data_interface 62 : unionDesc ? unionDesc->slave_interfaces[0] : 0; 63 64 TRACE("ACM device found on configuration #%d: master itf: %d, " 65 "slave/data itf: %d\n", config->descr->configuration, 66 masterIndex, slaveIndex); 67 68 // Some ACM USB devices report the wrong unions which rightfully 69 // breaks probing. Some drivers keep a list of these devices, 70 // for now we just assume identical indexes are wrong. 71 if (masterIndex == slaveIndex) { 72 TRACE_ALWAYS("Command interface matches data interface, " 73 "assuming broken union quirk!\n"); 74 masterIndex = 0; 75 slaveIndex = 1; 76 } 77 78 status = B_OK; 79 break; 80 } 81 82 if (status == B_OK && masterIndex < config->interface_count) { 83 // check that the indicated master interface fits our need 84 usb_interface_info *interface = config->interface[masterIndex].active; 85 usb_interface_descriptor *descriptor = interface->descr; 86 if ((descriptor->interface_class == USB_CDC_COMMUNICATION_INTERFACE_CLASS 87 || descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS) 88 && interface->endpoint_count >= 1) { 89 SetControlPipe(interface->endpoint[0].handle); 90 SetInterruptBufferSize(interface->endpoint[0].descr->max_packet_size); 91 } else { 92 TRACE("Indicated command interface doesn't fit our needs!\n"); 93 status = ENODEV; 94 } 95 } 96 97 if (status == B_OK && slaveIndex < config->interface_count) { 98 // check that the indicated slave interface fits our need 99 usb_interface_info *interface = config->interface[slaveIndex].active; 100 usb_interface_descriptor *descriptor = interface->descr; 101 if (descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS 102 && interface->endpoint_count >= 2) { 103 if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)) { 104 SetWriteBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16)); 105 SetWritePipe(interface->endpoint[0].handle); 106 } else { 107 SetReadBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16)); 108 SetReadPipe(interface->endpoint[0].handle); 109 } 110 111 if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) { 112 SetReadBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16)); 113 SetReadPipe(interface->endpoint[1].handle); 114 } else { 115 SetWriteBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16)); 116 SetWritePipe(interface->endpoint[1].handle); 117 } 118 } else { 119 TRACE("Indicated data interface doesn't fit our needs!\n"); 120 status = ENODEV; 121 } 122 } 123 124 TRACE_FUNCRET("< ACMDevice::AddDevice() returns: 0x%08x\n", status); 125 return status; 126 } 127 128 129 status_t 130 ACMDevice::SetLineCoding(usb_cdc_line_coding *lineCoding) 131 { 132 TRACE_FUNCALLS("> ACMDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n", 133 this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity, 134 lineCoding->databits); 135 136 size_t length = 0; 137 status_t status = gUSBModule->send_request(Device(), 138 USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, 139 USB_CDC_SET_LINE_CODING, 0, 0, 140 sizeof(usb_cdc_line_coding), 141 lineCoding, &length); 142 143 TRACE_FUNCRET("< ACMDevice::SetLineCoding() returns: 0x%08x\n", status); 144 return status; 145 } 146 147 148 status_t 149 ACMDevice::SetControlLineState(uint16 state) 150 { 151 TRACE_FUNCALLS("> ACMDevice::SetControlLineState(0x%08x, 0x%04x)\n", this, state); 152 153 size_t length = 0; 154 status_t status = gUSBModule->send_request(Device(), 155 USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, 156 USB_CDC_SET_CONTROL_LINE_STATE, state, 0, 0, NULL, &length); 157 158 TRACE_FUNCRET("< ACMDevice::SetControlLineState() returns: 0x%08x\n", status); 159 return status; 160 } 161