/* * Copyright (c) 2007-2008 by Michael Lotz * Heavily based on the original usb_serial driver which is: * * Copyright (c) 2003 by Siarzhuk Zharski * Distributed under the terms of the MIT License. */ #include "ACM.h" #include "Driver.h" ACMDevice::ACMDevice(usb_device device, uint16 vendorID, uint16 productID, const char *description) : SerialDevice(device, vendorID, productID, description) { } status_t ACMDevice::AddDevice(const usb_configuration_info *config) { TRACE_FUNCALLS("> ACMDevice::AddDevice(0x%08x, 0x%08x)\n", this, config); status_t status = ENODEV; uint8 masterIndex = 0; uint8 slaveIndex = 0; usb_cdc_cm_functional_descriptor* cmDesc = NULL; usb_cdc_union_functional_descriptor* unionDesc = NULL; // Search ACM Communication Interface for (size_t i = 0; i < config->interface_count && status < B_OK; i++) { usb_interface_info *interface = config->interface[i].active; if (interface == NULL) continue; usb_interface_descriptor *descriptor = interface->descr; if (descriptor == NULL) continue; if (descriptor->interface_class != USB_CDC_COMMUNICATION_INTERFACE_CLASS || descriptor->interface_subclass != USB_CDC_COMMUNICATION_INTERFACE_ACM_SUBCLASS) continue; // Found ACM Communication Interface! // Get functional descriptors of some interest, if any for (size_t j = 0; j < interface->generic_count; j++) { usb_generic_descriptor *generic = &interface->generic[j]->generic; switch (generic->data[0]) { case USB_CDC_CM_FUNCTIONAL_DESCRIPTOR: cmDesc = (usb_cdc_cm_functional_descriptor*)generic; break; case USB_CDC_ACM_FUNCTIONAL_DESCRIPTOR: break; case USB_CDC_UNION_FUNCTIONAL_DESCRIPTOR: unionDesc = (usb_cdc_union_functional_descriptor*)generic; break; } } masterIndex = unionDesc ? unionDesc->master_interface : i; slaveIndex = cmDesc ? cmDesc->data_interface : unionDesc ? unionDesc->slave_interfaces[0] : 0; TRACE("ACM device found on configuration #%d: master itf: %d, " "slave/data itf: %d\n", config->descr->configuration, masterIndex, slaveIndex); // Some ACM USB devices report the wrong unions which rightfully // breaks probing. Some drivers keep a list of these devices, // for now we just assume identical indexes are wrong. if (masterIndex == slaveIndex) { TRACE_ALWAYS("Command interface matches data interface, " "assuming broken union quirk!\n"); masterIndex = 0; slaveIndex = 1; } status = B_OK; break; } if (status == B_OK && masterIndex < config->interface_count) { // check that the indicated master interface fits our need usb_interface_info *interface = config->interface[masterIndex].active; usb_interface_descriptor *descriptor = interface->descr; if ((descriptor->interface_class == USB_CDC_COMMUNICATION_INTERFACE_CLASS || descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS) && interface->endpoint_count >= 1) { SetControlPipe(interface->endpoint[0].handle); SetInterruptBufferSize(interface->endpoint[0].descr->max_packet_size); } else { TRACE("Indicated command interface doesn't fit our needs!\n"); status = ENODEV; } } if (status == B_OK && slaveIndex < config->interface_count) { // check that the indicated slave interface fits our need usb_interface_info *interface = config->interface[slaveIndex].active; usb_interface_descriptor *descriptor = interface->descr; if (descriptor->interface_class == USB_CDC_DATA_INTERFACE_CLASS && interface->endpoint_count >= 2) { if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)) { SetWriteBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16)); SetWritePipe(interface->endpoint[0].handle); } else { SetReadBufferSize(ROUNDUP(interface->endpoint[0].descr->max_packet_size, 16)); SetReadPipe(interface->endpoint[0].handle); } if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) { SetReadBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16)); SetReadPipe(interface->endpoint[1].handle); } else { SetWriteBufferSize(ROUNDUP(interface->endpoint[1].descr->max_packet_size, 16)); SetWritePipe(interface->endpoint[1].handle); } } else { TRACE("Indicated data interface doesn't fit our needs!\n"); status = ENODEV; } } TRACE_FUNCRET("< ACMDevice::AddDevice() returns: 0x%08x\n", status); return status; } status_t ACMDevice::SetLineCoding(usb_cdc_line_coding *lineCoding) { TRACE_FUNCALLS("> ACMDevice::SetLineCoding(0x%08x, {%d, 0x%02x, 0x%02x, 0x%02x})\n", this, lineCoding->speed, lineCoding->stopbits, lineCoding->parity, lineCoding->databits); size_t length = 0; status_t status = gUSBModule->send_request(Device(), USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, USB_CDC_SET_LINE_CODING, 0, 0, sizeof(usb_cdc_line_coding), lineCoding, &length); TRACE_FUNCRET("< ACMDevice::SetLineCoding() returns: 0x%08x\n", status); return status; } status_t ACMDevice::SetControlLineState(uint16 state) { TRACE_FUNCALLS("> ACMDevice::SetControlLineState(0x%08x, 0x%04x)\n", this, state); size_t length = 0; status_t status = gUSBModule->send_request(Device(), USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, USB_CDC_SET_CONTROL_LINE_STATE, state, 0, 0, NULL, &length); TRACE_FUNCRET("< ACMDevice::SetControlLineState() returns: 0x%08x\n", status); return status; }