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