xref: /haiku/src/add-ons/kernel/drivers/input/usb_hid/Driver.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
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 "DeviceList.h"
11 #include "Driver.h"
12 #include "HIDDevice.h"
13 #include "ProtocolHandler.h"
14 
15 #include <lock.h>
16 #include <util/AutoLock.h>
17 
18 #include <new>
19 #include <stdio.h>
20 #include <string.h>
21 
22 
23 struct device_cookie {
24 	ProtocolHandler*	handler;
25 	uint32				cookie;
26 };
27 
28 
29 int32 api_version = B_CUR_DRIVER_API_VERSION;
30 usb_module_info *gUSBModule = NULL;
31 DeviceList *gDeviceList = NULL;
32 static int32 sParentCookie = 0;
33 static mutex sDriverLock;
34 
35 
36 // #pragma mark - notify hooks
37 
38 
39 status_t
40 usb_hid_device_added(usb_device device, void **cookie)
41 {
42 	TRACE("device_added()\n");
43 	const usb_device_descriptor *deviceDescriptor
44 		= gUSBModule->get_device_descriptor(device);
45 
46 	TRACE("vendor id: 0x%04x; product id: 0x%04x\n",
47 		deviceDescriptor->vendor_id, deviceDescriptor->product_id);
48 
49 	// wacom devices are handled by the dedicated wacom driver
50 	if (deviceDescriptor->vendor_id == USB_VENDOR_WACOM)
51 		return B_ERROR;
52 
53 	const usb_configuration_info *config
54 		= gUSBModule->get_nth_configuration(device, USB_DEFAULT_CONFIGURATION);
55 	if (config == NULL) {
56 		TRACE_ALWAYS("cannot get default configuration\n");
57 		return B_ERROR;
58 	}
59 
60 	// ensure default configuration is set
61 	status_t result = gUSBModule->set_configuration(device, config);
62 	if (result != B_OK) {
63 		TRACE_ALWAYS("set_configuration() failed 0x%08lx\n", result);
64 		return result;
65 	}
66 
67 	// refresh config
68 	config = gUSBModule->get_configuration(device);
69 	if (config == NULL) {
70 		TRACE_ALWAYS("cannot get current configuration\n");
71 		return B_ERROR;
72 	}
73 
74 	bool devicesFound = false;
75 	int32 parentCookie = atomic_add(&sParentCookie, 1);
76 	for (size_t i = 0; i < config->interface_count; i++) {
77 		const usb_interface_info *interface = config->interface[i].active;
78 		uint8 interfaceClass = interface->descr->interface_class;
79 		TRACE("interface %lu: class: %u; subclass: %u; protocol: %u\n",
80 			i, interfaceClass, interface->descr->interface_subclass,
81 			interface->descr->interface_protocol);
82 
83 		if (interfaceClass == USB_INTERFACE_CLASS_HID) {
84 			mutex_lock(&sDriverLock);
85 			HIDDevice *hidDevice
86 				= new(std::nothrow) HIDDevice(device, config, i);
87 
88 			if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) {
89 				hidDevice->SetParentCookie(parentCookie);
90 
91 				for (uint32 i = 0;; i++) {
92 					ProtocolHandler *handler = hidDevice->ProtocolHandlerAt(i);
93 					if (handler == NULL)
94 						break;
95 
96 					// As devices can be un- and replugged at will, we cannot
97 					// simply rely on a device count. If there is just one
98 					// keyboard, this does not mean that it uses the 0 name.
99 					// There might have been two keyboards and the one using 0
100 					// might have been unplugged. So we just generate names
101 					// until we find one that is not currently in use.
102 					int32 index = 0;
103 					char pathBuffer[128];
104 					const char *basePath = handler->BasePath();
105 					while (true) {
106 						sprintf(pathBuffer, "%s%ld", basePath, index++);
107 						if (gDeviceList->FindDevice(pathBuffer) == NULL) {
108 							// this name is still free, use it
109 							handler->SetPublishPath(strdup(pathBuffer));
110 							break;
111 						}
112 					}
113 
114 					gDeviceList->AddDevice(handler->PublishPath(), handler);
115 					devicesFound = true;
116 				}
117 			} else
118 				delete hidDevice;
119 
120 			mutex_unlock(&sDriverLock);
121 		}
122 	}
123 
124 	if (!devicesFound)
125 		return B_ERROR;
126 
127 	*cookie = (void *)parentCookie;
128 	return B_OK;
129 }
130 
131 
132 status_t
133 usb_hid_device_removed(void *cookie)
134 {
135 	mutex_lock(&sDriverLock);
136 	int32 parentCookie = (int32)cookie;
137 	TRACE("device_removed(%ld)\n", parentCookie);
138 
139 	for (int32 i = 0; i < gDeviceList->CountDevices(); i++) {
140 		ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->DeviceAt(i);
141 		if (!handler)
142 			continue;
143 
144 		HIDDevice *device = handler->Device();
145 		if (device->ParentCookie() != parentCookie)
146 			continue;
147 
148 		// this handler's device belongs to the one removed
149 		if (device->IsOpen()) {
150 			// the device and it's handlers will be deleted in the free hook
151 			device->Removed();
152 			break;
153 		}
154 
155 		// remove all the handlers
156 		for (uint32 i = 0;; i++) {
157 			handler = device->ProtocolHandlerAt(i);
158 			if (handler == NULL)
159 				break;
160 
161 			gDeviceList->RemoveDevice(NULL, handler);
162 		}
163 
164 		delete device;
165 		break;
166 	}
167 
168 	mutex_unlock(&sDriverLock);
169 	return B_OK;
170 }
171 
172 
173 // #pragma mark - driver hooks
174 
175 
176 static status_t
177 usb_hid_open(const char *name, uint32 flags, void **_cookie)
178 {
179 	TRACE("open(%s, %lu, %p)\n", name, flags, _cookie);
180 
181 	device_cookie *cookie = new(std::nothrow) device_cookie();
182 	if (cookie == NULL)
183 		return B_NO_MEMORY;
184 
185 	MutexLocker locker(sDriverLock);
186 
187 	ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->FindDevice(name);
188 	TRACE("  name %s: handler %p\n", name, handler);
189 
190 	cookie->handler = handler;
191 	cookie->cookie = 0;
192 
193 	status_t result = handler == NULL ? B_ENTRY_NOT_FOUND : B_OK;
194 	if (result == B_OK)
195 		result = handler->Open(flags, &cookie->cookie);
196 
197 	if (result != B_OK) {
198 		delete cookie;
199 		return result;
200 	}
201 
202 	*_cookie = cookie;
203 
204 	return B_OK;
205 }
206 
207 
208 static status_t
209 usb_hid_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
210 {
211 	TRACE_ALWAYS("read on hid device\n");
212 	*numBytes = 0;
213 	return B_ERROR;
214 }
215 
216 
217 static status_t
218 usb_hid_write(void *cookie, off_t position, const void *buffer,
219 	size_t *numBytes)
220 {
221 	TRACE_ALWAYS("write on hid device\n");
222 	*numBytes = 0;
223 	return B_ERROR;
224 }
225 
226 
227 static status_t
228 usb_hid_control(void *_cookie, uint32 op, void *buffer, size_t length)
229 {
230 	device_cookie *cookie = (device_cookie *)_cookie;
231 
232 	TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length);
233 	return cookie->handler->Control(&cookie->cookie, op, buffer, length);
234 }
235 
236 
237 static status_t
238 usb_hid_close(void *_cookie)
239 {
240 	device_cookie *cookie = (device_cookie *)_cookie;
241 
242 	TRACE("close(%p)\n", cookie);
243 	return cookie->handler->Close(&cookie->cookie);
244 }
245 
246 
247 static status_t
248 usb_hid_free(void *_cookie)
249 {
250 	device_cookie *cookie = (device_cookie *)_cookie;
251 	TRACE("free(%p)\n", cookie);
252 
253 	mutex_lock(&sDriverLock);
254 
255 	HIDDevice *device = cookie->handler->Device();
256 	if (device->IsOpen()) {
257 		// another handler of this device is still open so we can't free it
258 	} else if (device->IsRemoved()) {
259 		// the parent device is removed already and none of its handlers are
260 		// open anymore so we can free it here
261 		for (uint32 i = 0;; i++) {
262 			ProtocolHandler *handler = device->ProtocolHandlerAt(i);
263 			if (handler == NULL)
264 				break;
265 
266 			gDeviceList->RemoveDevice(NULL, handler);
267 		}
268 
269 		delete device;
270 	}
271 
272 	mutex_unlock(&sDriverLock);
273 
274 	delete cookie;
275 	return B_OK;
276 }
277 
278 
279 //	#pragma mark - driver API
280 
281 
282 status_t
283 init_hardware()
284 {
285 	TRACE("init_hardware() " __DATE__ " " __TIME__ "\n");
286 	return B_OK;
287 }
288 
289 
290 status_t
291 init_driver()
292 {
293 	TRACE("init_driver() " __DATE__ " " __TIME__ "\n");
294 	if (get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule) != B_OK)
295 		return B_ERROR;
296 
297 	gDeviceList = new(std::nothrow) DeviceList();
298 	if (gDeviceList == NULL) {
299 		put_module(B_USB_MODULE_NAME);
300 		return B_NO_MEMORY;
301 	}
302 
303 	mutex_init(&sDriverLock, "usb hid driver lock");
304 
305 	static usb_notify_hooks notifyHooks = {
306 		&usb_hid_device_added,
307 		&usb_hid_device_removed
308 	};
309 
310 	static usb_support_descriptor supportDescriptor = {
311 		USB_INTERFACE_CLASS_HID, 0, 0, 0, 0
312 	};
313 
314 	gUSBModule->register_driver(DRIVER_NAME, &supportDescriptor, 1, NULL);
315 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
316 	TRACE("init_driver() OK\n");
317 	return B_OK;
318 }
319 
320 
321 void
322 uninit_driver()
323 {
324 	TRACE("uninit_driver()\n");
325 	gUSBModule->uninstall_notify(DRIVER_NAME);
326 	put_module(B_USB_MODULE_NAME);
327 	delete gDeviceList;
328 	gDeviceList = NULL;
329 	mutex_destroy(&sDriverLock);
330 }
331 
332 
333 const char **
334 publish_devices()
335 {
336 	TRACE("publish_devices()\n");
337 	const char **publishList = gDeviceList->PublishDevices();
338 
339 	int32 index = 0;
340 	while (publishList[index] != NULL) {
341 		TRACE("publishing %s\n", publishList[index]);
342 		index++;
343 	}
344 
345 	return publishList;
346 }
347 
348 
349 device_hooks *
350 find_device(const char *name)
351 {
352 	static device_hooks hooks = {
353 		usb_hid_open,
354 		usb_hid_close,
355 		usb_hid_free,
356 		usb_hid_control,
357 		usb_hid_read,
358 		usb_hid_write,
359 		NULL,				/* select */
360 		NULL				/* deselect */
361 	};
362 
363 	TRACE("find_device(%s)\n", name);
364 	if (gDeviceList->FindDevice(name) == NULL) {
365 		TRACE_ALWAYS("didn't find device %s\n", name);
366 		return NULL;
367 	}
368 
369 	return &hooks;
370 }
371