haiku_usb_backend.cpp revision 3dc781ce3ecd0315cfc0b4fdcef9e062990f289b
1/*
2 * Haiku Backend for libusb
3 * Copyright © 2014 Akshay Jaggi <akshay1994.leo@gmail.com>
4 *
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.
9 *
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.
14 *
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
18 */
19
20
21#include <unistd.h>
22#include <string.h>
23#include <stdlib.h>
24#include <new>
25#include <vector>
26
27#include "haiku_usb.h"
28
29int _errno_to_libusb(int status)
30{
31	return status;
32}
33
34USBTransfer::USBTransfer(struct usbi_transfer *itransfer, USBDevice *device)
35{
36	fUsbiTransfer = itransfer;
37	fLibusbTransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
38	fUSBDevice = device;
39	fCancelled = false;
40}
41
42USBTransfer::~USBTransfer()
43{
44}
45
46struct usbi_transfer *
47USBTransfer::UsbiTransfer()
48{
49	return fUsbiTransfer;
50}
51
52void
53USBTransfer::SetCancelled()
54{
55	fCancelled = true;
56}
57
58bool
59USBTransfer::IsCancelled()
60{
61	return fCancelled;
62}
63
64void
65USBTransfer::Do(int fRawFD)
66{
67	switch (fLibusbTransfer->type) {
68		case LIBUSB_TRANSFER_TYPE_CONTROL:
69		{
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;
78			if (fCancelled)
79				break;
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");
84				break;
85			}
86			fUsbiTransfer->transferred = command.control.length;
87		}
88		break;
89		case LIBUSB_TRANSFER_TYPE_BULK:
90		case LIBUSB_TRANSFER_TYPE_INTERRUPT:
91		{
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;
97			if (fCancelled)
98				break;
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");
104					break;
105				}
106			}
107			else {
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");
112					break;
113				}
114			}
115			fUsbiTransfer->transferred = command.transfer.length;
116		}
117		break;
118		// IsochronousTransfers not tested
119		case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
120		{
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;
127			int i;
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");
133					break;
134				}
135				packetDescriptors[i].request_length = (int16)(fLibusbTransfer->iso_packet_desc[i]).length;
136			}
137			if (i < fLibusbTransfer->num_iso_packets)
138				break;	// TODO Handle this error
139			command.isochronous.packet_descriptors = packetDescriptors;
140			if (fCancelled)
141				break;
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");
146				break;
147			}
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) {
151					case B_OK:
152						(fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_COMPLETED;
153						break;
154					default:
155						(fLibusbTransfer->iso_packet_desc[i]).status = LIBUSB_TRANSFER_ERROR;
156						break;
157				}
158			}
159			delete[] packetDescriptors;
160			// Do we put the length of transfer here, for isochronous transfers?
161			fUsbiTransfer->transferred = command.transfer.length;
162		}
163		break;
164		default:
165			usbi_err(TRANSFER_CTX(fLibusbTransfer), "Unknown type of transfer");
166	}
167}
168
169bool
170USBDeviceHandle::InitCheck()
171{
172	return fInitCheck;
173}
174
175status_t
176USBDeviceHandle::TransfersThread(void *self)
177{
178	USBDeviceHandle *handle = (USBDeviceHandle *)self;
179	handle->TransfersWorker();
180	return B_OK;
181}
182
183void
184USBDeviceHandle::TransfersWorker()
185{
186	while (true) {
187		status_t status = acquire_sem(fTransfersSem);
188		if (status == B_BAD_SEM_ID)
189			break;
190		if (status == B_INTERRUPTED)
191			continue;
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());
197	}
198}
199
200status_t
201USBDeviceHandle::SubmitTransfer(struct usbi_transfer *itransfer)
202{
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;
209}
210
211status_t
212USBDeviceHandle::CancelTransfer(USBTransfer *transfer)
213{
214	transfer->SetCancelled();
215	fTransfersLock.Lock();
216	bool removed = fTransfers.RemoveItem(transfer);
217	fTransfersLock.Unlock();
218	if(removed)
219		usbi_signal_transfer_completion(transfer->UsbiTransfer());
220	return LIBUSB_SUCCESS;
221}
222
223USBDeviceHandle::USBDeviceHandle(USBDevice *dev)
224	:
225	fTransfersThread(-1),
226	fUSBDevice(dev),
227	fClaimedInterfaces(0),
228	fInitCheck(false)
229{
230	fRawFD = open(dev->Location(), O_RDWR | O_CLOEXEC);
231	if (fRawFD < 0) {
232		usbi_err(NULL,"failed to open device");
233		return;
234	}
235	fTransfersSem = create_sem(0, "Transfers Queue Sem");
236	fTransfersThread = spawn_thread(TransfersThread, "Transfer Worker", B_NORMAL_PRIORITY, this);
237	resume_thread(fTransfersThread);
238	fInitCheck = true;
239}
240
241USBDeviceHandle::~USBDeviceHandle()
242{
243	if (fRawFD > 0)
244		close(fRawFD);
245	for(int i = 0; i < 32; i++) {
246		if (fClaimedInterfaces & (1 << i))
247			ReleaseInterface(i);
248	}
249	delete_sem(fTransfersSem);
250	if (fTransfersThread > 0)
251		wait_for_thread(fTransfersThread, NULL);
252}
253
254int
255USBDeviceHandle::ClaimInterface(int inumber)
256{
257	int status = fUSBDevice->ClaimInterface(inumber);
258	if (status == LIBUSB_SUCCESS)
259		fClaimedInterfaces |= (1 << inumber);
260	return status;
261}
262
263int
264USBDeviceHandle::ReleaseInterface(int inumber)
265{
266	fUSBDevice->ReleaseInterface(inumber);
267	fClaimedInterfaces &= ~(1 << inumber);
268	return LIBUSB_SUCCESS;
269}
270
271int
272USBDeviceHandle::SetConfiguration(int config)
273{
274	int config_index = fUSBDevice->CheckInterfacesFree(config);
275	if(config_index == LIBUSB_ERROR_BUSY || config_index == LIBUSB_ERROR_NOT_FOUND)
276		return config_index;
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);
282	}
283	fUSBDevice->SetActiveConfiguration(config_index);
284	return LIBUSB_SUCCESS;
285}
286
287int
288USBDeviceHandle::SetAltSetting(int inumber, int alt)
289{
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);
297	}
298	if (command.alternate.alternate_info == alt) {
299		usbi_dbg("Setting alternate interface successful");
300		return LIBUSB_SUCCESS;
301	}
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);
307	}
308	usbi_dbg("Setting alternate interface successful");
309	return LIBUSB_SUCCESS;
310}
311
312
313USBDevice::USBDevice(const char *path)
314	:
315	fPath(NULL),
316	fActiveConfiguration(0),	//0?
317	fConfigurationDescriptors(NULL),
318	fClaimedInterfaces(0),
319	fEndpointToIndex(NULL),
320	fEndpointToInterface(NULL),
321	fInitCheck(false)
322{
323	fPath=strdup(path);
324	Initialise();
325}
326
327USBDevice::~USBDevice()
328{
329	free(fPath);
330	if (fConfigurationDescriptors) {
331		for(int i = 0; i < fDeviceDescriptor.num_configurations; i++) {
332			if (fConfigurationDescriptors[i])
333				delete fConfigurationDescriptors[i];
334		}
335		delete[] fConfigurationDescriptors;
336	}
337	if (fEndpointToIndex)
338		delete[] fEndpointToIndex;
339	if (fEndpointToInterface)
340		delete[] fEndpointToInterface;
341}
342
343bool
344USBDevice::InitCheck()
345{
346	return fInitCheck;
347}
348
349const char *
350USBDevice::Location() const
351{
352	return fPath;
353}
354
355uint8
356USBDevice::CountConfigurations() const
357{
358	return fDeviceDescriptor.num_configurations;
359}
360
361const usb_device_descriptor *
362USBDevice::Descriptor() const
363{
364	return &fDeviceDescriptor;
365}
366
367const usb_configuration_descriptor *
368USBDevice::ConfigurationDescriptor(uint32 index) const
369{
370	if (index > CountConfigurations())
371		return NULL;
372	return (usb_configuration_descriptor *) fConfigurationDescriptors[index];
373}
374
375const usb_configuration_descriptor *
376USBDevice::ActiveConfiguration() const
377{
378	return (usb_configuration_descriptor *) fConfigurationDescriptors[fActiveConfiguration];
379}
380
381int
382USBDevice::ActiveConfigurationIndex() const
383{
384	return fActiveConfiguration;
385}
386
387int USBDevice::ClaimInterface(int interface)
388{
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;
395}
396
397int USBDevice::ReleaseInterface(int interface)
398{
399	fClaimedInterfaces &= ~(1 << interface);
400	return LIBUSB_SUCCESS;
401}
402
403int
404USBDevice::CheckInterfacesFree(int config)
405{
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;
411}
412
413int
414USBDevice::SetActiveConfiguration(int config_index)
415{
416	fActiveConfiguration = config_index;
417	return LIBUSB_SUCCESS;
418}
419
420uint8
421USBDevice::EndpointToIndex(uint8 address) const
422{
423	return fEndpointToIndex[fActiveConfiguration][address];
424}
425
426uint8
427USBDevice::EndpointToInterface(uint8 address) const
428{
429	return fEndpointToInterface[fActiveConfiguration][address];
430}
431
432int
433USBDevice::Initialise()		//Do we need more error checking, etc? How to report?
434{
435	int fRawFD = open(fPath, O_RDWR | O_CLOEXEC);
436	if (fRawFD < 0)
437		return B_ERROR;
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) {
442		close(fRawFD);
443		return B_ERROR;
444	}
445
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");
456			close(fRawFD);
457			return B_ERROR;
458		}
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");
470			close(fRawFD);
471			return B_ERROR;
472		}
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");
479				close(fRawFD);
480				return B_ERROR;
481			}
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");
492					close(fRawFD);
493					return B_ERROR;
494				}
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");
505						close(fRawFD);
506						return B_ERROR;
507					}
508					fEndpointToIndex[i][tmp_endpoint.endpoint_address] = l;
509					fEndpointToInterface[i][tmp_endpoint.endpoint_address] = j;
510				}
511			}
512		}
513	}
514	close(fRawFD);
515	fInitCheck = true;
516	return B_OK;
517}
518