xref: /haiku/src/add-ons/kernel/drivers/input/i2c_hid/HIDDevice.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2  * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com.
3  * Copyright 2008-2011, Michael Lotz <mmlr@mlotz.ch>
4  * Distributed under the terms of the MIT license.
5  */
6 
7 
8 //!	Driver for I2C Human Interface Devices.
9 
10 
11 #include "Driver.h"
12 #include "HIDDevice.h"
13 #include "HIDReport.h"
14 #include "HIDWriter.h"
15 #include "ProtocolHandler.h"
16 
17 #include <usb/USB_hid.h>
18 
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <new>
24 
25 
26 HIDDevice::HIDDevice(uint16 descriptorAddress, i2c_device_interface* i2c,
27 	i2c_device i2cCookie)
28 	:	fStatus(B_NO_INIT),
29 		fTransferLastschedule(0),
30 		fTransferScheduled(0),
31 		fTransferBufferSize(0),
32 		fTransferBuffer(NULL),
33 		fOpenCount(0),
34 		fRemoved(false),
35 		fParser(this),
36 		fProtocolHandlerCount(0),
37 		fProtocolHandlerList(NULL),
38 		fDescriptorAddress(descriptorAddress),
39 		fI2C(i2c),
40 		fI2CCookie(i2cCookie)
41 {
42 	// fetch HID descriptor
43 	fStatus = _FetchBuffer((uint8*)&fDescriptorAddress,
44 		sizeof(fDescriptorAddress), &fDescriptor, sizeof(fDescriptor));
45 	if (fStatus != B_OK) {
46 		ERROR("failed to fetch HID descriptor\n");
47 		return;
48 	}
49 
50 	// fetch HID Report descriptor
51 
52 	HIDWriter descriptorWriter;
53 
54 	uint16 descriptorLength = fDescriptor.wReportDescLength;
55 	fReportDescriptor = (uint8 *)malloc(descriptorLength);
56 	if (fReportDescriptor == NULL) {
57 		ERROR("failed to allocate buffer for report descriptor\n");
58 		fStatus = B_NO_MEMORY;
59 		return;
60 	}
61 
62 	uint16 reportDescRegister = fDescriptor.wReportDescRegister;
63 	fStatus = _FetchBuffer((uint8*)&reportDescRegister,
64 		sizeof(reportDescRegister), fReportDescriptor,
65 		descriptorLength);
66 	if (fStatus != B_OK) {
67 		ERROR("failed tot get report descriptor\n");
68 		free(fReportDescriptor);
69 		return;
70 	}
71 
72 #if 1
73 	// save report descriptor for troubleshooting
74 	char outputFile[128];
75 	sprintf(outputFile, "/tmp/i2c_hid_report_descriptor_%04x_%04x.bin",
76 		fDescriptor.wVendorID, fDescriptor.wProductID);
77 	int fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
78 	if (fd >= 0) {
79 		write(fd, fReportDescriptor, descriptorLength);
80 		close(fd);
81 	}
82 #endif
83 
84 	status_t result = fParser.ParseReportDescriptor(fReportDescriptor,
85 		descriptorLength);
86 	free(fReportDescriptor);
87 
88 	if (result != B_OK) {
89 		ERROR("parsing the report descriptor failed\n");
90 		fStatus = result;
91 		return;
92 	}
93 
94 #if 0
95 	for (uint32 i = 0; i < fParser.CountReports(HID_REPORT_TYPE_ANY); i++)
96 		fParser.ReportAt(HID_REPORT_TYPE_ANY, i)->PrintToStream();
97 #endif
98 
99 	fTransferBufferSize = fParser.MaxReportSize();
100 	if (fTransferBufferSize == 0) {
101 		TRACE_ALWAYS("report claims a report size of 0\n");
102 		return;
103 	}
104 
105 	// We pad the allocation size so that we can always read 32 bits at a time
106 	// (as done in HIDReportItem) without the need for an additional boundary
107 	// check. We don't increase the transfer buffer size though as to not expose
108 	// this implementation detail onto the device when scheduling transfers.
109 	fTransferBuffer = (uint8 *)malloc(fTransferBufferSize + 3);
110 	if (fTransferBuffer == NULL) {
111 		TRACE_ALWAYS("failed to allocate transfer buffer\n");
112 		fStatus = B_NO_MEMORY;
113 		return;
114 	}
115 
116 	ProtocolHandler::AddHandlers(*this, fProtocolHandlerList,
117 		fProtocolHandlerCount);
118 	fStatus = B_OK;
119 }
120 
121 
122 HIDDevice::~HIDDevice()
123 {
124 	ProtocolHandler *handler = fProtocolHandlerList;
125 	while (handler != NULL) {
126 		ProtocolHandler *next = handler->NextHandler();
127 		delete handler;
128 		handler = next;
129 	}
130 
131 	free(fTransferBuffer);
132 }
133 
134 
135 status_t
136 HIDDevice::Open(ProtocolHandler *handler, uint32 flags)
137 {
138 	atomic_add(&fOpenCount, 1);
139 	_Reset();
140 
141 	return B_OK;
142 }
143 
144 
145 status_t
146 HIDDevice::Close(ProtocolHandler *handler)
147 {
148 	atomic_add(&fOpenCount, -1);
149 	_SetPower(I2C_HID_POWER_OFF);
150 
151 	return B_OK;
152 }
153 
154 
155 void
156 HIDDevice::Removed()
157 {
158 	fRemoved = true;
159 }
160 
161 
162 status_t
163 HIDDevice::MaybeScheduleTransfer(HIDReport *report)
164 {
165 	if (fRemoved)
166 		return ENODEV;
167 
168 	if (atomic_get_and_set(&fTransferScheduled, 1) != 0) {
169 		// someone else already caused a transfer to be scheduled
170 		return B_OK;
171 	}
172 
173 	snooze_until(fTransferLastschedule, B_SYSTEM_TIMEBASE);
174 	fTransferLastschedule = system_time() + 10000;
175 
176 	TRACE("scheduling interrupt transfer of %lu bytes\n",
177 		report->ReportSize());
178 	return _FetchReport(report->Type(), report->ID(), report->ReportSize());
179 }
180 
181 
182 status_t
183 HIDDevice::SendReport(HIDReport *report)
184 {
185 	// TODO
186 	return B_OK;
187 }
188 
189 
190 ProtocolHandler *
191 HIDDevice::ProtocolHandlerAt(uint32 index) const
192 {
193 	ProtocolHandler *handler = fProtocolHandlerList;
194 	while (handler != NULL) {
195 		if (index == 0)
196 			return handler;
197 
198 		handler = handler->NextHandler();
199 		index--;
200 	}
201 
202 	return NULL;
203 }
204 
205 
206 void
207 HIDDevice::_UnstallCallback(void *cookie, status_t status, void *data,
208 	size_t actualLength)
209 {
210 	HIDDevice *device = (HIDDevice *)cookie;
211 	if (status != B_OK) {
212 		TRACE_ALWAYS("Unable to unstall device: %s\n", strerror(status));
213 	}
214 
215 	// Now report the original failure, since we're ready to retry
216 	_TransferCallback(cookie, B_ERROR, device->fTransferBuffer, 0);
217 }
218 
219 
220 void
221 HIDDevice::_TransferCallback(void *cookie, status_t status, void *data,
222 	size_t actualLength)
223 {
224 	HIDDevice *device = (HIDDevice *)cookie;
225 
226 	atomic_set(&device->fTransferScheduled, 0);
227 	device->fParser.SetReport(status, device->fTransferBuffer, actualLength);
228 }
229 
230 
231 status_t
232 HIDDevice::_Reset()
233 {
234 	CALLED();
235 	status_t status = _SetPower(I2C_HID_POWER_ON);
236 	if (status != B_OK)
237 		return status;
238 
239 	snooze(1000);
240 
241 	uint8 cmd[] = {
242 		(uint8)(fDescriptor.wCommandRegister & 0xff),
243 		(uint8)(fDescriptor.wCommandRegister >> 8),
244 		0,
245 		I2C_HID_CMD_RESET,
246 	};
247 
248 	status = _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0);
249 	if (status != B_OK) {
250 		_SetPower(I2C_HID_POWER_OFF);
251 		return status;
252 	}
253 
254 	snooze(1000);
255 	return B_OK;
256 }
257 
258 
259 status_t
260 HIDDevice::_SetPower(uint8 power)
261 {
262 	CALLED();
263 	uint8 cmd[] = {
264 		(uint8)(fDescriptor.wCommandRegister & 0xff),
265 		(uint8)(fDescriptor.wCommandRegister >> 8),
266 		power,
267 		I2C_HID_CMD_SET_POWER
268 	};
269 
270 	return _ExecCommand(I2C_OP_WRITE_STOP, cmd, sizeof(cmd), NULL, 0);
271 }
272 
273 
274 status_t
275 HIDDevice::_FetchReport(uint8 type, uint8 id, size_t reportSize)
276 {
277 	uint8 reportId = id > 15 ? 15 : id;
278 	size_t cmdLength = 6;
279 	uint8 cmd[] = {
280 		(uint8)(fDescriptor.wCommandRegister & 0xff),
281 		(uint8)(fDescriptor.wCommandRegister >> 8),
282 		(uint8)(reportId | (type << 4)),
283 		I2C_HID_CMD_GET_REPORT,
284 		0, 0, 0,
285 	};
286 
287 	int dataOffset = 4;
288 	int reportIdLength = 1;
289 	if (reportId == 15) {
290 		cmd[dataOffset++] = id;
291 		cmdLength++;
292 		reportIdLength++;
293 	}
294 	cmd[dataOffset++] = fDescriptor.wDataRegister & 0xff;
295 	cmd[dataOffset++] = fDescriptor.wDataRegister >> 8;
296 
297 	size_t bufferLength = reportSize + reportIdLength + 2;
298 
299 	status_t status = _FetchBuffer(cmd, cmdLength, fTransferBuffer,
300 		bufferLength);
301 	if (status != B_OK) {
302 		atomic_set(&fTransferScheduled, 0);
303 		return status;
304 	}
305 
306 	uint16 actualLength = fTransferBuffer[0] | (fTransferBuffer[1] << 8);
307 	TRACE("_FetchReport %" B_PRIuSIZE " %" B_PRIu16 "\n", reportSize,
308 		actualLength);
309 	if (actualLength <= 2 || actualLength == 0xffff || bufferLength == 0)
310 		actualLength = 0;
311 	else
312 		actualLength -= 2;
313 
314 	atomic_set(&fTransferScheduled, 0);
315 	fParser.SetReport(status,
316 		(uint8*)((addr_t)fTransferBuffer + 2), actualLength);
317 	return B_OK;
318 }
319 
320 
321 status_t
322 HIDDevice::_FetchBuffer(uint8* cmd, size_t cmdLength, void* buffer,
323 	size_t bufferLength)
324 {
325 	return _ExecCommand(I2C_OP_READ_STOP, cmd, cmdLength,
326 		buffer, bufferLength);
327 }
328 
329 
330 status_t
331 HIDDevice::_ExecCommand(i2c_op op, uint8* cmd, size_t cmdLength, void* buffer,
332 	size_t bufferLength)
333 {
334 	status_t status = fI2C->acquire_bus(fI2CCookie);
335 	if (status != B_OK)
336 		return status;
337 	status = fI2C->exec_command(fI2CCookie, I2C_OP_READ_STOP, cmd, cmdLength,
338 		buffer, bufferLength);
339 	fI2C->release_bus(fI2CCookie);
340 	return status;
341 }
342