blob: daa4d33853e800a64a70ea5e872077e467ee0faa [file] [log] [blame]
/*
* 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 <string.h>
#include <errno.h>
#include <usb.h>
#include "log.h"
#include "io_usb.h"
#include "usb_internal.h"
#include "bitfield.h"
#ifdef __MINGW32__
#ifndef ETIMEDOUT
#define ETIMEDOUT 116
#endif /* ETIMEDOUT */
#endif /* __MINGW32__ */
struct UsbDeviceExtensionStruct {
struct usb_dev_handle *handle;
};
int
usbDisableAutosuspend (UsbDevice *device) {
logUnsupportedFunction();
return 0;
}
int
usbSetConfiguration (UsbDevice *device, unsigned char configuration) {
UsbDeviceExtension *devx = device->extension;
int result;
logMessage(LOG_CATEGORY(USB_IO), "setting configuration: %u", configuration);
result = usb_set_configuration(devx->handle, configuration);
if (result >= 0) return 1;
errno = -result;
logSystemError("USB configuration set");
return 0;
}
int
usbClaimInterface (UsbDevice *device, unsigned char interface) {
UsbDeviceExtension *devx = device->extension;
int detached = 0;
int result;
logMessage(LOG_CATEGORY(USB_IO), "claiming interface: %u", interface);
while (1) {
char driver[0X100];
result = usb_claim_interface(devx->handle, interface);
if (result >= 0) return 1;
if (result != -EBUSY) break;
if (detached) break;
#ifdef LIBUSB_HAS_GET_DRIVER_NP
result = usb_get_driver_np(devx->handle, interface, driver, sizeof(driver));
if (result < 0)
#endif /* LIBUSB_HAS_GET_DRIVER_NP */
{
strcpy(driver, "unknown");
}
logMessage(LOG_WARNING, "USB interface in use: %u (%s)", interface, driver);
if (strcmp(driver, "usbfs") == 0) {
result = -EBUSY;
break;
}
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
logMessage(LOG_CATEGORY(USB_IO), "detaching kernel driver: %u (%s)",
interface, driver);
result = usb_detach_kernel_driver_np(devx->handle, interface);
if (result >= 0) {
logMessage(LOG_CATEGORY(USB_IO), "detached kernel driver: %u (%s)",
interface, driver);
detached = 1;
continue;
}
result = -EBUSY;
#endif /* LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP */
break;
}
errno = -result;
logSystemError("USB interface claim");
return 0;
}
int
usbReleaseInterface (UsbDevice *device, unsigned char interface) {
UsbDeviceExtension *devx = device->extension;
int result;
logMessage(LOG_CATEGORY(USB_IO), "releasing interface: %u", interface);
result = usb_release_interface(devx->handle, interface);
if (result >= 0) return 1;
errno = -result;
logSystemError("USB interface release");
return 0;
}
int
usbSetAlternative (
UsbDevice *device,
unsigned char interface,
unsigned char alternative
) {
UsbDeviceExtension *devx = device->extension;
int result;
logMessage(LOG_CATEGORY(USB_IO), "setting alternative: %u[%u]", interface, alternative);
result = usb_set_altinterface(devx->handle, alternative);
if (result >= 0) return 1;
errno = -result;
logSystemError("USB alternative set");
return 0;
}
int
usbResetDevice (UsbDevice *device) {
logMessage(LOG_CATEGORY(USB_IO), "reset device");
UsbDeviceExtension *devx = device->extension;
int result = usb_reset(devx->handle);
if (result >= 0) return 1;
errno = -result;
logSystemError("USB device reset");
return 0;
}
int
usbClearHalt (UsbDevice *device, unsigned char endpointAddress) {
logMessage(LOG_CATEGORY(USB_IO), "clear halt: %02X", endpointAddress);
UsbDeviceExtension *devx = device->extension;
int result = usb_clear_halt(devx->handle, endpointAddress);
if (result >= 0) return 1;
errno = -result;
logSystemError("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;
UsbSetupPacket setup;
int result;
usbMakeSetupPacket(&setup, direction, recipient, type, request, value, index, length);
if (direction == UsbControlDirection_Output) {
if (length) logBytes(LOG_CATEGORY(USB_IO), "control output", buffer, length);
}
result = usb_control_msg(devx->handle, setup.bRequestType, setup.bRequest,
getLittleEndian16(setup.wValue),
getLittleEndian16(setup.wIndex), buffer,
getLittleEndian16(setup.wLength), timeout);
if (result >= 0) {
if (direction == UsbControlDirection_Input) {
logBytes(LOG_CATEGORY(USB_IO), "control input", buffer, result);
}
return result;
}
errno = -result;
logSystemError("USB control transfer");
return -1;
}
void *
usbSubmitRequest (
UsbDevice *device,
unsigned char endpointAddress,
void *buffer,
size_t length,
void *data
) {
logUnsupportedFunction();
return NULL;
}
int
usbCancelRequest (UsbDevice *device, void *request) {
logUnsupportedFunction();
return 0;
}
void *
usbReapResponse (
UsbDevice *device,
unsigned char endpointAddress,
UsbResponse *response,
int wait
) {
logUnsupportedFunction();
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))) {
const UsbEndpointDescriptor *descriptor = endpoint->descriptor;
UsbEndpointTransfer transfer = USB_ENDPOINT_TRANSFER(descriptor);
ssize_t result = -1;
switch (transfer) {
case UsbEndpointTransfer_Bulk:
result = usb_bulk_read(devx->handle, descriptor->bEndpointAddress,
buffer, length, timeout);
break;
case UsbEndpointTransfer_Interrupt:
result = usb_interrupt_read(devx->handle, descriptor->bEndpointAddress,
buffer, length, timeout);
break;
default:
logMessage(LOG_ERR, "USB endpoint input transfer not supported: 0X%02X", transfer);
result = -ENOSYS;
break;
}
if (result >= 0) {
if (!usbApplyInputFilters(endpoint, buffer, length, &result)) {
result = -EIO;
}
}
if (result >= 0) return result;
errno = -result;
}
#ifdef ETIMEDOUT
if (errno == ETIMEDOUT) errno = EAGAIN;
#endif /* ETIMEDOUT */
if (errno != EAGAIN) logSystemError("USB endpoint read");
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))) {
const UsbEndpointDescriptor *descriptor = endpoint->descriptor;
UsbEndpointTransfer transfer = USB_ENDPOINT_TRANSFER(descriptor);
int result = -1;
usbLogEndpointData(endpoint, "output", buffer, length);
switch (transfer) {
case UsbEndpointTransfer_Bulk:
result = usb_bulk_write(devx->handle, descriptor->bEndpointAddress,
(char *)buffer, length, timeout);
break;
case UsbEndpointTransfer_Interrupt:
result = usb_interrupt_write(devx->handle, descriptor->bEndpointAddress,
(char *)buffer, length, timeout);
break;
default:
logMessage(LOG_ERR, "USB endpoint output transfer not supported: 0X%02X", transfer);
result = -ENOSYS;
break;
}
if (result >= 0) return result;
errno = -result;
}
logSystemError("USB endpoint write");
return -1;
}
int
usbReadDeviceDescriptor (UsbDevice *device) {
UsbDeviceExtension *devx = device->extension;
memcpy(&device->descriptor, &usb_device(devx->handle)->descriptor, UsbDescriptorSize_Device);
return 1;
}
int
usbAllocateEndpointExtension (UsbEndpoint *endpoint) {
return 1;
}
void
usbDeallocateEndpointExtension (UsbEndpointExtension *eptx) {
}
void
usbDeallocateDeviceExtension (UsbDeviceExtension *devx) {
if (devx->handle) {
usb_close(devx->handle);
devx->handle = NULL;
}
free(devx);
}
UsbDevice *
usbFindDevice (UsbDeviceChooser *chooser, UsbChooseChannelData *data) {
UsbDevice *device = NULL;
int result;
{
static int initialized = 0;
if (!initialized) {
usb_init();
initialized = 1;
}
}
if ((result = usb_find_busses()) >= 0) {
if ((result = usb_find_devices()) >= 0) {
struct usb_bus *bus = usb_get_busses();
if (bus) {
struct usb_bus *bus0 = bus;
do {
struct usb_device *dev = bus->devices;
if (dev) {
struct usb_device *dev0 = dev;
do {
UsbDeviceExtension *devx;
if ((devx = malloc(sizeof(*devx)))) {
if ((devx->handle = usb_open(dev))) {
if ((device = usbTestDevice(devx, chooser, data))) return device;
usb_close(devx->handle);
} else {
logMessage(LOG_ERR, "USB open error: vendor=%X product=%X",
getLittleEndian16(dev->descriptor.idVendor),
getLittleEndian16(dev->descriptor.idProduct));
}
free(devx);
} else {
logSystemError("USB device extension allocate");
}
if ((dev = dev->next) == dev0) dev = NULL;
} while (dev);
}
if ((bus = bus->next) == bus0) bus = NULL;
} while (bus);
}
} else {
errno = -result;
logSystemError("USB devices find");
}
} else {
errno = -result;
logSystemError("USB busses find");
}
return device;
}
void
usbForgetDevices (void) {
}