xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/Driver.cpp (revision 4b3b81da9e459443d75329cfd08bc9a57ad02653)
1 /*
2  * Copyright (c) 2007-2008 by Michael Lotz
3  * Heavily based on the original usb_serial driver which is:
4  *
5  * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
6  * Distributed under the terms of the MIT License.
7  */
8 #include <KernelExport.h>
9 #include <Drivers.h>
10 #include <malloc.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 
14 #include "Driver.h"
15 #include "SerialDevice.h"
16 #include "USB3.h"
17 
18 static const char *sDeviceBaseName = "ports/usb";
19 SerialDevice *gSerialDevices[DEVICES_COUNT];
20 char *gDeviceNames[DEVICES_COUNT + 1];
21 usb_module_info *gUSBModule = NULL;
22 tty_module_info *gTTYModule = NULL;
23 struct ddomain gSerialDomain;
24 sem_id gDriverLock = -1;
25 
26 
27 status_t
28 usb_serial_device_added(usb_device device, void **cookie)
29 {
30 	TRACE_FUNCALLS("> usb_serial_device_added(0x%08x, 0x%08x)\n", device, cookie);
31 
32 	status_t status = B_OK;
33 	const usb_device_descriptor *descriptor
34 		= gUSBModule->get_device_descriptor(device);
35 
36 	TRACE_ALWAYS("probing device: 0x%04x/0x%04x\n", descriptor->vendor_id,
37 		descriptor->product_id);
38 
39 	*cookie = NULL;
40 	SerialDevice *serialDevice = SerialDevice::MakeDevice(device,
41 		descriptor->vendor_id, descriptor->product_id);
42 
43 	const usb_configuration_info *configuration
44 		= gUSBModule->get_nth_configuration(device, 0);
45 
46 	if (!configuration)
47 		return B_ERROR;
48 
49 	status = serialDevice->AddDevice(configuration);
50 	if (status < B_OK) {
51 		delete serialDevice;
52 		return status;
53 	}
54 
55 	acquire_sem(gDriverLock);
56 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
57 		if (gSerialDevices[i] != NULL)
58 			continue;
59 
60 		status = serialDevice->Init();
61 		if (status < B_OK) {
62 			delete serialDevice;
63 			return status;
64 		}
65 
66 		gSerialDevices[i] = serialDevice;
67 		*cookie = serialDevice;
68 
69 		release_sem(gDriverLock);
70 		TRACE_ALWAYS("%s (0x%04x/0x%04x) added\n", serialDevice->Description(),
71 			descriptor->vendor_id, descriptor->product_id);
72 		return B_OK;
73 	}
74 
75 	release_sem(gDriverLock);
76 	return B_ERROR;
77 }
78 
79 
80 status_t
81 usb_serial_device_removed(void *cookie)
82 {
83 	TRACE_FUNCALLS("> usb_serial_device_removed(0x%08x)\n", cookie);
84 
85 	acquire_sem(gDriverLock);
86 
87 	SerialDevice *device = (SerialDevice *)cookie;
88 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
89 		if (gSerialDevices[i] == device) {
90 			if (device->IsOpen()) {
91 				// the device will be deleted upon being freed
92 				device->Removed();
93 			} else {
94 				delete device;
95 				gSerialDevices[i] = NULL;
96 			}
97 			break;
98 		}
99 	}
100 
101 	release_sem(gDriverLock);
102 	TRACE_FUNCRET("< usb_serial_device_removed() returns\n");
103 	return B_OK;
104 }
105 
106 
107 //#pragma mark -
108 
109 
110 /* init_hardware - called once the first time the driver is loaded */
111 status_t
112 init_hardware()
113 {
114 	TRACE("init_hardware\n");
115 	return B_OK;
116 }
117 
118 
119 /* init_driver - called every time the driver is loaded. */
120 status_t
121 init_driver()
122 {
123 	load_settings();
124 	create_log_file();
125 
126 	TRACE_FUNCALLS("> init_driver()\n");
127 
128 	status_t status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
129 	if (status < B_OK)
130 		return status;
131 
132 	status = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule);
133 	if (status < B_OK) {
134 		put_module(B_TTY_MODULE_NAME);
135 		return status;
136 	}
137 
138 	for (int32 i = 0; i < DEVICES_COUNT; i++)
139 		gSerialDevices[i] = NULL;
140 
141 	gDeviceNames[0] = NULL;
142 
143 	gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
144 	if (gDriverLock < B_OK) {
145 		put_module(B_USB_MODULE_NAME);
146 		put_module(B_TTY_MODULE_NAME);
147 		return gDriverLock;
148 	}
149 
150 	static usb_notify_hooks notifyHooks = {
151 		&usb_serial_device_added,
152 		&usb_serial_device_removed
153 	};
154 
155 	gUSBModule->register_driver(DRIVER_NAME, NULL, 0, NULL);
156 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
157 	TRACE_FUNCRET("< init_driver() returns\n");
158 	return B_OK;
159 }
160 
161 
162 /* uninit_driver - called every time the driver is unloaded */
163 void
164 uninit_driver()
165 {
166 	TRACE_FUNCALLS("> uninit_driver()\n");
167 
168 	gUSBModule->uninstall_notify(DRIVER_NAME);
169 	acquire_sem(gDriverLock);
170 
171 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
172 		if (gSerialDevices[i]) {
173 			delete gSerialDevices[i];
174 			gSerialDevices[i] = NULL;
175 		}
176 	}
177 
178 	for (int32 i = 0; gDeviceNames[i]; i++)
179 		free(gDeviceNames[i]);
180 
181 	delete_sem(gDriverLock);
182 	put_module(B_USB_MODULE_NAME);
183 	put_module(B_TTY_MODULE_NAME);
184 
185 	TRACE_FUNCRET("< uninit_driver() returns\n");
186 }
187 
188 
189 bool
190 usb_serial_service(struct tty *ptty, struct ddrover *ddr, uint flags)
191 {
192 	TRACE_FUNCALLS("> usb_serial_service(0x%08x, 0x%08x, 0x%08x)\n", ptty, ddr, flags);
193 
194 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
195 		if (gSerialDevices[i] && gSerialDevices[i]->Service(ptty, ddr, flags)) {
196 			TRACE_FUNCRET("< usb_serial_service() returns: true\n");
197 			return true;
198 		}
199 	}
200 
201 	TRACE_FUNCRET("< usb_serial_service() returns: false\n");
202 	return false;
203 }
204 
205 
206 /* usb_serial_open - handle open() calls */
207 static status_t
208 usb_serial_open(const char *name, uint32 flags, void **cookie)
209 {
210 	TRACE_FUNCALLS("> usb_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
211 	acquire_sem(gDriverLock);
212 	status_t status = ENODEV;
213 
214 	*cookie = NULL;
215 	int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
216 	if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
217 		status = gSerialDevices[i]->Open(flags);
218 		*cookie = gSerialDevices[i];
219 	}
220 
221 	release_sem(gDriverLock);
222 	TRACE_FUNCRET("< usb_serial_open() returns: 0x%08x\n", status);
223 	return status;
224 }
225 
226 
227 /* usb_serial_read - handle read() calls */
228 static status_t
229 usb_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
230 {
231 	TRACE_FUNCALLS("> usb_serial_read(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
232 		position, buffer, *numBytes);
233 	SerialDevice *device = (SerialDevice *)cookie;
234 	return device->Read((char *)buffer, numBytes);
235 }
236 
237 
238 /* usb_serial_write - handle write() calls */
239 static status_t
240 usb_serial_write(void *cookie, off_t position, const void *buffer,
241 	size_t *numBytes)
242 {
243 	TRACE_FUNCALLS("> usb_serial_write(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
244 		position, buffer, *numBytes);
245 	SerialDevice *device = (SerialDevice *)cookie;
246 	return device->Write((const char *)buffer, numBytes);
247 }
248 
249 
250 /* usb_serial_control - handle ioctl calls */
251 static status_t
252 usb_serial_control(void *cookie, uint32 op, void *arg, size_t length)
253 {
254 	TRACE_FUNCALLS("> usb_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
255 		cookie, op, arg, length);
256 	SerialDevice *device = (SerialDevice *)cookie;
257 	return device->Control(op, arg, length);
258 }
259 
260 
261 #if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__)
262 /* usb_serial_select - handle select start */
263 static status_t
264 usb_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
265 {
266 	TRACE_FUNCALLS("> usb_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
267 		cookie, event, ref, sync);
268 	SerialDevice *device = (SerialDevice *)cookie;
269 	return device->Select(event, ref, sync);
270 }
271 
272 
273 /* usb_serial_deselect - handle select exit */
274 static status_t
275 usb_serial_deselect(void *cookie, uint8 event, selectsync *sync)
276 {
277 	TRACE_FUNCALLS("> usb_serial_deselect(0x%08x, 0x%08x, %p)\n",
278 		cookie, event, sync);
279 	SerialDevice *device = (SerialDevice *)cookie;
280 	return device->DeSelect(event, sync);
281 }
282 #endif // DANO, HAIKU
283 
284 
285 /* usb_serial_close - handle close() calls */
286 static status_t
287 usb_serial_close(void *cookie)
288 {
289 	TRACE_FUNCALLS("> usb_serial_close(0x%08x)\n", cookie);
290 	SerialDevice *device = (SerialDevice *)cookie;
291 	return device->Close();
292 }
293 
294 
295 /* usb_serial_free - called after last device is closed, and all i/o complete. */
296 static status_t
297 usb_serial_free(void *cookie)
298 {
299 	TRACE_FUNCALLS("> usb_serial_free(0x%08x)\n", cookie);
300 	SerialDevice *device = (SerialDevice *)cookie;
301 	acquire_sem(gDriverLock);
302 	status_t status = device->Free();
303 	if (device->IsRemoved()) {
304 		for (int32 i = 0; i < DEVICES_COUNT; i++) {
305 			if (gSerialDevices[i] == device) {
306 				// the device is removed already but as it was open the
307 				// removed hook has not deleted the object
308 				delete device;
309 				gSerialDevices[i] = NULL;
310 				break;
311 			}
312 		}
313 	}
314 
315 	release_sem(gDriverLock);
316 	return status;
317 }
318 
319 
320 /* publish_devices - null-terminated array of devices supported by this driver. */
321 const char **
322 publish_devices()
323 {
324 	TRACE_FUNCALLS("> publish_devices()\n");
325 	for (int32 i = 0; gDeviceNames[i]; i++)
326 		free(gDeviceNames[i]);
327 
328 	int32 j = 0;
329 	acquire_sem(gDriverLock);
330 	for(int32 i = 0; i < DEVICES_COUNT; i++) {
331 		if (gSerialDevices[i]) {
332 			gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName + 4));
333 			if (gDeviceNames[j]) {
334 				sprintf(gDeviceNames[j], "%s%ld", sDeviceBaseName, i);
335 				j++;
336 			} else
337 				TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
338 		}
339 	}
340 
341 	gDeviceNames[j] = NULL;
342 	release_sem(gDriverLock);
343 	return (const char **)&gDeviceNames[0];
344 }
345 
346 
347 /* find_device - return poiter to device hooks structure for a given device */
348 device_hooks *
349 find_device(const char *name)
350 {
351 	static device_hooks deviceHooks = {
352 		usb_serial_open,			/* -> open entry point */
353 		usb_serial_close,			/* -> close entry point */
354 		usb_serial_free,			/* -> free cookie */
355 		usb_serial_control,			/* -> control entry point */
356 		usb_serial_read,			/* -> read entry point */
357 		usb_serial_write,			/* -> write entry point */
358 #if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__)
359 		usb_serial_select,			/* -> select entry point */
360 		usb_serial_deselect			/* -> deselect entry point */
361 #endif
362 	};
363 
364 	TRACE_FUNCALLS("> find_device(%s)\n", name);
365 	return &deviceHooks;
366 }
367