2 * Haiku Backend for libusb
3 * Copyright © 2014 Akshay Jaggi <akshay1994.leo@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include "haiku_usb.h"
29 int _errno_to_libusb(int status)
34 USBTransfer::USBTransfer(struct usbi_transfer *itransfer, USBDevice *device)
36 fUsbiTransfer = itransfer;
37 fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
42 USBTransfer::~USBTransfer()
46 struct usbi_transfer *
47 USBTransfer::UsbiTransfer()
53 USBTransfer::SetCancelled()
59 USBTransfer::IsCancelled()
65 USBTransfer::Do(int fRawFD)
67 switch (fLibusbTransfer->type) {
68 case LIBUSB_TRANSFER_TYPE_CONTROL:
70 struct libusb_control_setup *setup = (struct libusb_control_setup *)fLibusbTransfer->buffer;
71 usb_raw_command command;
72 command.control.request_type = setup->bmRequestType;
73 command.control.request = setup->bRequest;
74 command.control.value = setup->wValue;
75 command.control.index = setup->wIndex;
76 command.control.length = setup->wLength;
77 command.control.data = fLibusbTransfer->buffer + LIBUSB_CONTROL_SETUP_SIZE;
80 if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) ||
81 command.control.status != B_USB_RAW_STATUS_SUCCESS) {
82 fUsbiTransfer->transferred = -1;
83 usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed control transfer");
86 fUsbiTransfer->transferred = command.control.length;
89 case LIBUSB_TRANSFER_TYPE_BULK:
90 case LIBUSB_TRANSFER_TYPE_INTERRUPT:
92 usb_raw_command command;
93 command.transfer.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint);
94 command.transfer.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint);
95 command.transfer.data = fLibusbTransfer->buffer;
96 command.transfer.length = fLibusbTransfer->length;
99 if (fLibusbTransfer->type == LIBUSB_TRANSFER_TYPE_BULK) {
100 if (ioctl(fRawFD, B_USB_RAW_COMMAND_BULK_TRANSFER, &command, sizeof(command)) ||
101 command.transfer.status != B_USB_RAW_STATUS_SUCCESS) {
102 fUsbiTransfer->transferred = -1;
103 usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed bulk transfer");
108 if (ioctl(fRawFD, B_USB_RAW_COMMAND_INTERRUPT_TRANSFER, &command, sizeof(command)) ||
109 command.transfer.status != B_USB_RAW_STATUS_SUCCESS) {
110 fUsbiTransfer->transferred = -1;
111 usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed interrupt transfer");
115 fUsbiTransfer->transferred = command.transfer.length;
118 // IsochronousTransfers not tested
119 case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
121 usb_raw_command command;
122 command.isochronous.interface = fUSBDevice->EndpointToInterface(fLibusbTransfer->endpoint);
123 command.isochronous.endpoint = fUSBDevice->EndpointToIndex(fLibusbTransfer->endpoint);
124 command.isochronous.data = fLibusbTransfer->buffer;
125 command.isochronous.length = fLibusbTransfer->length;
126 command.isochronous.packet_count = fLibusbTransfer->num_iso_packets;
128 usb_iso_packet_descriptor *packetDescriptors = new usb_iso_packet_descriptor[fLibusbTransfer->num_iso_packets];
129 for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) {
130 if ((int16)(fLibusbTransfer->iso_packet_desc[i]).length != (fLibusbTransfer->iso_packet_desc[i]).length) {
131 fUsbiTransfer->transferred = -1;
132 usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer");
135 packetDescriptors[i].request_length = (int16)(fLibusbTransfer->iso_packet_desc[i]).length;
137 if (i < fLibusbTransfer->num_iso_packets)
138 break; // TODO Handle this error
139 command.isochronous.packet_descriptors = packetDescriptors;
142 if (ioctl(fRawFD, B_USB_RAW_COMMAND_ISOCHRONOUS_TRANSFER, &command, sizeof(command)) ||
143 command.isochronous.status != B_USB_RAW_STATUS_SUCCESS) {
144 fUsbiTransfer->transferred = -1;
145 usbi_err(TRANSFER_CTX(fLibusbTransfer), "failed isochronous transfer");
148 for (i = 0; i < fLibusbTransfer->num_iso_packets; i++) {
149 (fLibusbTransfer->iso_packet_desc[i]).actual_length = packetDescriptors[i].actual_length;
150 switch (packetDescriptors[i].status) {
152 (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_COMPLETED;
155 (fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_ERROR;
159 delete[] packetDescriptors;
160 // Do we put the length of transfer here, for isochronous transfers?
161 fUsbiTransfer->transferred = command.transfer.length;
165 usbi_err(TRANSFER_CTX(fLibusbTransfer), "Unknown type of transfer");
170 USBDeviceHandle::InitCheck()
176 USBDeviceHandle::TransfersThread(void *self)
178 USBDeviceHandle *handle = (USBDeviceHandle *)self;
179 handle->TransfersWorker();
184 USBDeviceHandle::TransfersWorker()
187 status_t status = acquire_sem(fTransfersSem);
188 if (status == B_BAD_SEM_ID)
190 if (status == B_INTERRUPTED)
192 fTransfersLock.Lock();
193 USBTransfer *fPendingTransfer = (USBTransfer *) fTransfers.RemoveItem((int32)0);
194 fTransfersLock.Unlock();
195 fPendingTransfer->Do(fRawFD);
196 usbi_signal_transfer_completion(fPendingTransfer->UsbiTransfer());
201 USBDeviceHandle::SubmitTransfer(struct usbi_transfer *itransfer)
203 USBTransfer *transfer = new USBTransfer(itransfer, fUSBDevice);
204 *((USBTransfer **)usbi_transfer_get_os_priv(itransfer)) = transfer;
205 BAutolock locker(fTransfersLock);
206 fTransfers.AddItem(transfer);
207 release_sem(fTransfersSem);
208 return LIBUSB_SUCCESS;
212 USBDeviceHandle::CancelTransfer(USBTransfer *transfer)
214 transfer->SetCancelled();
215 fTransfersLock.Lock();
216 bool removed = fTransfers.RemoveItem(transfer);
217 fTransfersLock.Unlock();
219 usbi_signal_transfer_completion(transfer->UsbiTransfer());
220 return LIBUSB_SUCCESS;
223 USBDeviceHandle::USBDeviceHandle(USBDevice *dev)
225 fTransfersThread(-1),
227 fClaimedInterfaces(0),
230 fRawFD = open(dev->Location(), O_RDWR | O_CLOEXEC);
232 usbi_err(NULL,"failed to open device");
235 fTransfersSem = create_sem(0, "Transfers Queue Sem");
236 fTransfersThread = spawn_thread(TransfersThread, "Transfer Worker", B_NORMAL_PRIORITY, this);
237 resume_thread(fTransfersThread);
241 USBDeviceHandle::~USBDeviceHandle()
245 for(int i = 0; i < 32; i++) {
246 if (fClaimedInterfaces & (1 << i))
249 delete_sem(fTransfersSem);
250 if (fTransfersThread > 0)
251 wait_for_thread(fTransfersThread, NULL);
255 USBDeviceHandle::ClaimInterface(int inumber)
257 int status = fUSBDevice->ClaimInterface(inumber);
258 if (status == LIBUSB_SUCCESS)
259 fClaimedInterfaces |= (1 << inumber);
264 USBDeviceHandle::ReleaseInterface(int inumber)
266 fUSBDevice->ReleaseInterface(inumber);
267 fClaimedInterfaces &= ~(1 << inumber);
268 return LIBUSB_SUCCESS;
272 USBDeviceHandle::SetConfiguration(int config)
274 int config_index = fUSBDevice->CheckInterfacesFree(config);
275 if(config_index == LIBUSB_ERROR_BUSY || config_index == LIBUSB_ERROR_NOT_FOUND)
277 usb_raw_command command;
278 command.config.config_index = config_index;
279 if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_CONFIGURATION, &command, sizeof(command)) ||
280 command.config.status != B_USB_RAW_STATUS_SUCCESS) {
281 return _errno_to_libusb(command.config.status);
283 fUSBDevice->SetActiveConfiguration(config_index);
284 return LIBUSB_SUCCESS;
288 USBDeviceHandle::SetAltSetting(int inumber, int alt)
290 usb_raw_command command;
291 command.alternate.config_index = fUSBDevice->ActiveConfigurationIndex();
292 command.alternate.interface_index = inumber;
293 if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ACTIVE_ALT_INTERFACE_INDEX, &command, sizeof(command)) ||
294 command.alternate.status != B_USB_RAW_STATUS_SUCCESS) {
295 usbi_err(NULL, "Error retrieving active alternate interface");
296 return _errno_to_libusb(command.alternate.status);
298 if (command.alternate.alternate_info == alt) {
299 usbi_dbg("Setting alternate interface successful");
300 return LIBUSB_SUCCESS;
302 command.alternate.alternate_info = alt;
303 if (ioctl(fRawFD, B_USB_RAW_COMMAND_SET_ALT_INTERFACE, &command, sizeof(command)) ||
304 command.alternate.status != B_USB_RAW_STATUS_SUCCESS) { //IF IOCTL FAILS DEVICE DISONNECTED PROBABLY
305 usbi_err(NULL, "Error setting alternate interface");
306 return _errno_to_libusb(command.alternate.status);
308 usbi_dbg("Setting alternate interface successful");
309 return LIBUSB_SUCCESS;
313 USBDevice::USBDevice(const char *path)
316 fActiveConfiguration(0), //0?
317 fConfigurationDescriptors(NULL),
318 fClaimedInterfaces(0),
319 fEndpointToIndex(NULL),
320 fEndpointToInterface(NULL),
327 USBDevice::~USBDevice()
330 if (fConfigurationDescriptors) {
331 for(int i = 0; i < fDeviceDescriptor.num_configurations; i++) {
332 if (fConfigurationDescriptors[i])
333 delete fConfigurationDescriptors[i];
335 delete[] fConfigurationDescriptors;
337 if (fEndpointToIndex)
338 delete[] fEndpointToIndex;
339 if (fEndpointToInterface)
340 delete[] fEndpointToInterface;
344 USBDevice::InitCheck()
350 USBDevice::Location() const
356 USBDevice::CountConfigurations() const
358 return fDeviceDescriptor.num_configurations;
361 const usb_device_descriptor *
362 USBDevice::Descriptor() const
364 return &fDeviceDescriptor;
367 const usb_configuration_descriptor *
368 USBDevice::ConfigurationDescriptor(uint32 index) const
370 if (index > CountConfigurations())
372 return (usb_configuration_descriptor *) fConfigurationDescriptors[index];
375 const usb_configuration_descriptor *
376 USBDevice::ActiveConfiguration() const
378 return (usb_configuration_descriptor *) fConfigurationDescriptors[fActiveConfiguration];
382 USBDevice::ActiveConfigurationIndex() const
384 return fActiveConfiguration;
387 int USBDevice::ClaimInterface(int interface)
389 if (interface > ActiveConfiguration()->number_interfaces)
390 return LIBUSB_ERROR_NOT_FOUND;
391 if (fClaimedInterfaces & (1 << interface))
392 return LIBUSB_ERROR_BUSY;
393 fClaimedInterfaces |= (1 << interface);
394 return LIBUSB_SUCCESS;
397 int USBDevice::ReleaseInterface(int interface)
399 fClaimedInterfaces &= ~(1 << interface);
400 return LIBUSB_SUCCESS;
404 USBDevice::CheckInterfacesFree(int config)
406 if (fConfigToIndex.count(config) == 0)
407 return LIBUSB_ERROR_NOT_FOUND;
408 if (fClaimedInterfaces == 0)
409 return fConfigToIndex[(uint8)config];
410 return LIBUSB_ERROR_BUSY;
414 USBDevice::SetActiveConfiguration(int config_index)
416 fActiveConfiguration = config_index;
417 return LIBUSB_SUCCESS;
421 USBDevice::EndpointToIndex(uint8 address) const
423 return fEndpointToIndex[fActiveConfiguration][address];
427 USBDevice::EndpointToInterface(uint8 address) const
429 return fEndpointToInterface[fActiveConfiguration][address];
433 USBDevice::Initialise() //Do we need more error checking, etc? How to report?
435 int fRawFD = open(fPath, O_RDWR | O_CLOEXEC);
438 usb_raw_command command;
439 command.device.descriptor = &fDeviceDescriptor;
440 if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_DEVICE_DESCRIPTOR, &command, sizeof(command)) ||
441 command.device.status != B_USB_RAW_STATUS_SUCCESS) {
446 fConfigurationDescriptors = new(std::nothrow) unsigned char *[fDeviceDescriptor.num_configurations];
447 fEndpointToIndex = new(std::nothrow) map<uint8,uint8> [fDeviceDescriptor.num_configurations];
448 fEndpointToInterface = new(std::nothrow) map<uint8,uint8> [fDeviceDescriptor.num_configurations];
449 for (int i = 0; i < fDeviceDescriptor.num_configurations; i++) {
450 usb_configuration_descriptor tmp_config;
451 command.config.descriptor = &tmp_config;
452 command.config.config_index = i;
453 if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_CONFIGURATION_DESCRIPTOR, &command, sizeof(command)) ||
454 command.config.status != B_USB_RAW_STATUS_SUCCESS) {
455 usbi_err(NULL, "failed retrieving configuration descriptor");
459 fConfigToIndex[tmp_config.configuration_value] = i;
460 fConfigurationDescriptors[i] = new(std::nothrow) unsigned char[tmp_config.total_length];
461 command.control.request_type = 128;
462 command.control.request = 6;
463 command.control.value = (2 << 8) | i;
464 command.control.index = 0;
465 command.control.length = tmp_config.total_length;
466 command.control.data = fConfigurationDescriptors[i];
467 if (ioctl(fRawFD, B_USB_RAW_COMMAND_CONTROL_TRANSFER, &command, sizeof(command)) ||
468 command.control.status!=B_USB_RAW_STATUS_SUCCESS) {
469 usbi_err(NULL, "failed retrieving full configuration descriptor");
473 for (int j = 0; j < tmp_config.number_interfaces; j++) {
474 command.alternate.config_index = i;
475 command.alternate.interface_index = j;
476 if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ALT_INTERFACE_COUNT, &command, sizeof(command)) ||
477 command.config.status != B_USB_RAW_STATUS_SUCCESS) {
478 usbi_err(NULL, "failed retrieving number of alternate interfaces");
482 int num_alternate = command.alternate.alternate_info;
483 for (int k = 0; k < num_alternate; k++) {
484 usb_interface_descriptor tmp_interface;
485 command.interface_etc.config_index = i;
486 command.interface_etc.interface_index = j;
487 command.interface_etc.alternate_index = k;
488 command.interface_etc.descriptor = &tmp_interface;
489 if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_INTERFACE_DESCRIPTOR_ETC, &command, sizeof(command)) ||
490 command.config.status != B_USB_RAW_STATUS_SUCCESS) {
491 usbi_err(NULL, "failed retrieving interface descriptor");
495 for (int l = 0; l < tmp_interface.num_endpoints; l++) {
496 usb_endpoint_descriptor tmp_endpoint;
497 command.endpoint_etc.config_index = i;
498 command.endpoint_etc.interface_index = j;
499 command.endpoint_etc.alternate_index = k;
500 command.endpoint_etc.endpoint_index = l;
501 command.endpoint_etc.descriptor = &tmp_endpoint;
502 if (ioctl(fRawFD, B_USB_RAW_COMMAND_GET_ENDPOINT_DESCRIPTOR_ETC, &command, sizeof(command)) ||
503 command.config.status != B_USB_RAW_STATUS_SUCCESS) {
504 usbi_err(NULL, "failed retrieving endpoint descriptor");
508 fEndpointToIndex[i][tmp_endpoint.endpoint_address] = l;
509 fEndpointToInterface[i][tmp_endpoint.endpoint_address] = j;