/* * Haiku Backend for libusb * Copyright © 2014 Akshay Jaggi * * This library is free software; you can redistribute it and/or * modify it 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. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "haiku_usb.h" static int _errno_to_libusb(int status) { return status; } USBTransfer::USBTransfer(struct usbi_transfer *itransfer, USBDevice *device) { fUsbiTransfer = itransfer; fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); fUSBDevice = device; fCancelled = false; } USBTransfer::~USBTransfer() { } struct usbi_transfer * USBTransfer::UsbiTransfer() { return fUsbiTransfer; } void USBTransfer::SetCancelled() { fCancelled = true; } bool USBTransfer::IsCancelled() { return fCancelled; } void USBTransfer::Do(int fRawFD) { switch (fLibusbTransfer->type) { case LIBUSB_TRANSFER_TYPE_CONTROL: { struct libusb_control_setup *setup = (struct libusb_control_setup *)fLibusbTransfer->buffer; usb_raw_command command; command.control.request_type = setup->bmRequestType; command.control.request = setup->bRequest; command.control.value = setup->wValue; command.control.index = setup->wIndex; command.control.length = setup->wLength; command.control.data = fLibusbTransfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; if (fCancelled) break; if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || command.control.status != B_USB_RAW_STATUS_SUCCESS) { fUsbiTransfer->transferred = -1; usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed control transfer"); break; } fUsbiTransfer->transferred = command.control.length; } break; case LIBUSB_TRANSFER_TYPE_BULK: case LIBUSB_TRANSFER_TYPE_INTERRUPT: { usb_raw_command command; command.transfer.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); command.transfer.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); command.transfer.data = fLibusbTransfer->buffer; command.transfer.length = fLibusbTransfer->length; if (fCancelled) break; if (fLibusbTransfer->type == LIBUSB_TRANSFER_TYPE_BULK) { if (ioctl(fRawFD, B_USB_RAW_COMMAND_BULK_TRANSFER, &command, sizeof(command)) || command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { fUsbiTransfer->transferred = -1; usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed bulk transfer"); break; } } else { if (ioctl(fRawFD, B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, &command, sizeof(command)) || command.transfer.status != B_USB_RAW_STATUS_SUCCESS) { fUsbiTransfer->transferred = -1; usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed interrupt transfer"); break; } } fUsbiTransfer->transferred = command.transfer.length; } break; // IsochronousTransfers not tested case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: { usb_raw_command command; command.isochronous.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint); command.isochronous.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint); command.isochronous.data = fLibusbTransfer->buffer; command.isochronous.length = fLibusbTransfer->length; command.isochronous.packet_count = fLibusbTransfer->num_iso_packets; int i; usb_iso_packet_descriptor *packetDescriptors = new usb_iso_packet_descriptor[fLibusbTransfer->num_iso_packets]; for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { if ((fLibusbTransfer->iso_packet_desc[i]).length > (unsigned int)INT16_MAX) { fUsbiTransfer->transferred = -1; usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); break; } packetDescriptors[i].request_length = (int16)(fLibusbTransfer->iso_packet_desc[i]).length; } if (i < fLibusbTransfer->num_iso_packets) break; // TODO Handle this error command.isochronous.packet_descriptors = packetDescriptors; if (fCancelled) break; if (ioctl(fRawFD, B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER, &command, sizeof(command)) || command.isochronous.status != B_USB_RAW_STATUS_SUCCESS) { fUsbiTransfer->transferred = -1; usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer"); break; } for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) { (fLibusbTransfer->iso_packet_desc[i]).actual_length = packetDescriptors[i].actual_length; switch (packetDescriptors[i].status) { case B_OK: (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_COMPLETED; break; default: (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_ERROR; break; } } delete[] packetDescriptors; // Do we put the length of transfer here, for isochronous transfers? fUsbiTransfer->transferred = command.transfer.length; } break; default: usbi_err(TRANSFER_CTX(fLibusbTransfer), "Unknown type of transfer"); } } bool USBDeviceHandle::InitCheck() { return fInitCheck; } status_t USBDeviceHandle::TransfersThread(void *self) { USBDeviceHandle *handle = (USBDeviceHandle *)self; handle->TransfersWorker(); return B_OK; } void USBDeviceHandle::TransfersWorker() { while (true) { status_t status = acquire_sem(fTransfersSem); if (status == B_BAD_SEM_ID) break; if (status == B_INTERRUPTED) continue; fTransfersLock.Lock(); USBTransfer *fPendingTransfer = (USBTransfer *) fTransfers.RemoveItem((int32)0); fTransfersLock.Unlock(); fPendingTransfer->Do(fRawFD); usbi_signal_transfer_completion(fPendingTransfer->UsbiTransfer()); } } status_t USBDeviceHandle::SubmitTransfer(struct usbi_transfer *itransfer) { USBTransfer *transfer = new USBTransfer(itransfer, fUSBDevice); *((USBTransfer **)usbi_get_transfer_priv(itransfer)) = transfer; BAutolock locker(fTransfersLock); fTransfers.AddItem(transfer); release_sem(fTransfersSem); return LIBUSB_SUCCESS; } status_t USBDeviceHandle::CancelTransfer(USBTransfer *transfer) { transfer->SetCancelled(); fTransfersLock.Lock(); bool removed = fTransfers.RemoveItem(transfer); fTransfersLock.Unlock(); if (removed) usbi_signal_transfer_completion(transfer->UsbiTransfer()); return LIBUSB_SUCCESS; } USBDeviceHandle::USBDeviceHandle(USBDevice *dev) : fUSBDevice(dev), fClaimedInterfaces(0), fTransfersThread(-1), fInitCheck(false) { fRawFD = open(dev->Location(), O_RDWR | O_CLOEXEC); if (fRawFD < 0) { usbi_err(NULL,"failed to open device"); return; } fTransfersSem = create_sem(0, "Transfers Queue Sem"); fTransfersThread = spawn_thread(TransfersThread, "Transfer Worker", B_NORMAL_PRIORITY, this); resume_thread(fTransfersThread); fInitCheck = true; } USBDeviceHandle::~USBDeviceHandle() { if (fRawFD > 0) close(fRawFD); for (int i = 0; i < 32; i++) { if (fClaimedInterfaces & (1U << i)) ReleaseInterface(i); } delete_sem(fTransfersSem); if (fTransfersThread > 0) wait_for_thread(fTransfersThread, NULL); } int USBDeviceHandle::ClaimInterface(uint8 inumber) { int status = fUSBDevice->ClaimInterface(inumber); if (status == LIBUSB_SUCCESS) fClaimedInterfaces |= (1U << inumber); return status; } int USBDeviceHandle::ReleaseInterface(uint8 inumber) { fUSBDevice->ReleaseInterface(inumber); fClaimedInterfaces &= ~(1U << inumber); return LIBUSB_SUCCESS; } int USBDeviceHandle::SetConfiguration(uint8 config) { int config_index = fUSBDevice->CheckInterfacesFree(config); if (config_index == LIBUSB_ERROR_BUSY || config_index == LIBUSB_ERROR_NOT_FOUND) return config_index; usb_raw_command command; command.config.config_index = config_index; if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_CONFIGURATION, &command, sizeof(command)) || command.config.status != B_USB_RAW_STATUS_SUCCESS) { return _errno_to_libusb(command.config.status); } fUSBDevice->SetActiveConfiguration((uint8)config_index); return LIBUSB_SUCCESS; } int USBDeviceHandle::SetAltSetting(uint8 inumber, uint8 alt) { usb_raw_command command; command.alternate.config_index = fUSBDevice->ActiveConfigurationIndex(); command.alternate.interface_index = inumber; if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, &command, sizeof(command)) || command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { usbi_err(NULL, "Error retrieving active alternate interface"); return _errno_to_libusb(command.alternate.status); } if (command.alternate.alternate_info == (uint32)alt) { usbi_dbg(NULL, "Setting alternate interface successful"); return LIBUSB_SUCCESS; } command.alternate.alternate_info = alt; if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_ALT_INTERFACE, &command, sizeof(command)) || command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { //IF IOCTL FAILS DEVICE DISCONNECTED PROBABLY usbi_err(NULL, "Error setting alternate interface"); return _errno_to_libusb(command.alternate.status); } usbi_dbg(NULL, "Setting alternate interface successful"); return LIBUSB_SUCCESS; } int USBDeviceHandle::ClearHalt(uint8 endpoint) { usb_raw_command command; command.control.request_type = USB_REQTYPE_ENDPOINT_OUT; command.control.request = USB_REQUEST_CLEAR_FEATURE; command.control.value = USB_FEATURE_ENDPOINT_HALT; command.control.index = endpoint; command.control.length = 0; if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) || command.control.status != B_USB_RAW_STATUS_SUCCESS) { return _errno_to_libusb(command.control.status); } return LIBUSB_SUCCESS; } USBDevice::USBDevice(const char *path) : fClaimedInterfaces(0), fConfigurationDescriptors(NULL), fActiveConfiguration(0), //0? fPath(NULL), fEndpointToIndex(NULL), fEndpointToInterface(NULL), fInitCheck(false) { fPath=strdup(path); Initialise(); } USBDevice::~USBDevice() { free(fPath); if (fConfigurationDescriptors) { for (uint8 i = 0; i < fDeviceDescriptor.num_configurations; i++) { if (fConfigurationDescriptors[i]) delete fConfigurationDescriptors[i]; } delete[] fConfigurationDescriptors; } if (fEndpointToIndex) delete[] fEndpointToIndex; if (fEndpointToInterface) delete[] fEndpointToInterface; } bool USBDevice::InitCheck() { return fInitCheck; } const char * USBDevice::Location() const { return fPath; } uint8 USBDevice::CountConfigurations() const { return fDeviceDescriptor.num_configurations; } const usb_device_descriptor * USBDevice::Descriptor() const { return &fDeviceDescriptor; } const usb_configuration_descriptor * USBDevice::ConfigurationDescriptor(uint8 index) const { if (index > CountConfigurations()) return NULL; return (usb_configuration_descriptor *) fConfigurationDescriptors[index]; } const usb_configuration_descriptor * USBDevice::ActiveConfiguration() const { return (usb_configuration_descriptor *) fConfigurationDescriptors[fActiveConfiguration]; } uint8 USBDevice::ActiveConfigurationIndex() const { return fActiveConfiguration; } int USBDevice::ClaimInterface(uint8 interface) { if (interface > ActiveConfiguration()->number_interfaces) return LIBUSB_ERROR_NOT_FOUND; if (fClaimedInterfaces & (1U << interface)) return LIBUSB_ERROR_BUSY; fClaimedInterfaces |= (1U << interface); return LIBUSB_SUCCESS; } int USBDevice::ReleaseInterface(uint8 interface) { fClaimedInterfaces &= ~(1U << interface); return LIBUSB_SUCCESS; } int USBDevice::CheckInterfacesFree(uint8 config) { if (fConfigToIndex.count(config) == 0) return LIBUSB_ERROR_NOT_FOUND; if (fClaimedInterfaces == 0) return fConfigToIndex[config]; return LIBUSB_ERROR_BUSY; } void USBDevice::SetActiveConfiguration(uint8 config_index) { fActiveConfiguration = config_index; } uint8 USBDevice::EndpointToIndex(uint8 address) const { return fEndpointToIndex[fActiveConfiguration][address]; } uint8 USBDevice::EndpointToInterface(uint8 address) const { return fEndpointToInterface[fActiveConfiguration][address]; } int USBDevice::Initialise() //Do we need more error checking, etc? How to report? { int fRawFD = open(fPath, O_RDWR | O_CLOEXEC); if (fRawFD < 0) return B_ERROR; usb_raw_command command; command.device.descriptor = &fDeviceDescriptor; if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR, &command, sizeof(command)) || command.device.status != B_USB_RAW_STATUS_SUCCESS) { close(fRawFD); return B_ERROR; } fConfigurationDescriptors = new(std::nothrow) unsigned char *[fDeviceDescriptor.num_configurations]; fEndpointToIndex = new(std::nothrow) map [fDeviceDescriptor.num_configurations]; fEndpointToInterface = new(std::nothrow) map [fDeviceDescriptor.num_configurations]; for (uint8 i = 0; i < fDeviceDescriptor.num_configurations; i++) { usb_configuration_descriptor tmp_config; command.config.descriptor = &tmp_config; command.config.config_index = i; if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, &command, sizeof(command)) || command.config.status != B_USB_RAW_STATUS_SUCCESS) { usbi_err(NULL, "failed retrieving configuration descriptor"); close(fRawFD); return B_ERROR; } fConfigToIndex[tmp_config.configuration_value] = i; fConfigurationDescriptors[i] = new(std::nothrow) unsigned char[tmp_config.total_length]; command.config_etc.descriptor = (usb_configuration_descriptor*)fConfigurationDescriptors[i]; command.config_etc.length = tmp_config.total_length; command.config_etc.config_index = i; if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR_ETC, &command, sizeof(command)) || command.config_etc.status != B_USB_RAW_STATUS_SUCCESS) { usbi_err(NULL, "failed retrieving full configuration descriptor"); close(fRawFD); return B_ERROR; } for (uint8 j = 0; j < tmp_config.number_interfaces; j++) { command.alternate.config_index = i; command.alternate.interface_index = j; if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, &command, sizeof(command)) || command.config.status != B_USB_RAW_STATUS_SUCCESS) { usbi_err(NULL, "failed retrieving number of alternate interfaces"); close(fRawFD); return B_ERROR; } uint8 num_alternate = (uint8)command.alternate.alternate_info; for (uint8 k = 0; k < num_alternate; k++) { usb_interface_descriptor tmp_interface; command.interface_etc.config_index = i; command.interface_etc.interface_index = j; command.interface_etc.alternate_index = k; command.interface_etc.descriptor = &tmp_interface; if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, &command, sizeof(command)) || command.config.status != B_USB_RAW_STATUS_SUCCESS) { usbi_err(NULL, "failed retrieving interface descriptor"); close(fRawFD); return B_ERROR; } for (uint8 l = 0; l < tmp_interface.num_endpoints; l++) { usb_endpoint_descriptor tmp_endpoint; command.endpoint_etc.config_index = i; command.endpoint_etc.interface_index = j; command.endpoint_etc.alternate_index = k; command.endpoint_etc.endpoint_index = l; command.endpoint_etc.descriptor = &tmp_endpoint; if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, &command, sizeof(command)) || command.config.status != B_USB_RAW_STATUS_SUCCESS) { usbi_err(NULL, "failed retrieving endpoint descriptor"); close(fRawFD); return B_ERROR; } fEndpointToIndex[i][tmp_endpoint.endpoint_address] = l; fEndpointToInterface[i][tmp_endpoint.endpoint_address] = j; } } } } close(fRawFD); fInitCheck = true; return B_OK; }