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