/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include #include #include #ifdef HAVE_REGEX_H #include #endif /* HAVE_REGEX_H */ #include "log.h" #include "strfmt.h" #include "parameters.h" #include "bitmask.h" #include "parse.h" #include "file.h" #include "utf8.h" #include "device.h" #include "timing.h" #include "async_wait.h" #include "async_alarm.h" #include "io_misc.h" #include "io_usb.h" #include "usb_internal.h" #include "usb_devices.h" #include "usb_serial.h" ssize_t usbControlRead ( UsbDevice *device, uint8_t recipient, uint8_t type, uint8_t request, uint16_t value, uint16_t index, void *buffer, uint16_t length, int timeout ) { return usbControlTransfer(device, UsbControlDirection_Input, recipient, type, request, value, index, buffer, length, timeout); } ssize_t usbControlWrite ( UsbDevice *device, uint8_t recipient, uint8_t type, uint8_t request, uint16_t value, uint16_t index, const void *buffer, uint16_t length, int timeout ) { return usbControlTransfer(device, UsbControlDirection_Output, recipient, type, request, value, index, (void *)buffer, length, timeout); } ssize_t usbGetDescriptor ( UsbDevice *device, unsigned char type, unsigned char number, unsigned int index, UsbDescriptor *descriptor, int timeout ) { return usbControlRead(device, UsbControlRecipient_Device, UsbControlType_Standard, UsbStandardRequest_GetDescriptor, (type << 8) | number, index, descriptor->bytes, sizeof(descriptor->bytes), timeout); } int usbGetDeviceDescriptor ( UsbDevice *device, UsbDeviceDescriptor *descriptor ) { UsbDescriptor desc; int size = usbGetDescriptor(device, UsbDescriptorType_Device, 0, 0, &desc, 1000); if (size != -1) { *descriptor = desc.device; } return size; } int usbGetLanguage ( UsbDevice *device, uint16_t *language, int timeout ) { UsbDescriptor descriptor; ssize_t size = usbGetDescriptor(device, UsbDescriptorType_String, 0, 0, &descriptor, timeout); if (size != -1) { if (size >= 4) { *language = getLittleEndian16(descriptor.string.wData[0]); logMessage(LOG_CATEGORY(USB_IO), "USB language: %02X", *language); return 1; } else { logMessage(LOG_ERR, "USB language code string too short: %"PRIssize, size); errno = EIO; } } else { logMessage(LOG_ERR, "USB language code string read error"); } return 0; } char * usbDecodeString (const UsbStringDescriptor *descriptor) { size_t count = (descriptor->bLength - 2) / sizeof(descriptor->wData[0]); char buffer[(count * UTF8_LEN_MAX) + 1]; const uint16_t *source = descriptor->wData; const uint16_t *end = source + count; char *target = buffer; while (source < end) { size_t length = convertWcharToUtf8(getLittleEndian16(*source++), target); target += length; } *target = 0; { char *string = strdup(buffer); if (!string) logMallocError(); return string; } } char * usbGetString ( UsbDevice *device, unsigned char number, int timeout ) { UsbDescriptor descriptor; if (!device->language) { if (!usbGetLanguage(device, &device->language, timeout)) { return NULL; } } if (usbGetDescriptor(device, UsbDescriptorType_String, number, device->language, &descriptor, timeout) == -1) { logMessage(LOG_ERR, "USB string read error: %u", number); return NULL; } return usbDecodeString(&descriptor.string); } char * usbGetManufacturer (UsbDevice *device, int timeout) { return usbGetString(device, device->descriptor.iManufacturer, timeout); } char * usbGetProduct (UsbDevice *device, int timeout) { return usbGetString(device, device->descriptor.iProduct, timeout); } char * usbGetSerialNumber (UsbDevice *device, int timeout) { return usbGetString(device, device->descriptor.iSerialNumber, timeout); } static size_t usbFormatLogSetupPacket (char *buffer, size_t size, const void *data) { const UsbSetupPacket *setup = data; size_t length; STR_BEGIN(buffer, size); STR_PRINTF("setup packet: Typ:%02X Req:%02X Val:%04X Idx:%04X Len:%04X", setup->bRequestType, setup->bRequest, getLittleEndian16(setup->wValue), getLittleEndian16(setup->wIndex), getLittleEndian16(setup->wLength)); length = STR_LENGTH; STR_END; return length; } void usbLogSetupPacket (const UsbSetupPacket *setup) { logData(LOG_CATEGORY(USB_IO), usbFormatLogSetupPacket, setup); } void usbMakeSetupPacket ( UsbSetupPacket *setup, uint8_t direction, uint8_t recipient, uint8_t type, uint8_t request, uint16_t value, uint16_t index, uint16_t length ) { setup->bRequestType = direction | recipient | type; setup->bRequest = request; putLittleEndian16(&setup->wValue, value); putLittleEndian16(&setup->wIndex, index); putLittleEndian16(&setup->wLength, length); usbLogSetupPacket(setup); } void usbLogEndpointData ( UsbEndpoint *endpoint, const char *label, const void *data, size_t size ) { logBytes(LOG_CATEGORY(USB_IO), "endpoint %02X %s", data, size, endpoint->descriptor->bEndpointAddress, label); } void usbLogString ( UsbDevice *device, unsigned char number, const char *label ) { if (number) { char *string = usbGetString(device, number, 1000); if (string) { logMessage(LOG_INFO, "USB: %s: %s", label, string); free(string); } } } int usbStringEquals (const char *reference, const char *value) { return strcmp(reference, value) == 0; } int usbStringMatches (const char *reference, const char *value) { int ok = 0; #ifdef REG_EXTENDED regex_t expression; if (regcomp(&expression, value, REG_EXTENDED|REG_NOSUB) == 0) { if (regexec(&expression, reference, 0, NULL, 0) == 0) { ok = 1; } regfree(&expression); } #endif /* REG_EXTENDED */ return ok; } int usbVerifyString ( UsbDevice *device, UsbStringVerifier verify, unsigned char index, const char *value ) { int ok = 0; if (!(value && *value)) return 1; if (index) { char *reference = usbGetString(device, index, 1000); if (reference) { if (verify(reference, value)) ok = 1; free(reference); } } return ok; } int usbVerifyManufacturerName (UsbDevice *device, const char *eRegExp) { return usbVerifyString(device, usbStringMatches, device->descriptor.iManufacturer, eRegExp); } int usbVerifyProductDescription (UsbDevice *device, const char *eRegExp) { return usbVerifyString(device, usbStringMatches, device->descriptor.iProduct, eRegExp); } int usbVerifySerialNumber (UsbDevice *device, const char *string) { return usbVerifyString(device, usbStringEquals, device->descriptor.iSerialNumber, string); } int usbParseVendorIdentifier (uint16_t *identifier, const char *string) { if (string && *string) { unsigned int value; if (isUnsignedInteger(&value, string)) { if ((value > 0) && (value <= UINT16_MAX)) { *identifier = value; return 1; } } logMessage(LOG_WARNING, "invalid USB vendor identifier: %s", string); return 0; } *identifier = 0; return 1; } int usbVerifyVendorIdentifier (const UsbDeviceDescriptor *descriptor, uint16_t identifier) { if (!identifier) return 1; return identifier == getLittleEndian16(descriptor->idVendor); } int usbParseProductIdentifier (uint16_t *identifier, const char *string) { if (string && *string) { unsigned int value; if (isUnsignedInteger(&value, string)) { if ((value > 0) && (value <= UINT16_MAX)) { *identifier = value; return 1; } } logMessage(LOG_WARNING, "invalid USB product identifier: %s", string); return 0; } *identifier = 0; return 1; } int usbVerifyProductIdentifier (const UsbDeviceDescriptor *descriptor, uint16_t identifier) { if (!identifier) return 1; return identifier == getLittleEndian16(descriptor->idProduct); } static int usbVerifyStrings ( UsbDevice *device, const char *const *strings, unsigned char number ) { if (!strings) return 1; if (!number) return 0; char *string = usbGetString(device, number, 1000); int matched = 0; if (string) { while (*strings) { if (strcmp(*strings, string) == 0) { matched = 1; break; } strings += 1; } free(string); } return matched; } const UsbDeviceDescriptor * usbDeviceDescriptor (UsbDevice *device) { return &device->descriptor; } int usbGetConfiguration ( UsbDevice *device, unsigned char *configuration ) { ssize_t size = usbControlRead(device, UsbControlRecipient_Device, UsbControlType_Standard, UsbStandardRequest_GetConfiguration, 0, 0, configuration, sizeof(*configuration), 1000); if (size != -1) return 1; logMessage(LOG_WARNING, "USB standard request not supported: get configuration"); return 0; } static void usbDeallocateConfigurationDescriptor (UsbDevice *device) { if (device->configuration) { free(device->configuration); device->configuration = NULL; } } const UsbConfigurationDescriptor * usbConfigurationDescriptor ( UsbDevice *device ) { if (!device->configuration) { unsigned char current; if (device->descriptor.bNumConfigurations < 2) { current = 1; } else if (!usbGetConfiguration(device, ¤t)) { current = 0; } if (current) { UsbDescriptor descriptor; unsigned char number; for (number=0; numberdescriptor.bNumConfigurations; number++) { int size = usbGetDescriptor(device, UsbDescriptorType_Configuration, number, 0, &descriptor, 1000); if (size == -1) { logMessage(LOG_WARNING, "USB configuration descriptor not readable: %d", number); } else if (descriptor.configuration.bConfigurationValue == current) { break; } } if (number < device->descriptor.bNumConfigurations) { int length = getLittleEndian16(descriptor.configuration.wTotalLength); UsbDescriptor *descriptors; if ((descriptors = malloc(length))) { ssize_t size; if (length > sizeof(descriptor)) { size = usbControlRead(device, UsbControlRecipient_Device, UsbControlType_Standard, UsbStandardRequest_GetDescriptor, (UsbDescriptorType_Configuration << 8) | number, 0, descriptors, length, 1000); } else { memcpy(descriptors, &descriptor, (size = length)); } if (size != -1) { device->configuration = &descriptors->configuration; } else { free(descriptors); } } else { logSystemError("USB configuration descriptor allocate"); } } else { logMessage(LOG_ERR, "USB configuration descriptor not found: %d", current); } } } return device->configuration; } int usbConfigureDevice ( UsbDevice *device, unsigned char configuration ) { usbCloseInterface(device); if (usbSetConfiguration(device, configuration)) { usbDeallocateConfigurationDescriptor(device); return 1; } { const UsbConfigurationDescriptor *descriptor = usbConfigurationDescriptor(device); if (descriptor) if (descriptor->bConfigurationValue == configuration) return 1; } return 0; } int usbNextDescriptor ( UsbDevice *device, const UsbDescriptor **descriptor ) { if (*descriptor) { const UsbDescriptor *next = (UsbDescriptor *)&(*descriptor)->bytes[(*descriptor)->header.bLength]; const UsbDescriptor *first = (UsbDescriptor *)device->configuration; unsigned int length = getLittleEndian16(first->configuration.wTotalLength); if ((&next->bytes[0] - &first->bytes[0]) >= length) return 0; if ((&next->bytes[next->header.bLength] - &first->bytes[0]) > length) return 0; *descriptor = next; } else if (usbConfigurationDescriptor(device)) { *descriptor = (UsbDescriptor *)device->configuration; } else { return 0; } return 1; } const UsbInterfaceDescriptor * usbInterfaceDescriptor ( UsbDevice *device, unsigned char interface, unsigned char alternative ) { const UsbDescriptor *descriptor = NULL; while (usbNextDescriptor(device, &descriptor)) { if (descriptor->interface.bDescriptorType == UsbDescriptorType_Interface) { if (descriptor->interface.bInterfaceNumber == interface) { if (descriptor->interface.bAlternateSetting == alternative) { return &descriptor->interface; } } } } logMessage(LOG_WARNING, "USB: interface descriptor not found: %d.%d", interface, alternative); errno = ENOENT; return NULL; } unsigned int usbAlternativeCount ( UsbDevice *device, unsigned char interface ) { unsigned int count = 0; const UsbDescriptor *descriptor = NULL; while (usbNextDescriptor(device, &descriptor)) { if (descriptor->interface.bDescriptorType == UsbDescriptorType_Interface) { if (descriptor->interface.bInterfaceNumber == interface) { count += 1; } } } return count; } const UsbEndpointDescriptor * usbEndpointDescriptor ( UsbDevice *device, unsigned char endpointAddress ) { const UsbDescriptor *descriptor = NULL; while (usbNextDescriptor(device, &descriptor)) { if (descriptor->endpoint.bDescriptorType == UsbDescriptorType_Endpoint) { if (descriptor->endpoint.bEndpointAddress == endpointAddress) { return &descriptor->endpoint; } } } logMessage(LOG_WARNING, "USB: endpoint descriptor not found: %02X", endpointAddress); errno = ENOENT; return NULL; } static void usbCancelInputMonitor (UsbEndpoint *endpoint) { if (endpoint->direction.input.pipe.monitor) { asyncCancelRequest(endpoint->direction.input.pipe.monitor); endpoint->direction.input.pipe.monitor = NULL; } } static inline int usbHaveInputPipe (UsbEndpoint *endpoint) { return endpoint->direction.input.pipe.output != INVALID_FILE_DESCRIPTOR; } static inline int usbHaveInputError (UsbEndpoint *endpoint) { return endpoint->direction.input.pipe.input == INVALID_FILE_DESCRIPTOR; } void usbSetEndpointInputError (UsbEndpoint *endpoint, int error) { if (!usbHaveInputError(endpoint)) { endpoint->direction.input.pipe.error = error; closeFile(&endpoint->direction.input.pipe.input); } } static int usbSetInputError (void *item, void *data) { UsbEndpoint *endpoint = item; const int *error = data; if (usbHaveInputPipe(endpoint)) { usbSetEndpointInputError(endpoint, *error); } return 0; } void usbSetDeviceInputError (UsbDevice *device, int error) { processQueue(device->endpoints, usbSetInputError, &error); } int usbEnqueueInput (UsbEndpoint *endpoint, const void *buffer, size_t length) { if (usbHaveInputError(endpoint)) { errno = EIO; return 0; } return writeFile(endpoint->direction.input.pipe.input, buffer, length) != -1; } void usbDestroyInputPipe (UsbEndpoint *endpoint) { usbCancelInputMonitor(endpoint); closeFile(&endpoint->direction.input.pipe.input); closeFile(&endpoint->direction.input.pipe.output); } int usbMakeInputPipe (UsbEndpoint *endpoint) { if (usbHaveInputPipe(endpoint)) return 1; if (createAnonymousPipe(&endpoint->direction.input.pipe.input, &endpoint->direction.input.pipe.output)) { if (setBlockingIo(endpoint->direction.input.pipe.output, 0)) { return 1; } } usbDestroyInputPipe(endpoint); return 0; } int usbMonitorInputPipe ( UsbDevice *device, unsigned char endpointNumber, AsyncMonitorCallback *callback, void *data ) { UsbEndpoint *endpoint = usbGetInputEndpoint(device, endpointNumber); if (endpoint) { if (usbHaveInputPipe(endpoint)) { usbCancelInputMonitor(endpoint); if (!callback) return 1; if (asyncMonitorFileInput(&endpoint->direction.input.pipe.monitor, endpoint->direction.input.pipe.output, callback, data)) { return 1; } } } return 0; } static void usbDeallocateEndpoint (void *item, void *data) { UsbEndpoint *endpoint = item; switch (USB_ENDPOINT_DIRECTION(endpoint->descriptor)) { case UsbEndpointDirection_Input: if (endpoint->direction.input.pending.alarm) { asyncCancelRequest(endpoint->direction.input.pending.alarm); endpoint->direction.input.pending.alarm = NULL; } if (endpoint->direction.input.pending.requests) { deallocateQueue(endpoint->direction.input.pending.requests); endpoint->direction.input.pending.requests = NULL; } if (endpoint->direction.input.completed.request) { free(endpoint->direction.input.completed.request); endpoint->direction.input.completed.request = NULL; } break; default: break; } if (endpoint->extension) { usbDeallocateEndpointExtension(endpoint->extension); endpoint->extension = NULL; } switch (USB_ENDPOINT_DIRECTION(endpoint->descriptor)) { case UsbEndpointDirection_Input: usbDestroyInputPipe(endpoint); break; default: break; } free(endpoint); } static int usbTestEndpoint (const void *item, void *data) { const UsbEndpoint *endpoint = item; const unsigned char *endpointAddress = data; return endpoint->descriptor->bEndpointAddress == *endpointAddress; } UsbEndpoint * usbGetEndpoint (UsbDevice *device, unsigned char endpointAddress) { UsbEndpoint *endpoint; const UsbEndpointDescriptor *descriptor; if ((endpoint = findItem(device->endpoints, usbTestEndpoint, &endpointAddress))) return endpoint; if ((descriptor = usbEndpointDescriptor(device, endpointAddress))) { { const char *direction; const char *transfer; switch (USB_ENDPOINT_DIRECTION(descriptor)) { default: direction = "?"; break; case UsbEndpointDirection_Input: direction = "in"; break; case UsbEndpointDirection_Output: direction = "out"; break; } switch (USB_ENDPOINT_TRANSFER(descriptor)) { default: transfer = "?"; break; case UsbEndpointTransfer_Control: transfer = "ctl"; break; case UsbEndpointTransfer_Isochronous: transfer = "iso"; break; case UsbEndpointTransfer_Bulk: transfer = "blk"; break; case UsbEndpointTransfer_Interrupt: transfer = "int"; break; } logMessage(LOG_CATEGORY(USB_IO), "ept=%02X dir=%s xfr=%s pkt=%d ivl=%dms", descriptor->bEndpointAddress, direction, transfer, getLittleEndian16(descriptor->wMaxPacketSize), descriptor->bInterval); } if ((endpoint = malloc(sizeof(*endpoint)))) { memset(endpoint, 0, sizeof(*endpoint)); endpoint->device = device; endpoint->descriptor = descriptor; endpoint->extension = NULL; endpoint->prepare = NULL; switch (USB_ENDPOINT_DIRECTION(endpoint->descriptor)) { case UsbEndpointDirection_Input: endpoint->direction.input.pending.requests = NULL; endpoint->direction.input.pending.alarm = NULL; endpoint->direction.input.pending.delay = 0; endpoint->direction.input.completed.request = NULL; endpoint->direction.input.completed.buffer = NULL; endpoint->direction.input.completed.length = 0; endpoint->direction.input.pipe.input = INVALID_FILE_DESCRIPTOR; endpoint->direction.input.pipe.output = INVALID_FILE_DESCRIPTOR; endpoint->direction.input.pipe.monitor = NULL; endpoint->direction.input.pipe.error = 0; break; } if (usbAllocateEndpointExtension(endpoint)) { if (enqueueItem(device->endpoints, endpoint)) { if (device->disableEndpointReset) { logMessage(LOG_CATEGORY(USB_IO), "endpoint reset disabled"); } else { usbClearHalt(device, endpoint->descriptor->bEndpointAddress); } if (!endpoint->prepare || endpoint->prepare(endpoint)) return endpoint; deleteItem(device->endpoints, endpoint); } usbDeallocateEndpointExtension(endpoint->extension); usbDestroyInputPipe(endpoint); } free(endpoint); } } return NULL; } UsbEndpoint * usbGetInputEndpoint (UsbDevice *device, unsigned char endpointNumber) { return usbGetEndpoint(device, endpointNumber|UsbEndpointDirection_Input); } UsbEndpoint * usbGetOutputEndpoint (UsbDevice *device, unsigned char endpointNumber) { return usbGetEndpoint(device, endpointNumber|UsbEndpointDirection_Output); } static int usbFinishEndpoint (void *item, void *data) { UsbEndpoint *endpoint = item; switch (USB_ENDPOINT_DIRECTION(endpoint->descriptor)) { case UsbEndpointDirection_Input: if (endpoint->direction.input.pending.requests) { deleteElements(endpoint->direction.input.pending.requests); } break; default: break; } return 0; } static void usbRemoveEndpoints (UsbDevice *device, int final) { if (device->endpoints) { processQueue(device->endpoints, usbFinishEndpoint, NULL); deleteElements(device->endpoints); if (final) { deallocateQueue(device->endpoints); device->endpoints = NULL; } } } static void usbDeallocateInputFilter (void *item, void *data) { UsbInputFilterEntry *entry = item; free(entry); } int usbAddInputFilter (UsbDevice *device, UsbInputFilter *filter) { UsbInputFilterEntry *entry; if ((entry = malloc(sizeof(*entry)))) { memset(entry, 0, sizeof(*entry)); entry->filter = filter; if (enqueueItem(device->inputFilters, entry)) return 1; free(entry); } return 0; } static int usbApplyInputFilter (void *item, void *data) { UsbInputFilterEntry *entry = item; return !entry->filter(data); } int usbApplyInputFilters (UsbEndpoint *endpoint, void *buffer, size_t size, ssize_t *length) { Queue *filters = endpoint->device->inputFilters; if (getQueueSize(filters) == 0) { usbLogEndpointData(endpoint, "input", buffer, *length); } else { usbLogEndpointData(endpoint, "unfiltered input", buffer, *length); UsbInputFilterData data = { .buffer = buffer, .size = size, .length = *length }; if (processQueue(filters, usbApplyInputFilter, &data)) { errno = EIO; return 0; } *length = data.length; usbLogEndpointData(endpoint, "filtered input", buffer, *length); } return 1; } void usbCloseInterface ( UsbDevice *device ) { usbRemoveEndpoints(device, 0); if (device->interface) { usbReleaseInterface(device, device->interface->bInterfaceNumber); device->interface = NULL; } } int usbOpenInterface ( UsbDevice *device, unsigned char interface, unsigned char alternative ) { const UsbInterfaceDescriptor *descriptor = usbInterfaceDescriptor(device, interface, alternative); if (!descriptor) return 0; if (descriptor == device->interface) return 1; if (device->interface) if (device->interface->bInterfaceNumber != interface) usbCloseInterface(device); if (!device->interface) if (!usbClaimInterface(device, interface)) return 0; if (usbAlternativeCount(device, interface) == 1) goto done; { unsigned char response[1]; ssize_t size = usbControlRead(device, UsbControlRecipient_Interface, UsbControlType_Standard, UsbStandardRequest_GetInterface, 0, interface, response, sizeof(response), 1000); if (size != -1) { if (response[0] == alternative) goto done; } else { logMessage(LOG_WARNING, "USB standard request not supported: get interface"); } } if (usbSetAlternative(device, interface, alternative)) goto done; if (!device->interface) usbReleaseInterface(device, interface); return 0; done: device->interface = descriptor; return 1; } void usbCloseDevice (UsbDevice *device) { if (device->serial.operations) { const UsbSerialOperations *uso = device->serial.operations; if (uso->disableAdapter) uso->disableAdapter(device); } usbCloseInterface(device); usbRemoveEndpoints(device, 1); if (device->inputFilters) { deallocateQueue(device->inputFilters); device->inputFilters = NULL; } if (device->serial.data) { device->serial.operations->destroyData(device->serial.data); device->serial.data = NULL; } if (device->extension) { usbDeallocateDeviceExtension(device->extension); device->extension = NULL; } usbDeallocateConfigurationDescriptor(device); free(device); } static UsbDevice * usbOpenDevice (UsbDeviceExtension *extension) { UsbDevice *device; if ((device = malloc(sizeof(*device)))) { memset(device, 0, sizeof(*device)); device->extension = extension; device->serial.operations = NULL; device->serial.data = NULL; device->resetDevice = 0; device->disableEndpointReset = 0; if ((device->endpoints = newQueue(usbDeallocateEndpoint, NULL))) { if ((device->inputFilters = newQueue(usbDeallocateInputFilter, NULL))) { if (usbReadDeviceDescriptor(device)) { if (device->descriptor.bDescriptorType == UsbDescriptorType_Device) { if (device->descriptor.bLength == UsbDescriptorSize_Device) { return device; } } } deallocateQueue(device->inputFilters); } usbRemoveEndpoints(device, 1); } free(device); } logSystemError("USB device open"); return NULL; } UsbDevice * usbTestDevice (UsbDeviceExtension *extension, UsbDeviceChooser *chooser, UsbChooseChannelData *data) { UsbDevice *device; if ((device = usbOpenDevice(extension))) { logMessage(LOG_CATEGORY(USB_IO), "testing device: vendor=%04X product=%04X", getLittleEndian16(device->descriptor.idVendor), getLittleEndian16(device->descriptor.idProduct)); if (chooser(device, data)) { usbLogString(device, device->descriptor.iManufacturer, "Manufacturer Name"); usbLogString(device, device->descriptor.iProduct, "Product Description"); usbLogString(device, device->descriptor.iSerialNumber, "Serial Number"); return device; } errno = ENOENT; device->extension = NULL; usbCloseDevice(device); } return NULL; } void usbLogInputProblem (UsbEndpoint *endpoint, const char *problem) { logMessage(LOG_WARNING, "USB input: %s: Ept:%02X", problem, endpoint->descriptor->bEndpointAddress); } static void usbDeallocatePendingInputRequest (void *item, void *data) { void *request = item; UsbEndpoint *endpoint = data; usbCancelRequest(endpoint->device, request); } static Element * usbAddPendingInputRequest (UsbEndpoint *endpoint) { void *request = usbSubmitRequest(endpoint->device, endpoint->descriptor->bEndpointAddress, NULL, getLittleEndian16(endpoint->descriptor->wMaxPacketSize), endpoint); if (request) { Element *element = enqueueItem(endpoint->direction.input.pending.requests, request); if (element) return element; usbCancelRequest(endpoint->device, request); } return NULL; } static void usbEnsurePendingInputRequests (UsbEndpoint *endpoint, int count) { int limit = USB_INPUT_INTERRUPT_REQUESTS_MAXIMUM; if ((count < 1) || (count > limit)) count = limit; endpoint->direction.input.pending.delay = 0; while (getQueueSize(endpoint->direction.input.pending.requests) < count) { if (!usbAddPendingInputRequest(endpoint)) { break; } } } ASYNC_ALARM_CALLBACK(usbHandleSchedulePendingInputRequest) { UsbEndpoint *endpoint = parameters->data; asyncDiscardHandle(endpoint->direction.input.pending.alarm); endpoint->direction.input.pending.alarm = NULL; usbAddPendingInputRequest(endpoint); } static void usbSchedulePendingInputRequest (UsbEndpoint *endpoint) { if (!endpoint->direction.input.pending.alarm) { int *delay = &endpoint->direction.input.pending.delay; if (!*delay) *delay = 1; *delay = MIN(*delay, USB_INPUT_INTERRUPT_DELAY_MAXIMUM); asyncNewRelativeAlarm(&endpoint->direction.input.pending.alarm, *delay, usbHandleSchedulePendingInputRequest, endpoint); *delay += 1; } } int usbHandleInputResponse (UsbEndpoint *endpoint, const void *buffer, size_t length) { int requestsLeft = getQueueSize(endpoint->direction.input.pending.requests); if (length > 0) { if (!usbEnqueueInput(endpoint, buffer, length)) { usbLogInputProblem(endpoint, "data not enqueued"); return 0; } usbEnsurePendingInputRequests(endpoint, requestsLeft+2); return 1; } if (requestsLeft == 0) { usbSchedulePendingInputRequest(endpoint); } return 1; } void usbBeginInput ( UsbDevice *device, unsigned char endpointNumber ) { UsbEndpoint *endpoint = usbGetInputEndpoint(device, endpointNumber); if (endpoint) { if (!endpoint->direction.input.pending.requests) { if ((endpoint->direction.input.pending.requests = newQueue(usbDeallocatePendingInputRequest, NULL))) { setQueueData(endpoint->direction.input.pending.requests, endpoint); } } if (endpoint->direction.input.pending.requests) { usbEnsurePendingInputRequests(endpoint, 0); } } } static int usbGetPollInterval (UsbEndpoint *endpoint) { int interval = endpoint->descriptor->bInterval; if (interval > 0) { if (getLittleEndian16(endpoint->device->descriptor.bcdUSB) >= UsbSpecificationVersion_2_0) { interval = (1 << (interval - 1)) / 8; } } return interval; } int usbAwaitInput ( UsbDevice *device, unsigned char endpointNumber, int timeout ) { UsbEndpoint *endpoint; int retryInterval; if (!(endpoint = usbGetInputEndpoint(device, endpointNumber))) { return 0; } if (usbHaveInputPipe(endpoint)) { if (usbHaveInputError(endpoint)) { errno = endpoint->direction.input.pipe.error; return 0; } return awaitFileInput(endpoint->direction.input.pipe.output, timeout); } if (endpoint->direction.input.completed.request) { return 1; } if (!timeout) { errno = EAGAIN; return 0; } retryInterval = usbGetPollInterval(endpoint); retryInterval = MAX(USB_INPUT_AWAIT_RETRY_INTERVAL_MINIMUM, retryInterval); if (!(endpoint->direction.input.pending.requests && getQueueSize(endpoint->direction.input.pending.requests))) { int size = getLittleEndian16(endpoint->descriptor->wMaxPacketSize); unsigned char *buffer = malloc(size); if (buffer) { TimePeriod period; startTimePeriod(&period, timeout); while (1) { ssize_t count = usbReadEndpoint(device, endpointNumber, buffer, size, 20); if (count != -1) { if (count) { endpoint->direction.input.completed.request = buffer; endpoint->direction.input.completed.buffer = buffer; endpoint->direction.input.completed.length = count; return 1; } errno = EAGAIN; } #ifdef ETIMEDOUT if (errno == ETIMEDOUT) errno = EAGAIN; #endif /* ETIMEDOUT */ if (errno != EAGAIN) break; if (afterTimePeriod(&period, NULL)) break; asyncWait(retryInterval); } free(buffer); } else { logMallocError(); } return 0; } { TimePeriod period; startTimePeriod(&period, timeout); while (1) { UsbResponse response; void *request; while (!(request = usbReapResponse(device, endpointNumber | UsbEndpointDirection_Input, &response, 0))) { if (errno != EAGAIN) return 0; if (afterTimePeriod(&period, NULL)) return 0; asyncWait(retryInterval); } usbAddPendingInputRequest(endpoint); deleteItem(endpoint->direction.input.pending.requests, request); if (response.count > 0) { endpoint->direction.input.completed.request = request; endpoint->direction.input.completed.buffer = response.buffer; endpoint->direction.input.completed.length = response.count; return 1; } free(request); } } } ssize_t usbReadData ( UsbDevice *device, unsigned char endpointNumber, void *buffer, size_t length, int initialTimeout, int subsequentTimeout ) { UsbEndpoint *endpoint = usbGetInputEndpoint(device, endpointNumber); if (endpoint) { unsigned char *bytes = buffer; unsigned char *target = bytes; if (usbHaveInputPipe(endpoint)) { if (usbHaveInputError(endpoint)) { errno = endpoint->direction.input.pipe.error; endpoint->direction.input.pipe.error = EAGAIN; return -1; } return readFile(endpoint->direction.input.pipe.output, buffer, length, initialTimeout, subsequentTimeout); } while (length > 0) { int timeout = (target != bytes)? subsequentTimeout: initialTimeout? initialTimeout: USB_INPUT_READ_INITIAL_TIMEOUT_DEFAULT; if (!usbAwaitInput(device, endpointNumber, timeout)) { if (errno == EAGAIN) break; return -1; } { size_t count = endpoint->direction.input.completed.length; if (length < count) count = length; memcpy(target, endpoint->direction.input.completed.buffer, count); if ((endpoint->direction.input.completed.length -= count)) { endpoint->direction.input.completed.buffer += count; } else { endpoint->direction.input.completed.buffer = NULL; free(endpoint->direction.input.completed.request); endpoint->direction.input.completed.request = NULL; } target += count; length -= count; } } return target - bytes; } return -1; } ssize_t usbWriteData ( UsbDevice *device, unsigned char endpointNumber, const void *data, size_t length, int timeout ) { UsbEndpoint *endpoint = usbGetOutputEndpoint(device, endpointNumber); if (endpoint) { const uint16_t size = getLittleEndian16(endpoint->descriptor->wMaxPacketSize); const unsigned char *from = data; const unsigned char *const end = from + length; while (from < end) { size_t count = MIN((end - from), size); ssize_t result = usbWriteEndpoint(device, endpointNumber, from, count, timeout); if (result == -1) return result; from += result; } return length; } return -1; } static int usbPrepareChannel (UsbChannel *channel) { const UsbChannelDefinition *definition = channel->definition; UsbDevice *device = channel->device; device->resetDevice = definition->resetDevice; device->disableEndpointReset = definition->disableEndpointReset; if (definition->disableAutosuspend) { logMessage(LOG_CATEGORY(USB_IO), "disabling autosuspend"); usbDisableAutosuspend(device); } if (device->resetDevice) { usbResetDevice(device); } if (usbConfigureDevice(device, definition->configuration)) { if (usbOpenInterface(device, definition->interface, definition->alternative)) { int ok = 1; if (ok) { if (!usbSetSerialOperations(device)) { ok = 0; } else if (device->serial.operations) { logMessage(LOG_CATEGORY(USB_IO), "serial adapter: %s", device->serial.operations->name); } } if (ok) { if (device->serial.operations) { if (device->serial.operations->enableAdapter) { if (!device->serial.operations->enableAdapter(device)) { ok = 0; } } } } if (ok) { if (definition->serial) { if (!usbSetSerialParameters(device, definition->serial)) { ok = 0; } } } if (ok) { if (definition->inputEndpoint) { UsbEndpoint *endpoint = usbGetInputEndpoint(device, definition->inputEndpoint); if (!endpoint) { ok = 0; } else if ((USB_ENDPOINT_TRANSFER(endpoint->descriptor) == UsbEndpointTransfer_Interrupt) || usbHaveInputPipe(endpoint)) { usbBeginInput(device, definition->inputEndpoint); } } } if (ok) { if (definition->outputEndpoint) { UsbEndpoint *endpoint = usbGetOutputEndpoint(device, definition->outputEndpoint); if (!endpoint) { ok = 0; } } } if (ok) return 1; usbCloseInterface(device); } } return 0; } static int usbVerifyInterface (UsbDevice *device, const UsbChannelDefinition *definition) { const UsbInterfaceDescriptor *interface = usbInterfaceDescriptor(device, definition->interface, definition->alternative); if (!interface) return 0; BITMASK(endpoints, 0X100, char); BITMASK_ZERO(endpoints); { const UsbDescriptor *descriptor = (const UsbDescriptor *)interface; while (usbNextDescriptor(device, &descriptor)) { uint8_t type = descriptor->header.bDescriptorType; if (type == UsbDescriptorType_Interface) break; if (type != UsbDescriptorType_Endpoint) continue; BITMASK_SET(endpoints, descriptor->endpoint.bEndpointAddress); } } if (definition->inputEndpoint) { if (!BITMASK_TEST(endpoints, (definition->inputEndpoint | UsbEndpointDirection_Input))) { return 0; } } if (definition->outputEndpoint) { if (!BITMASK_TEST(endpoints, (definition->outputEndpoint | UsbEndpointDirection_Output))) { return 0; } } return 1; } struct UsbChooseChannelDataStruct { const UsbChannelDefinition *definition; const char *serialNumber; uint16_t vendorIdentifier; uint16_t productIdentifier; unsigned genericDevices:1; }; static int usbChooseChannel (UsbDevice *device, UsbChooseChannelData *data) { const UsbDeviceDescriptor *descriptor = &device->descriptor; if (!(descriptor->iManufacturer || descriptor->iProduct || descriptor->iSerialNumber)) { UsbDeviceDescriptor actualDescriptor; ssize_t result = usbGetDeviceDescriptor(device, &actualDescriptor); if (result == UsbDescriptorSize_Device) { logMessage(LOG_CATEGORY(USB_IO), "using actual device descriptor"); device->descriptor = actualDescriptor; } } { uint16_t vendor = getLittleEndian16(descriptor->idVendor); uint16_t product = getLittleEndian16(descriptor->idProduct); const char *const *drivers = usbGetDriverCodes(vendor, product); if (!drivers) return 0; } for (const UsbChannelDefinition *definition = data->definition; definition->vendor; definition+=1) { if (definition->version && (definition->version != getLittleEndian16(descriptor->bcdUSB))) continue; if (!USB_IS_PRODUCT(descriptor, definition->vendor, definition->product)) continue; if (!data->genericDevices) { const UsbSerialAdapter *adapter = usbFindSerialAdapter(descriptor); if (adapter && adapter->generic) continue; } if (!usbVerifyVendorIdentifier(descriptor, data->vendorIdentifier)) continue; if (!usbVerifyProductIdentifier(descriptor, data->productIdentifier)) continue; if (!usbVerifySerialNumber(device, data->serialNumber)) continue; if (!usbVerifyStrings(device, definition->manufacturers, descriptor->iManufacturer)) continue; if (!usbVerifyStrings(device, definition->products, descriptor->iProduct)) continue; if (definition->verifyInterface) { if (!usbConfigureDevice(device, definition->configuration)) continue; if (!usbVerifyInterface(device, definition)) continue; } data->definition = definition; return 1; } return 0; } static UsbChannel * usbNewChannel (UsbChooseChannelData *data) { UsbChannel *channel; if ((channel = malloc(sizeof(*channel)))) { memset(channel, 0, sizeof(*channel)); if ((channel->device = usbFindDevice(usbChooseChannel, data))) { channel->definition = data->definition; return channel; } free(channel); } else { logMallocError(); } return NULL; } typedef enum { USB_CHAN_SERIAL_NUMBER, USB_CHAN_VENDOR_IDENTIFIER, USB_CHAN_PRODUCT_IDENTIFIER, USB_CHAN_GENERIC_DEVICES } UsbChannelParameter; static const char *const usbChannelParameters[] = { "serialNumber", "vendorIdentifier", "productIdentifier", "genericDevices", NULL }; static char ** usbGetChannelParameters (const char *identifier) { if (!identifier) identifier = ""; return getDeviceParameters(usbChannelParameters, identifier); } UsbChannel * usbOpenChannel (const UsbChannelDefinition *definitions, const char *identifier) { UsbChannel *channel = NULL; char **parameters = usbGetChannelParameters(identifier); if (parameters) { int ok = 1; UsbChooseChannelData choose = { .definition = definitions, .serialNumber = parameters[USB_CHAN_SERIAL_NUMBER] }; if (!usbParseVendorIdentifier(&choose.vendorIdentifier, parameters[USB_CHAN_VENDOR_IDENTIFIER])) ok = 0; if (!usbParseProductIdentifier(&choose.productIdentifier, parameters[USB_CHAN_PRODUCT_IDENTIFIER])) ok = 0; { const char *parameter = parameters[USB_CHAN_GENERIC_DEVICES]; if (!(parameter && *parameter)) { choose.genericDevices = 1; } else { unsigned int flag; if (validateYesNo(&flag, parameter)) { choose.genericDevices = flag; } else { logMessage(LOG_WARNING, "invalid generic devices option: %s", parameter); ok = 0; } } } if (ok) { if (!(channel = usbNewChannel(&choose))) { logMessage(LOG_CATEGORY(USB_IO), "device not found%s%s", (*identifier? ": ": ""), identifier); } } deallocateStrings(parameters); } if (channel) { if (usbPrepareChannel(channel)) { return channel; } usbCloseChannel(channel); } return NULL; } void usbCloseChannel (UsbChannel *channel) { usbCloseDevice(channel->device); free(channel); } const char * usbMakeChannelIdentifier (UsbChannel *channel, char *buffer, size_t size) { UsbDevice *device = channel->device; UsbDeviceDescriptor descriptor; if (!usbGetDeviceDescriptor(device, &descriptor)) return NULL; size_t length; STR_BEGIN(buffer, size); STR_PRINTF("%s%c", USB_DEVICE_QUALIFIER, PARAMETER_QUALIFIER_CHARACTER); { uint16_t vendorIdentifier = getLittleEndian16(descriptor.idVendor); if (vendorIdentifier) { STR_PRINTF( "%s%c0X%04X%c", usbChannelParameters[USB_CHAN_VENDOR_IDENTIFIER], PARAMETER_ASSIGNMENT_CHARACTER, vendorIdentifier, DEVICE_PARAMETER_SEPARATOR ); } } { uint16_t productIdentifier = getLittleEndian16(descriptor.idProduct); if (productIdentifier) { STR_PRINTF( "%s%c0X%04X%c", usbChannelParameters[USB_CHAN_PRODUCT_IDENTIFIER], PARAMETER_ASSIGNMENT_CHARACTER, productIdentifier, DEVICE_PARAMETER_SEPARATOR ); } } { char *serialNumber = usbGetSerialNumber(device, 1000); if (serialNumber) { if (!strchr(serialNumber, DEVICE_PARAMETER_SEPARATOR)) { STR_PRINTF( "%s%c%s%c", usbChannelParameters[USB_CHAN_SERIAL_NUMBER], PARAMETER_ASSIGNMENT_CHARACTER, serialNumber, DEVICE_PARAMETER_SEPARATOR ); } free(serialNumber); } } length = STR_LENGTH; STR_END; { char *last = &buffer[length] - 1; if (*last == DEVICE_PARAMETER_SEPARATOR) *last = 0; } return buffer; } static int usbCompareDeviceEntries (const void *element1, const void *element2) { const UsbDeviceEntry *entry1 = element1; const UsbDeviceEntry *entry2 = element2; if (entry1->vendorIdentifier < entry2->vendorIdentifier) return -1; if (entry1->vendorIdentifier > entry2->vendorIdentifier) return 1; if (entry1->productIdentifier < entry2->productIdentifier) return -1; if (entry1->productIdentifier > entry2->productIdentifier) return 1; return 0; } static int usbSearchDeviceEntry (const void *target, const void *element) { const UsbDeviceEntry *entry1 = target; const UsbDeviceEntry *entry2 = element; return usbCompareDeviceEntries(entry1, entry2); } const char *const * usbGetDriverCodes (uint16_t vendor, uint16_t product) { const UsbDeviceEntry target = { .vendorIdentifier = vendor, .productIdentifier = product }; const UsbDeviceEntry *entry = bsearch(&target, usbDeviceTable, usbDeviceCount, sizeof(usbDeviceTable[0]), usbSearchDeviceEntry); return entry? entry->driverCodes: NULL; } int isUsbDeviceIdentifier (const char **identifier) { return hasQualifier(identifier, USB_DEVICE_QUALIFIER); }