| /* |
| * 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-2023 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 <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <mach/mach.h> |
| #include <IOKit/IOKitLib.h> |
| #include <IOKit/IOCFPlugIn.h> |
| #include <IOKit/usb/IOUSBLib.h> |
| |
| #include "log.h" |
| #include "io_usb.h" |
| #include "usb_internal.h" |
| #include "system_darwin.h" |
| |
| typedef struct { |
| UsbEndpoint *endpoint; |
| void *context; |
| void *buffer; |
| size_t length; |
| |
| IOReturn result; |
| ssize_t count; |
| } UsbAsynchronousRequest; |
| |
| struct UsbDeviceExtensionStruct { |
| IOUSBDeviceInterface182 **device; |
| unsigned deviceOpened:1; |
| |
| IOUSBInterfaceInterface190 **interface; |
| unsigned interfaceOpened:1; |
| UInt8 pipeCount; |
| |
| CFRunLoopSourceRef runloopSource; |
| }; |
| |
| struct UsbEndpointExtensionStruct { |
| UsbEndpoint *endpoint; |
| Queue *completedRequests; |
| |
| UInt8 pipeNumber; |
| UInt8 endpointNumber; |
| UInt8 transferDirection; |
| UInt8 transferMode; |
| UInt8 pollInterval; |
| UInt16 packetSize; |
| }; |
| |
| static void |
| setUsbError (long int result, const char *action) { |
| switch (result) { |
| default: setDarwinSystemError(result); break; |
| |
| //MAP_DARWIN_ERROR(kIOUSBUnknownPipeErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBTooManyPipesErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBNoAsyncPortErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBNotEnoughPipesErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBNotEnoughPowerErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBEndpointNotFound, ) |
| //MAP_DARWIN_ERROR(kIOUSBConfigNotFound, ) |
| MAP_DARWIN_ERROR(kIOUSBTransactionTimeout, ETIMEDOUT) |
| //MAP_DARWIN_ERROR(kIOUSBTransactionReturned, ) |
| //MAP_DARWIN_ERROR(kIOUSBPipeStalled, ) |
| //MAP_DARWIN_ERROR(kIOUSBInterfaceNotFound, ) |
| //MAP_DARWIN_ERROR(kIOUSBLowLatencyBufferNotPreviouslyAllocated, ) |
| //MAP_DARWIN_ERROR(kIOUSBLowLatencyFrameListNotPreviouslyAllocated, ) |
| //MAP_DARWIN_ERROR(kIOUSBHighSpeedSplitError, ) |
| //MAP_DARWIN_ERROR(kIOUSBLinkErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBNotSent2Err, ) |
| //MAP_DARWIN_ERROR(kIOUSBNotSent1Err, ) |
| //MAP_DARWIN_ERROR(kIOUSBBufferUnderrunErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBBufferOverrunErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBReserved2Err, ) |
| //MAP_DARWIN_ERROR(kIOUSBReserved1Err, ) |
| //MAP_DARWIN_ERROR(kIOUSBWrongPIDErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBPIDCheckErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBDataToggleErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBBitstufErr, ) |
| //MAP_DARWIN_ERROR(kIOUSBCRCErr, ) |
| } |
| |
| if (action) { |
| logMessage(LOG_WARNING, "Darwin error 0X%lX.", result); |
| logSystemError(action); |
| } |
| } |
| |
| static int |
| openDevice (UsbDevice *device, int seize) { |
| UsbDeviceExtension *devx = device->extension; |
| IOReturn result; |
| |
| if (!devx->deviceOpened) { |
| const char *action = "opened"; |
| int level = LOG_INFO; |
| |
| result = (*devx->device)->USBDeviceOpen(devx->device); |
| if (result != kIOReturnSuccess) { |
| setUsbError(result, "USB device open"); |
| if ((result != kIOReturnExclusiveAccess) || !seize) return 0; |
| |
| result = (*devx->device)->USBDeviceOpenSeize(devx->device); |
| if (result != kIOReturnSuccess) { |
| setUsbError(result, "USB device seize"); |
| return 0; |
| } |
| |
| action = "seized"; |
| level = LOG_NOTICE; |
| } |
| |
| logMessage(level, "USB device %s: vendor=%04X product=%04X", |
| action, device->descriptor.idVendor, device->descriptor.idProduct); |
| devx->deviceOpened = 1; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| unsetInterface (UsbDeviceExtension *devx) { |
| int ok = 1; |
| |
| if (devx->interface) { |
| IOReturn result; |
| |
| if (devx->interfaceOpened) { |
| if (devx->runloopSource) { |
| { |
| int pipe; |
| for (pipe=1; pipe<=devx->pipeCount; ++pipe) { |
| result = (*devx->interface)->AbortPipe(devx->interface, pipe); |
| if (result != kIOReturnSuccess) { |
| setUsbError(result, "USB pipe abort"); |
| } |
| } |
| } |
| |
| removeRunLoopSource(devx->runloopSource); |
| } |
| |
| result = (*devx->interface)->USBInterfaceClose(devx->interface); |
| if (result != kIOReturnSuccess) { |
| setUsbError(result, "USB interface close"); |
| ok = 0; |
| } |
| devx->interfaceOpened = 0; |
| } |
| |
| (*devx->interface)->Release(devx->interface); |
| devx->interface = NULL; |
| } |
| |
| return ok; |
| } |
| |
| static int |
| isInterface (IOUSBInterfaceInterface190 **interface, UInt8 number) { |
| IOReturn result; |
| UInt8 num; |
| |
| result = (*interface)->GetInterfaceNumber(interface, &num); |
| if (result != kIOReturnSuccess) { |
| setUsbError(result, "USB interface number query"); |
| } else if (num == number) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| setInterface (UsbDeviceExtension *devx, UInt8 number) { |
| int found = 0; |
| IOReturn result; |
| io_iterator_t iterator = 0; |
| |
| if (devx->interface) |
| if (isInterface(devx->interface, number)) |
| return 1; |
| |
| { |
| IOUSBFindInterfaceRequest request; |
| |
| request.bInterfaceClass = kIOUSBFindInterfaceDontCare; |
| request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; |
| request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; |
| request.bAlternateSetting = kIOUSBFindInterfaceDontCare; |
| |
| result = (*devx->device)->CreateInterfaceIterator(devx->device, &request, &iterator); |
| } |
| |
| if ((result == kIOReturnSuccess) && iterator) { |
| io_service_t service; |
| |
| while ((service = IOIteratorNext(iterator))) { |
| IOCFPlugInInterface **plugin = NULL; |
| SInt32 score; |
| |
| result = IOCreatePlugInInterfaceForService(service, |
| kIOUSBInterfaceUserClientTypeID, |
| kIOCFPlugInInterfaceID, |
| &plugin, &score); |
| IOObjectRelease(service); |
| service = 0; |
| |
| if ((result == kIOReturnSuccess) && plugin) { |
| IOUSBInterfaceInterface190 **interface = NULL; |
| |
| result = (*plugin)->QueryInterface(plugin, |
| CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID190), |
| (LPVOID)&interface); |
| (*plugin)->Release(plugin); |
| plugin = NULL; |
| |
| if ((result == kIOReturnSuccess) && interface) { |
| if (isInterface(interface, number)) { |
| unsetInterface(devx); |
| devx->interface = interface; |
| found = 1; |
| break; |
| } |
| |
| (*interface)->Release(interface); |
| interface = NULL; |
| } else { |
| setUsbError(result, "USB interface interface create"); |
| } |
| } else { |
| setUsbError(result, "USB interface service plugin create"); |
| } |
| } |
| if (!found) logMessage(LOG_ERR, "USB interface not found: %d", number); |
| |
| IOObjectRelease(iterator); |
| iterator = 0; |
| } else { |
| setUsbError(result, "USB interface iterator create"); |
| } |
| |
| return found; |
| } |
| |
| static void |
| usbAsynchronousRequestCallback (void *context, IOReturn result, void *arg) { |
| UsbAsynchronousRequest *request = context; |
| UsbEndpoint *endpoint = request->endpoint; |
| UsbEndpointExtension *eptx = endpoint->extension; |
| |
| request->result = result; |
| request->count = (intptr_t)arg; |
| |
| if (!enqueueItem(eptx->completedRequests, request)) { |
| logSystemError("USB completed request enqueue"); |
| free(request); |
| } |
| } |
| |
| int |
| usbDisableAutosuspend (UsbDevice *device) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| int |
| usbSetConfiguration (UsbDevice *device, unsigned char configuration) { |
| UsbDeviceExtension *devx = device->extension; |
| |
| if (openDevice(device, 1)) { |
| UInt8 arg = configuration; |
| IOReturn result = (*devx->device)->SetConfiguration(devx->device, arg); |
| if (result == kIOReturnSuccess) return 1; |
| setUsbError(result, "USB configuration set"); |
| } |
| |
| return 0; |
| } |
| |
| int |
| usbClaimInterface (UsbDevice *device, unsigned char interface) { |
| UsbDeviceExtension *devx = device->extension; |
| IOReturn result; |
| |
| if (setInterface(devx, interface)) { |
| if (devx->interfaceOpened) return 1; |
| |
| result = (*devx->interface)->USBInterfaceOpen(devx->interface); |
| if (result == kIOReturnSuccess) { |
| result = (*devx->interface)->GetNumEndpoints(devx->interface, &devx->pipeCount); |
| if (result == kIOReturnSuccess) { |
| devx->interfaceOpened = 1; |
| return 1; |
| } else { |
| setUsbError(result, "USB pipe count query"); |
| } |
| |
| (*devx->interface)->USBInterfaceClose(devx->interface); |
| } else { |
| setUsbError(result, "USB interface open"); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| usbReleaseInterface (UsbDevice *device, unsigned char interface) { |
| UsbDeviceExtension *devx = device->extension; |
| return setInterface(devx, interface) && unsetInterface(devx); |
| } |
| |
| int |
| usbSetAlternative ( |
| UsbDevice *device, |
| unsigned char interface, |
| unsigned char alternative |
| ) { |
| UsbDeviceExtension *devx = device->extension; |
| |
| if (setInterface(devx, interface)) { |
| IOReturn result; |
| UInt8 arg; |
| |
| result = (*devx->interface)->GetAlternateSetting(devx->interface, &arg); |
| if (result == kIOReturnSuccess) { |
| if (arg == alternative) return 1; |
| |
| arg = alternative; |
| result = (*devx->interface)->SetAlternateInterface(devx->interface, arg); |
| if (result == kIOReturnSuccess) return 1; |
| setUsbError(result, "USB alternative set"); |
| } else { |
| setUsbError(result, "USB alternative get"); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| usbResetDevice (UsbDevice *device) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| int |
| usbClearHalt (UsbDevice *device, unsigned char endpointAddress) { |
| UsbDeviceExtension *devx = device->extension; |
| UsbEndpoint *endpoint; |
| |
| if ((endpoint = usbGetEndpoint(device, endpointAddress))) { |
| UsbEndpointExtension *eptx = endpoint->extension; |
| IOReturn result; |
| |
| result = (*devx->interface)->ClearPipeStallBothEnds(devx->interface, eptx->pipeNumber); |
| if (result == kIOReturnSuccess) return 1; |
| setUsbError(result, "USB endpoint clear"); |
| } |
| |
| return 0; |
| } |
| |
| ssize_t |
| usbControlTransfer ( |
| UsbDevice *device, |
| uint8_t direction, |
| uint8_t recipient, |
| uint8_t type, |
| uint8_t request, |
| uint16_t value, |
| uint16_t index, |
| void *buffer, |
| uint16_t length, |
| int timeout |
| ) { |
| UsbDeviceExtension *devx = device->extension; |
| IOReturn result; |
| IOUSBDevRequestTO arg; |
| |
| arg.bmRequestType = direction | recipient | type; |
| arg.bRequest = request; |
| arg.wValue = value; |
| arg.wIndex = index; |
| arg.wLength = length; |
| |
| arg.pData = buffer; |
| arg.noDataTimeout = timeout; |
| arg.completionTimeout = timeout; |
| |
| result = (*devx->device)->DeviceRequestTO(devx->device, &arg); |
| if (result == kIOReturnSuccess) return arg.wLenDone; |
| setUsbError(result, "USB control transfer"); |
| return -1; |
| } |
| |
| void * |
| usbSubmitRequest ( |
| UsbDevice *device, |
| unsigned char endpointAddress, |
| void *buffer, |
| size_t length, |
| void *context |
| ) { |
| UsbDeviceExtension *devx = device->extension; |
| UsbEndpoint *endpoint; |
| |
| if ((endpoint = usbGetEndpoint(device, endpointAddress))) { |
| UsbEndpointExtension *eptx = endpoint->extension; |
| IOReturn result; |
| UsbAsynchronousRequest *request; |
| |
| if (!devx->runloopSource) { |
| result = (*devx->interface)->CreateInterfaceAsyncEventSource(devx->interface, |
| &devx->runloopSource); |
| if (result != kIOReturnSuccess) { |
| setUsbError(result, "USB interface event source create"); |
| return NULL; |
| } |
| |
| addRunLoopSource(devx->runloopSource); |
| } |
| |
| if ((request = malloc(sizeof(*request) + length))) { |
| request->endpoint = endpoint; |
| request->context = context; |
| request->buffer = (request->length = length)? (request + 1): NULL; |
| |
| switch (eptx->transferDirection) { |
| case kUSBIn: |
| result = (*devx->interface)->ReadPipeAsync(devx->interface, eptx->pipeNumber, |
| request->buffer, request->length, |
| usbAsynchronousRequestCallback, request); |
| if (result == kIOReturnSuccess) return request; |
| setUsbError(result, "USB endpoint asynchronous read"); |
| break; |
| |
| case kUSBOut: |
| if (request->buffer) memcpy(request->buffer, buffer, length); |
| result = (*devx->interface)->WritePipeAsync(devx->interface, eptx->pipeNumber, |
| request->buffer, request->length, |
| usbAsynchronousRequestCallback, request); |
| if (result == kIOReturnSuccess) return request; |
| setUsbError(result, "USB endpoint asynchronous write"); |
| break; |
| |
| default: |
| logMessage(LOG_ERR, "USB endpoint direction not suppported: %d", |
| eptx->transferDirection); |
| errno = ENOSYS; |
| break; |
| } |
| |
| free(request); |
| } else { |
| logSystemError("USB asynchronous request allocate"); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| int |
| usbCancelRequest (UsbDevice *device, void *request) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| void * |
| usbReapResponse ( |
| UsbDevice *device, |
| unsigned char endpointAddress, |
| UsbResponse *response, |
| int wait |
| ) { |
| UsbEndpoint *endpoint; |
| |
| if ((endpoint = usbGetEndpoint(device, endpointAddress))) { |
| UsbEndpointExtension *eptx = endpoint->extension; |
| UsbAsynchronousRequest *request; |
| |
| while (!(request = dequeueItem(eptx->completedRequests))) { |
| switch (executeRunLoop((wait? 60: 0))) { |
| case kCFRunLoopRunTimedOut: |
| if (wait) continue; |
| case kCFRunLoopRunFinished: |
| errno = EAGAIN; |
| goto none; |
| |
| case kCFRunLoopRunStopped: |
| case kCFRunLoopRunHandledSource: |
| default: |
| continue; |
| } |
| } |
| |
| response->context = request->context; |
| response->buffer = request->buffer; |
| response->size = request->length; |
| |
| if (request->result == kIOReturnSuccess) { |
| response->error = 0; |
| response->count = request->count; |
| |
| if (!usbApplyInputFilters(endpoint, response->buffer, response->size, &response->count)) { |
| response->error = EIO; |
| response->count = -1; |
| } |
| } else { |
| setUsbError(request->result, "USB asynchronous response"); |
| response->error = errno; |
| response->count = -1; |
| } |
| |
| return request; |
| } |
| |
| none: |
| return NULL; |
| } |
| |
| int |
| usbMonitorInputEndpoint ( |
| UsbDevice *device, unsigned char endpointNumber, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| return 0; |
| } |
| |
| ssize_t |
| usbReadEndpoint ( |
| UsbDevice *device, |
| unsigned char endpointNumber, |
| void *buffer, |
| size_t length, |
| int timeout |
| ) { |
| UsbDeviceExtension *devx = device->extension; |
| UsbEndpoint *endpoint; |
| |
| if ((endpoint = usbGetInputEndpoint(device, endpointNumber))) { |
| UsbEndpointExtension *eptx = endpoint->extension; |
| IOReturn result; |
| UInt32 count; |
| int stalled = 0; |
| |
| read: |
| count = length; |
| result = (*devx->interface)->ReadPipeTO(devx->interface, eptx->pipeNumber, |
| buffer, &count, |
| timeout, timeout); |
| |
| switch (result) { |
| case kIOReturnSuccess: |
| { |
| ssize_t actual = count; |
| |
| if (usbApplyInputFilters(endpoint, buffer, length, &actual)) return actual; |
| } |
| |
| errno = EIO; |
| break; |
| |
| case kIOUSBTransactionTimeout: |
| errno = EAGAIN; |
| break; |
| |
| case kIOUSBPipeStalled: |
| if (!stalled) { |
| result = (*devx->interface)->ClearPipeStallBothEnds(devx->interface, eptx->pipeNumber); |
| if (result == kIOReturnSuccess) { |
| stalled = 1; |
| goto read; |
| } |
| |
| setUsbError(result, "USB stall clear"); |
| break; |
| } |
| |
| default: |
| setUsbError(result, "USB endpoint read"); |
| break; |
| } |
| } |
| |
| return -1; |
| } |
| |
| ssize_t |
| usbWriteEndpoint ( |
| UsbDevice *device, |
| unsigned char endpointNumber, |
| const void *buffer, |
| size_t length, |
| int timeout |
| ) { |
| UsbDeviceExtension *devx = device->extension; |
| UsbEndpoint *endpoint; |
| |
| if ((endpoint = usbGetOutputEndpoint(device, endpointNumber))) { |
| UsbEndpointExtension *eptx = endpoint->extension; |
| IOReturn result; |
| |
| result = (*devx->interface)->WritePipeTO(devx->interface, eptx->pipeNumber, |
| (void *)buffer, length, |
| timeout, timeout); |
| if (result == kIOReturnSuccess) return length; |
| setUsbError(result, "USB endpoint write"); |
| } |
| |
| return -1; |
| } |
| |
| int |
| usbReadDeviceDescriptor (UsbDevice *device) { |
| UsbDeviceExtension *devx = device->extension; |
| IOReturn result; |
| |
| { |
| uint8_t speed; |
| if ((result = (*devx->device)->GetDeviceSpeed(devx->device, &speed)) != kIOReturnSuccess) goto error; |
| switch (speed) { |
| default : device->descriptor.bcdUSB = 0X0000; break; |
| case kUSBDeviceSpeedLow : device->descriptor.bcdUSB = kUSBRel10; break; |
| case kUSBDeviceSpeedFull: device->descriptor.bcdUSB = kUSBRel11; break; |
| case kUSBDeviceSpeedHigh: device->descriptor.bcdUSB = kUSBRel20; break; |
| } |
| } |
| |
| if ((result = (*devx->device)->GetDeviceClass(devx->device, &device->descriptor.bDeviceClass)) != kIOReturnSuccess) goto error; |
| if ((result = (*devx->device)->GetDeviceSubClass(devx->device, &device->descriptor.bDeviceSubClass)) != kIOReturnSuccess) goto error; |
| if ((result = (*devx->device)->GetDeviceProtocol(devx->device, &device->descriptor.bDeviceProtocol)) != kIOReturnSuccess) goto error; |
| |
| if ((result = (*devx->device)->GetDeviceVendor(devx->device, &device->descriptor.idVendor)) != kIOReturnSuccess) goto error; |
| if ((result = (*devx->device)->GetDeviceProduct(devx->device, &device->descriptor.idProduct)) != kIOReturnSuccess) goto error; |
| if ((result = (*devx->device)->GetDeviceReleaseNumber(devx->device, &device->descriptor.bcdDevice)) != kIOReturnSuccess) goto error; |
| |
| if ((result = (*devx->device)->USBGetManufacturerStringIndex(devx->device, &device->descriptor.iManufacturer)) != kIOReturnSuccess) goto error; |
| if ((result = (*devx->device)->USBGetProductStringIndex(devx->device, &device->descriptor.iProduct)) != kIOReturnSuccess) goto error; |
| if ((result = (*devx->device)->USBGetSerialNumberStringIndex(devx->device, &device->descriptor.iSerialNumber)) != kIOReturnSuccess) goto error; |
| |
| if ((result = (*devx->device)->GetNumberOfConfigurations(devx->device, &device->descriptor.bNumConfigurations)) != kIOReturnSuccess) goto error; |
| device->descriptor.bMaxPacketSize0 = 0; |
| |
| device->descriptor.bLength = UsbDescriptorSize_Device; |
| device->descriptor.bDescriptorType = UsbDescriptorType_Device; |
| return 1; |
| |
| error: |
| setUsbError(result, "USB device descriptor read"); |
| return 0; |
| } |
| |
| int |
| usbAllocateEndpointExtension (UsbEndpoint *endpoint) { |
| UsbDeviceExtension *devx = endpoint->device->extension; |
| UsbEndpointExtension *eptx; |
| |
| if ((eptx = malloc(sizeof(*eptx)))) { |
| if ((eptx->completedRequests = newQueue(NULL, NULL))) { |
| IOReturn result; |
| unsigned char number = USB_ENDPOINT_NUMBER(endpoint->descriptor); |
| unsigned char direction = USB_ENDPOINT_DIRECTION(endpoint->descriptor); |
| |
| for (eptx->pipeNumber=1; eptx->pipeNumber<=devx->pipeCount; ++eptx->pipeNumber) { |
| result = (*devx->interface)->GetPipeProperties(devx->interface, eptx->pipeNumber, |
| &eptx->transferDirection, &eptx->endpointNumber, |
| &eptx->transferMode, &eptx->packetSize, &eptx->pollInterval); |
| if (result == kIOReturnSuccess) { |
| if ((eptx->endpointNumber == number) && |
| (((eptx->transferDirection == kUSBIn) && (direction == UsbEndpointDirection_Input)) || |
| ((eptx->transferDirection == kUSBOut) && (direction == UsbEndpointDirection_Output)))) { |
| logMessage(LOG_CATEGORY(USB_IO), "ept=%02X -> pip=%d (num=%d dir=%d xfr=%d int=%d pkt=%d)", |
| endpoint->descriptor->bEndpointAddress, eptx->pipeNumber, |
| eptx->endpointNumber, eptx->transferDirection, eptx->transferMode, |
| eptx->pollInterval, eptx->packetSize); |
| |
| eptx->endpoint = endpoint; |
| endpoint->extension = eptx; |
| return 1; |
| } |
| } else { |
| setUsbError(result, "USB pipe properties query"); |
| } |
| } |
| |
| errno = EIO; |
| logMessage(LOG_ERR, "USB pipe not found: ept=%02X", |
| endpoint->descriptor->bEndpointAddress); |
| |
| deallocateQueue(eptx->completedRequests); |
| } else { |
| logSystemError("USB completed request queue allocate"); |
| } |
| |
| free(eptx); |
| } else { |
| logSystemError("USB endpoint extension allocate"); |
| } |
| |
| return 0; |
| } |
| |
| void |
| usbDeallocateEndpointExtension (UsbEndpointExtension *eptx) { |
| if (eptx->completedRequests) { |
| deallocateQueue(eptx->completedRequests); |
| eptx->completedRequests = NULL; |
| } |
| |
| free(eptx); |
| } |
| |
| void |
| usbDeallocateDeviceExtension (UsbDeviceExtension *devx) { |
| IOReturn result; |
| |
| unsetInterface(devx); |
| |
| if (devx->deviceOpened) { |
| result = (*devx->device)->USBDeviceClose(devx->device); |
| if (result != kIOReturnSuccess) setUsbError(result, "USB device close"); |
| devx->deviceOpened = 0; |
| } |
| |
| (*devx->device)->Release(devx->device); |
| devx->device = NULL; |
| |
| free(devx); |
| } |
| |
| UsbDevice * |
| usbFindDevice (UsbDeviceChooser *chooser, UsbChooseChannelData *data) { |
| UsbDevice *device = NULL; |
| kern_return_t kernelResult; |
| IOReturn ioResult; |
| mach_port_t port; |
| |
| kernelResult = IOMasterPort(MACH_PORT_NULL, &port); |
| if (kernelResult == KERN_SUCCESS) { |
| CFMutableDictionaryRef dictionary; |
| |
| if ((dictionary = IOServiceMatching(kIOUSBDeviceClassName))) { |
| io_iterator_t iterator = 0; |
| |
| kernelResult = IOServiceGetMatchingServices(port, dictionary, &iterator); |
| dictionary = NULL; |
| |
| if ((kernelResult == KERN_SUCCESS) && iterator) { |
| io_service_t service; |
| |
| while ((service = IOIteratorNext(iterator))) { |
| IOCFPlugInInterface **plugin = NULL; |
| SInt32 score; |
| |
| ioResult = IOCreatePlugInInterfaceForService(service, |
| kIOUSBDeviceUserClientTypeID, |
| kIOCFPlugInInterfaceID, |
| &plugin, &score); |
| IOObjectRelease(service); |
| service = 0; |
| |
| if ((ioResult == kIOReturnSuccess) && plugin) { |
| IOUSBDeviceInterface182 **interface = NULL; |
| |
| ioResult = (*plugin)->QueryInterface(plugin, |
| CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182), |
| (LPVOID)&interface); |
| (*plugin)->Release(plugin); |
| plugin = NULL; |
| |
| if ((ioResult == kIOReturnSuccess) && interface) { |
| UsbDeviceExtension *devx; |
| |
| if ((devx = malloc(sizeof(*devx)))) { |
| devx->device = interface; |
| devx->deviceOpened = 0; |
| |
| devx->interface = NULL; |
| devx->interfaceOpened = 0; |
| |
| devx->runloopSource = NULL; |
| |
| if ((device = usbTestDevice(devx, chooser, data))) break; |
| free(devx); |
| devx = NULL; |
| } else { |
| logSystemError("USB device extension allocate"); |
| } |
| |
| (*interface)->Release(interface); |
| interface = NULL; |
| } else { |
| setUsbError(ioResult, "USB device interface create"); |
| } |
| } else { |
| setUsbError(ioResult, "USB device service plugin create"); |
| } |
| } |
| |
| IOObjectRelease(iterator); |
| iterator = 0; |
| } else { |
| setUsbError(kernelResult, "USB device iterator create"); |
| } |
| } else { |
| logMessage(LOG_ERR, "USB device matching dictionary create error."); |
| } |
| |
| mach_port_deallocate(mach_task_self(), port); |
| } else { |
| setUsbError(kernelResult, "Darwin master port create"); |
| } |
| |
| return device; |
| } |
| |
| void |
| usbForgetDevices (void) { |
| } |