xref: /haiku/src/add-ons/kernel/drivers/input/usb_hid/HIDDevice.cpp (revision 97901ec593ec4dd50ac115c1c35a6d72f6e489a5)
1 /*
2  * Copyright 2008-2009 Michael Lotz <mmlr@mlotz.ch>
3  * Distributed under the terms of the MIT license.
4  */
5 
6 
7 //!	Driver for USB Human Interface Devices.
8 
9 
10 #include "Driver.h"
11 #include "HIDDevice.h"
12 #include "HIDReport.h"
13 #include "ProtocolHandler.h"
14 
15 #include <usb/USB_hid.h>
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <new>
22 
23 
24 HIDDevice::HIDDevice(usb_device device, const usb_configuration_info *config,
25 	size_t interfaceIndex)
26 	:	fStatus(B_NO_INIT),
27 		fDevice(device),
28 		fInterfaceIndex(interfaceIndex),
29 		fTransferScheduled(0),
30 		fTransferBufferSize(0),
31 		fTransferBuffer(NULL),
32 		fParentCookie(-1),
33 		fOpenCount(0),
34 		fRemoved(false),
35 		fParser(this),
36 		fProtocolHandlerCount(0),
37 		fProtocolHandlers(NULL)
38 {
39 	// read HID descriptor
40 	size_t descriptorLength = sizeof(usb_hid_descriptor);
41 	usb_hid_descriptor *hidDescriptor
42 		= (usb_hid_descriptor *)malloc(descriptorLength);
43 	if (hidDescriptor == NULL) {
44 		TRACE_ALWAYS("failed to allocate buffer for hid descriptor\n");
45 		fStatus = B_NO_MEMORY;
46 		return;
47 	}
48 
49 	status_t result = gUSBModule->send_request(device,
50 		USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
51 		USB_REQUEST_GET_DESCRIPTOR,
52 		USB_HID_DESCRIPTOR_HID << 8, interfaceIndex, descriptorLength,
53 		hidDescriptor, &descriptorLength);
54 
55 	TRACE("get hid descriptor: result: 0x%08lx; length: %lu\n", result,
56 		descriptorLength);
57 	if (result == B_OK)
58 		descriptorLength = hidDescriptor->descriptor_info[0].descriptor_length;
59 	else
60 		descriptorLength = 256; /* XXX */
61 	free(hidDescriptor);
62 
63 	uint8 *reportDescriptor = (uint8 *)malloc(descriptorLength);
64 	if (reportDescriptor == NULL) {
65 		TRACE_ALWAYS("failed to allocate buffer for report descriptor\n");
66 		fStatus = B_NO_MEMORY;
67 		return;
68 	}
69 
70 	result = gUSBModule->send_request(device,
71 		USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_STANDARD,
72 		USB_REQUEST_GET_DESCRIPTOR,
73 		USB_HID_DESCRIPTOR_REPORT << 8, interfaceIndex, descriptorLength,
74 		reportDescriptor, &descriptorLength);
75 
76 	TRACE("get report descriptor: result: 0x%08lx; length: %lu\n", result,
77 		descriptorLength);
78 	if (result != B_OK) {
79 		TRACE_ALWAYS("failed tot get report descriptor\n");
80 		free(reportDescriptor);
81 		return;
82 	}
83 
84 #if 1
85 	// save report descriptor for troubleshooting
86 	const usb_device_descriptor *deviceDescriptor
87 		= gUSBModule->get_device_descriptor(device);
88 	char outputFile[128];
89 	sprintf(outputFile, "/tmp/usb_hid_report_descriptor_%04x_%04x_%lu.bin",
90 		deviceDescriptor->vendor_id, deviceDescriptor->product_id,
91 		interfaceIndex);
92 	int fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
93 	if (fd >= 0) {
94 		write(fd, reportDescriptor, descriptorLength);
95 		close(fd);
96 	}
97 #endif
98 
99 	result = fParser.ParseReportDescriptor(reportDescriptor, descriptorLength);
100 	free(reportDescriptor);
101 	if (result != B_OK) {
102 		TRACE_ALWAYS("parsing the report descriptor failed\n");
103 		fStatus = result;
104 		return;
105 	}
106 
107 #if 0
108 	for (uint32 i = 0; i < fParser.CountReports(HID_REPORT_TYPE_ANY); i++)
109 		fParser.ReportAt(HID_REPORT_TYPE_ANY, i)->PrintToStream();
110 #endif
111 
112 	// find the interrupt in pipe
113 	usb_interface_info *interface = config->interface[interfaceIndex].active;
114 	for (size_t i = 0; i < interface->endpoint_count; i++) {
115 		usb_endpoint_descriptor *descriptor = interface->endpoint[i].descr;
116 		if ((descriptor->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
117 			&& (descriptor->attributes & USB_ENDPOINT_ATTR_MASK)
118 				== USB_ENDPOINT_ATTR_INTERRUPT) {
119 			fInterruptPipe = interface->endpoint[i].handle;
120 			break;
121 		}
122 	}
123 
124 	if (fInterruptPipe == 0) {
125 		TRACE_ALWAYS("didn't find a suitable interrupt pipe\n");
126 		return;
127 	}
128 
129 	fTransferBufferSize = fParser.MaxReportSize();
130 	if (fTransferBufferSize == 0) {
131 		TRACE_ALWAYS("report claims a report size of 0\n");
132 		return;
133 	}
134 
135 	fTransferBuffer = (uint8 *)malloc(fTransferBufferSize);
136 	if (fTransferBuffer == NULL) {
137 		TRACE_ALWAYS("failed to allocate transfer buffer\n");
138 		fStatus = B_NO_MEMORY;
139 		return;
140 	}
141 
142 	ProtocolHandler::AddHandlers(this, &fProtocolHandlers,
143 		&fProtocolHandlerCount);
144 	fStatus = B_OK;
145 }
146 
147 
148 HIDDevice::~HIDDevice()
149 {
150 	for (uint32 i = 0; i < fProtocolHandlerCount; i++)
151 		delete fProtocolHandlers[i];
152 
153 	free(fProtocolHandlers);
154 	free(fTransferBuffer);
155 }
156 
157 
158 void
159 HIDDevice::SetParentCookie(int32 cookie)
160 {
161 	fParentCookie = cookie;
162 }
163 
164 
165 status_t
166 HIDDevice::Open(ProtocolHandler *handler, uint32 flags)
167 {
168 	atomic_add(&fOpenCount, 1);
169 	return B_OK;
170 }
171 
172 
173 status_t
174 HIDDevice::Close(ProtocolHandler *handler)
175 {
176 	atomic_add(&fOpenCount, -1);
177 	return B_OK;
178 }
179 
180 
181 void
182 HIDDevice::Removed()
183 {
184 	fRemoved = true;
185 	gUSBModule->cancel_queued_transfers(fInterruptPipe);
186 }
187 
188 
189 status_t
190 HIDDevice::MaybeScheduleTransfer()
191 {
192 	if (fRemoved)
193 		return B_ERROR;
194 
195 	if (atomic_set(&fTransferScheduled, 1) != 0) {
196 		// someone else already caused a transfer to be scheduled
197 		return B_OK;
198 	}
199 
200 	TRACE("scheduling interrupt transfer of %lu bytes\n", fTransferBufferSize);
201 	status_t result = gUSBModule->queue_interrupt(fInterruptPipe,
202 		fTransferBuffer, fTransferBufferSize, _TransferCallback, this);
203 	if (result != B_OK) {
204 		TRACE_ALWAYS("failed to schedule interrupt transfer 0x%08lx\n", result);
205 		return result;
206 	}
207 
208 	return B_OK;
209 }
210 
211 
212 status_t
213 HIDDevice::SendReport(HIDReport *report)
214 {
215 	size_t actualLength;
216 	return gUSBModule->send_request(fDevice,
217 		USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS,
218 		USB_REQUEST_HID_SET_REPORT, 0x200 | report->ID(), fInterfaceIndex,
219 		report->ReportSize(), report->CurrentReport(), &actualLength);
220 }
221 
222 
223 ProtocolHandler *
224 HIDDevice::ProtocolHandlerAt(uint32 index) const
225 {
226 	if (index >= fProtocolHandlerCount)
227 		return NULL;
228 	return fProtocolHandlers[index];
229 }
230 
231 
232 void
233 HIDDevice::_TransferCallback(void *cookie, status_t status, void *data,
234 	size_t actualLength)
235 {
236 	HIDDevice *device = (HIDDevice *)cookie;
237 	if (status == B_DEV_STALLED && !device->fRemoved) {
238 		// try clearing stalls right away, the report listeners will resubmit
239 		gUSBModule->clear_feature(device->fInterruptPipe,
240 			USB_FEATURE_ENDPOINT_HALT);
241 	}
242 
243 	atomic_set(&device->fTransferScheduled, 0);
244 	device->fParser.SetReport(status, device->fTransferBuffer, actualLength);
245 }
246