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