xref: /haiku/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp (revision 425ac1b60a56f4df7a0e88bd784545c0ec4fa01f)
1 /*
2  * Copyright 2009-2010, François Revol, <revol@free.fr>.
3  * Sponsored by TuneTracker Systems.
4  * Based on the Haiku usb_serial driver which is:
5  *
6  * Copyright (c) 2007-2008 by Michael Lotz
7  * Heavily based on the original usb_serial driver which is:
8  *
9  * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
10  * Distributed under the terms of the MIT License.
11  */
12 #include <KernelExport.h>
13 #include <dpc.h>
14 #include <Drivers.h>
15 #include <driver_settings.h>
16 #include <image.h>
17 #include <kernel/safemode.h>
18 #include <malloc.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 
22 #include "Driver.h"
23 #include "SerialDevice.h"
24 
25 int32 api_version = B_CUR_DRIVER_API_VERSION;
26 static const char *sDeviceBaseName = DEVFS_BASE;
27 SerialDevice *gSerialDevices[DEVICES_COUNT];
28 char *gDeviceNames[DEVICES_COUNT + 1];
29 isa_module_info *gISAModule = NULL;
30 pci_module_info *gPCIModule = NULL;
31 tty_module_info *gTTYModule = NULL;
32 dpc_module_info *gDPCModule = NULL;
33 void* gDPCHandle = NULL;
34 sem_id gDriverLock = -1;
35 bool gHandleISA = false;
36 uint32 gKernelDebugPort = 0x3f8;
37 
38 // 24 MHz clock
39 static const uint32 sDefaultRates[] = {
40 		0,		//B0
41 		2304,	//B50
42 		1536,	//B75
43 		1047,	//B110
44 		857,	//B134
45 		768,	//B150
46 		512,	//B200
47 		384,	//B300
48 		192,	//B600
49 		96,		//B1200
50 		64,		//B1800
51 		48,		//B2400
52 		24,		//B4800
53 		12,		//B9600
54 		6,		//B19200
55 		3,		//B38400
56 		2,		//B57600
57 		1,		//B115200
58 		0,		//B230400
59 		4,		//B31250
60 		0, //921600 !?
61 };
62 
63 // 8MHz clock on serial3 and 4 on the BeBox
64 #if 0
65 static const uint32 sBeBoxRates[] = {
66 		0,		//B0
67 		//... TODO
68 };
69 #endif
70 
71 // XXX: should really be generated from metadata (CSV ?)
72 
73 static const struct serial_support_descriptor sSupportedDevices[] = {
74 
75 #ifdef HANDLE_ISA_COM
76 	// ISA devices
77 	{ B_ISA_BUS, "Generic 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
78 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550 } },
79 #endif
80 	// PCI devices
81 
82 	// vendor/device matches first
83 
84 /*
85 	// vendor: OxfordSemi
86 #define VN "OxfordSemi"
87 	// http://www.softio.com/ox16pci954ds.pdf
88 	{ B_PCI_BUS, "OxfordSemi 16950 Serial Port", sDefaultRates, NULL, { 32, 32, 8 },
89 	  { PCI_simple_communications, PCI_serial, PCI_serial_16950,
90 		0x1415, 0x9501, PCI_INVAL, PCI_INVAL } },
91 
92 	// http://www.softio.com/ox16pci952ds.pdf
93 	{ B_PCI_BUS, "OxfordSemi 16950 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
94 	  { PCI_simple_communications, PCI_serial, PCI_serial_16950,
95 		0x1415, 0x9521, PCI_INVAL, PCI_INVAL } },
96 */
97 
98 
99 	// vendor: NetMos
100 #define VN "MosChip"
101 
102 	// used in Manhattan cards
103 	// 1 function / port
104 	// http://www.moschip.com/data/products/MCS9865/Data%20Sheet_9865.pdf
105 	{ B_PCI_BUS, VN" 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8, 0, 0, 0 },
106 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550,
107 		0x9710, 0x9865, PCI_INVAL, PCI_INVAL } },
108 
109 	// single function with all ports
110 	// only BAR 0 & 1 are UART
111 	// http://www.moschip.com/data/products/NM9835/Data%20Sheet_9835.pdf
112 	{ B_PCI_BUS, VN" 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8, (uint8)~0x3, 2, 0x000f },
113 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550,
114 		0x9710, 0x9835, PCI_INVAL, PCI_INVAL } },
115 
116 #undef VN
117 
118 
119 
120 	// generic fallback matches
121 	/*
122 	{ B_PCI_BUS, "Generic XT Serial Port", NULL },
123 	  { PCI_INVAL, PCI_INVAL, PCI_simple_communications,
124 		PCI_serial, PCI_serial_xt, PCI_INVAL, PCI_INVAL } },
125 
126 	{ B_PCI_BUS, "Generic 16450 Serial Port", NULL },
127 	  { PCI_INVAL, PCI_INVAL, PCI_simple_communications,
128 		PCI_serial, PCI_serial_16450, PCI_INVAL, PCI_INVAL } },
129 
130 	*/
131 	{ B_PCI_BUS, "Generic 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
132 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550,
133 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
134 
135 	{ B_PCI_BUS, "Generic 16650 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
136 	  { PCI_simple_communications, PCI_serial, PCI_serial_16650,
137 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
138 
139 	{ B_PCI_BUS, "Generic 16750 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
140 	  { PCI_simple_communications, PCI_serial, PCI_serial_16750,
141 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
142 
143 	{ B_PCI_BUS, "Generic 16850 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
144 	  { PCI_simple_communications, PCI_serial, PCI_serial_16850,
145 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
146 
147 	{ B_PCI_BUS, "Generic 16950 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
148 	  { PCI_simple_communications, PCI_serial, PCI_serial_16950,
149 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
150 
151 	// non PCI_serial devices
152 
153 	// beos zz driver supported that one
154 	{ B_PCI_BUS, "Lucent Modem", sDefaultRates, NULL, { 8, 8, 8 },
155 	  { PCI_simple_communications, PCI_simple_communications_other, 0x00,
156 		0x11C1, 0x0480, PCI_INVAL, PCI_INVAL } },
157 
158 	{ B_PCI_BUS, NULL, NULL, NULL, {0}, {0} }
159 };
160 
161 
162 // hardcoded ISA ports
163 static struct isa_ports {
164 	uint32 ioBase;
165 	uint32 irq;
166 } sHardcodedPorts[] = {
167 	{ 0x3f8, 4 },
168 	{ 0x2f8, 3 },
169 	{ 0x3e8, 4 },
170 	{ 0x2e8, 3 },
171 };
172 
173 #if 0
174 status_t
175 pc_serial_device_added(pc_device device, void **cookie)
176 {
177 	TRACE_FUNCALLS("> pc_serial_device_added(0x%08x, 0x%08x)\n", device, cookie);
178 
179 	status_t status = B_OK;
180 	const pc_device_descriptor *descriptor
181 		= gUSBModule->get_device_descriptor(device);
182 
183 	TRACE_ALWAYS("probing device: 0x%04x/0x%04x\n", descriptor->vendor_id,
184 		descriptor->product_id);
185 
186 	*cookie = NULL;
187 	SerialDevice *serialDevice = SerialDevice::MakeDevice(device,
188 		descriptor->vendor_id, descriptor->product_id);
189 
190 	const pc_configuration_info *configuration
191 		= gUSBModule->get_nth_configuration(device, 0);
192 
193 	if (!configuration)
194 		return B_ERROR;
195 
196 	status = serialDevice->AddDevice(configuration);
197 	if (status < B_OK) {
198 		delete serialDevice;
199 		return status;
200 	}
201 
202 	acquire_sem(gDriverLock);
203 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
204 		if (gSerialDevices[i] != NULL)
205 			continue;
206 
207 		status = serialDevice->Init();
208 		if (status < B_OK) {
209 			delete serialDevice;
210 			return status;
211 		}
212 
213 		gSerialDevices[i] = serialDevice;
214 		*cookie = serialDevice;
215 
216 		release_sem(gDriverLock);
217 		TRACE_ALWAYS("%s (0x%04x/0x%04x) added\n", serialDevice->Description(),
218 			descriptor->vendor_id, descriptor->product_id);
219 		return B_OK;
220 	}
221 
222 	release_sem(gDriverLock);
223 	return B_ERROR;
224 }
225 
226 
227 status_t
228 pc_serial_device_removed(void *cookie)
229 {
230 	TRACE_FUNCALLS("> pc_serial_device_removed(0x%08x)\n", cookie);
231 
232 	acquire_sem(gDriverLock);
233 
234 	SerialDevice *device = (SerialDevice *)cookie;
235 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
236 		if (gSerialDevices[i] == device) {
237 			if (device->IsOpen()) {
238 				// the device will be deleted upon being freed
239 				device->Removed();
240 			} else {
241 				delete device;
242 				gSerialDevices[i] = NULL;
243 			}
244 			break;
245 		}
246 	}
247 
248 	release_sem(gDriverLock);
249 	TRACE_FUNCRET("< pc_serial_device_removed() returns\n");
250 	return B_OK;
251 }
252 #endif
253 
254 //#pragma mark -
255 
256 status_t
pc_serial_insert_device(SerialDevice * device)257 pc_serial_insert_device(SerialDevice *device)
258 {
259 	status_t status = B_OK;
260 
261 	//XXX fix leaks!
262 	acquire_sem(gDriverLock);
263 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
264 		if (gSerialDevices[i] != NULL)
265 			continue;
266 
267 		status = device->Init();
268 		if (status < B_OK) {
269 			delete device;
270 			//return status;
271 			break;
272 		}
273 
274 		gSerialDevices[i] = device;
275 
276 		release_sem(gDriverLock);
277 		TRACE_ALWAYS("%s added\n", device->Description());
278 		return B_OK;
279 	}
280 
281 	release_sem(gDriverLock);
282 	return B_ERROR;
283 }
284 
285 
286 // until we support ISA device enumeration from PnP BIOS or ACPI,
287 // we have to probe the 4 default COM ports...
288 status_t
scan_isa_hardcoded()289 scan_isa_hardcoded()
290 {
291 #ifdef HANDLE_ISA_COM
292 	int i;
293 	bool serialDebug = get_safemode_boolean("serial_debug_output", true);
294 
295 	for (i = 0; i < 4; i++) {
296 		// skip the port used for kernel debugging...
297 		if (serialDebug && sHardcodedPorts[i].ioBase == gKernelDebugPort) {
298 			TRACE_ALWAYS("Skipping port %d as it is used for kernel debug.\n", i);
299 			continue;
300 		}
301 
302 		SerialDevice *device;
303 		device = new(std::nothrow) SerialDevice(&sSupportedDevices[0],
304 			sHardcodedPorts[i].ioBase, sHardcodedPorts[i].irq);
305 		if (device != NULL && device->Probe())
306 			pc_serial_insert_device(device);
307 		else
308 			delete device;
309 	}
310 #endif
311 	return B_OK;
312 }
313 
314 
315 status_t
scan_pci()316 scan_pci()
317 {
318 	pci_info info;
319 	int ix;
320 	TRACE_ALWAYS("scanning PCI bus (alt)...\n");
321 
322 	// probe PCI devices
323 	for (ix = 0; (*gPCIModule->get_nth_pci_info)(ix, &info) == B_OK; ix++) {
324 		// sanity check
325 		if ((info.header_type & PCI_header_type_mask) != PCI_header_type_generic)
326 			continue;
327 		/*
328 		TRACE_ALWAYS("probing PCI device %2d [%x|%x|%x] %04x:%04x\n",
329 			ix, info.class_base, info.class_sub, info.class_api,
330 			info.vendor_id, info.device_id);
331 		*/
332 
333 		const struct serial_support_descriptor *supported = NULL;
334 		for (int i = 0; sSupportedDevices[i].name; i++) {
335 			if (sSupportedDevices[i].bus != B_PCI_BUS)
336 				continue;
337 			if (info.class_base != sSupportedDevices[i].match.class_base)
338 				continue;
339 			if (info.class_sub != sSupportedDevices[i].match.class_sub)
340 				continue;
341 			if (info.class_api != sSupportedDevices[i].match.class_api)
342 				continue;
343 			if (sSupportedDevices[i].match.vendor_id != PCI_INVAL
344 				&& info.vendor_id != sSupportedDevices[i].match.vendor_id)
345 				continue;
346 			if (sSupportedDevices[i].match.device_id != PCI_INVAL
347 				&& info.device_id != sSupportedDevices[i].match.device_id)
348 				continue;
349 			supported = &sSupportedDevices[i];
350 			break;
351 		}
352 		if (supported == NULL)
353 			continue;
354 
355 		TRACE_ALWAYS("found PCI device %2d [%x|%x|%x] %04x:%04x as %s\n",
356 			ix, info.class_base, info.class_sub, info.class_api,
357 			info.vendor_id, info.device_id, supported->name);
358 
359 		// XXX: interrupt_line doesn't seem to
360 		TRACE_ALWAYS("irq line %d, pin %d\n",
361 			info.u.h0.interrupt_line, info.u.h0.interrupt_pin);
362 		int irq = info.u.h0.interrupt_line;
363 
364 		SerialDevice *master = NULL;
365 
366 		uint8 portCount = 0;
367 		uint32 maxPorts = DEVICES_COUNT;
368 
369 		if (supported->constraints.maxports) {
370 			maxPorts = supported->constraints.maxports;
371 			TRACE_ALWAYS("card supports up to %d ports\n", maxPorts);
372 		}
373 		if (supported->constraints.subsystem_id_mask) {
374 			uint32 id = info.u.h0.subsystem_id;
375 			uint32 mask = supported->constraints.subsystem_id_mask;
376 			id &= mask;
377 			//TRACE_ALWAYS("mask: %lx, masked: %lx\n", mask, id);
378 			while (!(mask & 0x1)) {
379 				mask >>= 1;
380 				id >>= 1;
381 			}
382 			maxPorts = (uint8)id;
383 			TRACE_ALWAYS("subsystem id tells card has %d ports\n", maxPorts);
384 		}
385 
386 		// find I/O ports
387 		for (int r = 0; r < 6; r++) {
388 			uint32 regbase = info.u.h0.base_registers[r];
389 			uint32 reglen = info.u.h0.base_register_sizes[r];
390 
391 			/**/
392 			TRACE("ranges[%d] at 0x%08lx len 0x%lx flags 0x%02x\n", r,
393 				regbase, reglen, info.u.h0.base_register_flags[r]);
394 			/**/
395 
396 			// empty
397 			if (reglen == 0)
398 				continue;
399 
400 			// not I/O
401 			if ((info.u.h0.base_register_flags[r] & PCI_address_space) == 0)
402 				continue;
403 
404 			// the range for sure doesn't contain any UART
405 			if (supported->constraints.ignoremask & (1 << r)) {
406 				TRACE_ALWAYS("ignored regs at 0x%08lx len 0x%lx\n",
407 					regbase, reglen);
408 				continue;
409 			}
410 
411 			TRACE_ALWAYS("regs at 0x%08lx len 0x%lx\n",
412 				regbase, reglen);
413 			//&PCI_address_io_mask
414 
415 			if (reglen < supported->constraints.minsize)
416 				continue;
417 			if (reglen > supported->constraints.maxsize)
418 				continue;
419 
420 			SerialDevice *device;
421 			uint32 ioport = regbase;
422 next_split_alt:
423 			// no more to split
424 			if ((ioport - regbase) >= reglen)
425 				continue;
426 
427 			if (portCount >= maxPorts)
428 				break;
429 
430 			TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
431 				supported->name);
432 
433 
434 /**/
435 			device = new(std::nothrow) SerialDevice(supported, ioport, irq, master);
436 			if (device == NULL) {
437 				TRACE_ALWAYS("can't allocate device\n");
438 				continue;
439 			}
440 
441 			if (pc_serial_insert_device(device) < B_OK) {
442 				TRACE_ALWAYS("can't insert device\n");
443 				continue;
444 			}
445 /**/			if (master == NULL)
446 				master = device;
447 
448 			ioport += supported->constraints.split;
449 			portCount++;
450 			goto next_split_alt;
451 			// try next part of the I/O range now
452 
453 		}
454 	}
455 
456 	return B_OK;
457 }
458 
459 
460 static void
check_kernel_debug_port()461 check_kernel_debug_port()
462 {
463 	void *handle;
464 	long int value;
465 
466 	handle = load_driver_settings("kernel");
467 	if (handle == NULL)
468 		return;
469 
470 	const char *str = get_driver_parameter(handle, "serial_debug_port",
471 		NULL, NULL);
472 	if (str != NULL) {
473 		value = strtol(str, NULL, 0);
474 		if (value >= 4) // XXX: actually should be MAX_SERIAL_PORTS...
475 			gKernelDebugPort = (uint32)value;
476 		else if (value >= 0) // XXX: we should use the kernel_arg's table...
477 			gKernelDebugPort = sHardcodedPorts[value].ioBase;
478 	}
479 
480 	/* TODO: actually handle this in the kernel debugger too!
481 	bool enabled = get_driver_boolean_parameter(handle, "serial_debug_output",
482 		false, true);
483 	if (!enabled)
484 		gKernelDebugPort = 0;
485 	*/
486 
487 	unload_driver_settings(handle);
488 }
489 
490 
491 //#pragma mark -
492 
493 
494 /* init_hardware - called once the first time the driver is loaded */
495 status_t
init_hardware()496 init_hardware()
497 {
498 	TRACE("init_hardware\n");
499 	return B_OK;
500 }
501 
502 
503 /* init_driver - called every time the driver is loaded. */
504 status_t
init_driver()505 init_driver()
506 {
507 	status_t status;
508 	load_settings();
509 	create_log_file();
510 
511 	TRACE_FUNCALLS("> init_driver()\n");
512 
513 	status = get_module(B_DPC_MODULE_NAME, (module_info **)&gDPCModule);
514 	if (status < B_OK)
515 		goto err_dpc;
516 
517 	status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
518 	if (status < B_OK)
519 		goto err_tty;
520 
521 	status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCIModule);
522 	if (status < B_OK)
523 		goto err_pci;
524 
525 	status = get_module(B_ISA_MODULE_NAME, (module_info **)&gISAModule);
526 	if (status < B_OK)
527 		goto err_isa;
528 
529 	status = gDPCModule->new_dpc_queue(&gDPCHandle, "pc_serial irq",
530 		B_REAL_TIME_PRIORITY);
531 	if (status != B_OK)
532 		goto err_dpcq;
533 
534 	for (int32 i = 0; i < DEVICES_COUNT; i++)
535 		gSerialDevices[i] = NULL;
536 
537 	gDeviceNames[0] = NULL;
538 
539 	gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
540 	if (gDriverLock < B_OK) {
541 		status = gDriverLock;
542 		goto err_sem;
543 	}
544 
545 	status = ENOENT;
546 
547 	check_kernel_debug_port();
548 
549 	scan_isa_hardcoded();
550 	scan_pci();
551 
552 	// XXX: ISA cards
553 	// XXX: pcmcia
554 
555 	TRACE_FUNCRET("< init_driver() returns\n");
556 	return B_OK;
557 
558 //err_none:
559 	delete_sem(gDriverLock);
560 err_sem:
561 	gDPCModule->delete_dpc_queue(gDPCHandle);
562 	gDPCHandle = NULL;
563 err_dpcq:
564 	put_module(B_ISA_MODULE_NAME);
565 err_isa:
566 	put_module(B_PCI_MODULE_NAME);
567 err_pci:
568 	put_module(B_TTY_MODULE_NAME);
569 err_tty:
570 	put_module(B_DPC_MODULE_NAME);
571 err_dpc:
572 	TRACE_FUNCRET("< init_driver() returns %s\n", strerror(status));
573 	return status;
574 }
575 
576 
577 /* uninit_driver - called every time the driver is unloaded */
578 void
uninit_driver()579 uninit_driver()
580 {
581 	TRACE_FUNCALLS("> uninit_driver()\n");
582 
583 	//gUSBModule->uninstall_notify(DRIVER_NAME);
584 	acquire_sem(gDriverLock);
585 
586 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
587 		if (gSerialDevices[i]) {
588 			/*
589 			if (gSerialDevices[i]->Master() == gSerialDevices[i])
590 				remove_io_interrupt_handler(gSerialDevices[i]->IRQ(),
591 					pc_serial_interrupt, gSerialDevices[i]);
592 			*/
593 			delete gSerialDevices[i];
594 			gSerialDevices[i] = NULL;
595 		}
596 	}
597 
598 	for (int32 i = 0; gDeviceNames[i]; i++)
599 		free(gDeviceNames[i]);
600 
601 	delete_sem(gDriverLock);
602 	gDPCModule->delete_dpc_queue(gDPCHandle);
603 	gDPCHandle = NULL;
604 	put_module(B_ISA_MODULE_NAME);
605 	put_module(B_PCI_MODULE_NAME);
606 	put_module(B_TTY_MODULE_NAME);
607 	put_module(B_DPC_MODULE_NAME);
608 
609 	TRACE_FUNCRET("< uninit_driver() returns\n");
610 }
611 
612 
613 bool
pc_serial_service(struct tty * tty,uint32 op,void * buffer,size_t length)614 pc_serial_service(struct tty *tty, uint32 op, void *buffer, size_t length)
615 {
616 	TRACE_FUNCALLS("> pc_serial_service(%p, 0x%08lx, %p, %lu)\n", tty,
617 		op, buffer, length);
618 
619 
620 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
621 		if (gSerialDevices[i]
622 			&& gSerialDevices[i]->Service(tty, op, buffer, length)) {
623 			TRACE_FUNCRET("< pc_serial_service() returns: true\n");
624 			return true;
625 		}
626 	}
627 
628 	TRACE_FUNCRET("< pc_serial_service() returns: false\n");
629 	return false;
630 }
631 
632 
633 static void
pc_serial_dpc(void * arg)634 pc_serial_dpc(void *arg)
635 {
636 	SerialDevice *master = (SerialDevice *)arg;
637 	TRACE_FUNCALLS("> pc_serial_dpc(%p)\n", arg);
638 	master->InterruptHandler();
639 }
640 
641 
642 int32
pc_serial_interrupt(void * arg)643 pc_serial_interrupt(void *arg)
644 {
645 	SerialDevice *device = (SerialDevice *)arg;
646 	TRACE_FUNCALLS("> pc_serial_interrupt(%p)\n", arg);
647 
648 	if (!device)
649 		return B_UNHANDLED_INTERRUPT;
650 
651 	if (device->IsInterruptPending()) {
652 		status_t err;
653 		err = gDPCModule->queue_dpc(gDPCHandle, pc_serial_dpc, device);
654 		if (err != B_OK)
655 			dprintf(DRIVER_NAME ": error queing irq: %s\n", strerror(err));
656 		else {
657 			TRACE_FUNCRET("< pc_serial_interrupt() returns: resched\n");
658 			return B_INVOKE_SCHEDULER;
659 		}
660 	}
661 
662 	TRACE_FUNCRET("< pc_serial_interrupt() returns: unhandled\n");
663 	return B_UNHANDLED_INTERRUPT;
664 }
665 
666 
667 /* pc_serial_open - handle open() calls */
668 status_t
pc_serial_open(const char * name,uint32 flags,void ** cookie)669 pc_serial_open(const char *name, uint32 flags, void **cookie)
670 {
671 	TRACE_FUNCALLS("> pc_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
672 	acquire_sem(gDriverLock);
673 	status_t status = ENODEV;
674 
675 	*cookie = NULL;
676 	int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
677 	if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
678 		status = gSerialDevices[i]->Open(flags);
679 		*cookie = gSerialDevices[i];
680 	}
681 
682 	release_sem(gDriverLock);
683 	TRACE_FUNCRET("< pc_serial_open() returns: 0x%08x\n", status);
684 	return status;
685 }
686 
687 
688 /* pc_serial_read - handle read() calls */
689 status_t
pc_serial_read(void * cookie,off_t position,void * buffer,size_t * numBytes)690 pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
691 {
692 	TRACE_FUNCALLS("> pc_serial_read(0x%08x, %lld, 0x%08x, %d)\n", cookie,
693 		position, buffer, *numBytes);
694 	SerialDevice *device = (SerialDevice *)cookie;
695 	return device->Read((char *)buffer, numBytes);
696 }
697 
698 
699 /* pc_serial_write - handle write() calls */
700 status_t
pc_serial_write(void * cookie,off_t position,const void * buffer,size_t * numBytes)701 pc_serial_write(void *cookie, off_t position, const void *buffer,
702 	size_t *numBytes)
703 {
704 	TRACE_FUNCALLS("> pc_serial_write(0x%08x, %lld, 0x%08x, %d)\n", cookie,
705 		position, buffer, *numBytes);
706 	SerialDevice *device = (SerialDevice *)cookie;
707 	return device->Write((const char *)buffer, numBytes);
708 }
709 
710 
711 /* pc_serial_control - handle ioctl calls */
712 status_t
pc_serial_control(void * cookie,uint32 op,void * arg,size_t length)713 pc_serial_control(void *cookie, uint32 op, void *arg, size_t length)
714 {
715 	TRACE_FUNCALLS("> pc_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
716 		cookie, op, arg, length);
717 	SerialDevice *device = (SerialDevice *)cookie;
718 	return device->Control(op, arg, length);
719 }
720 
721 
722 /* pc_serial_select - handle select start */
723 status_t
pc_serial_select(void * cookie,uint8 event,uint32 ref,selectsync * sync)724 pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
725 {
726 	TRACE_FUNCALLS("> pc_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
727 		cookie, event, ref, sync);
728 	SerialDevice *device = (SerialDevice *)cookie;
729 	return device->Select(event, ref, sync);
730 }
731 
732 
733 /* pc_serial_deselect - handle select exit */
734 status_t
pc_serial_deselect(void * cookie,uint8 event,selectsync * sync)735 pc_serial_deselect(void *cookie, uint8 event, selectsync *sync)
736 {
737 	TRACE_FUNCALLS("> pc_serial_deselect(0x%08x, 0x%08x, %p)\n",
738 		cookie, event, sync);
739 	SerialDevice *device = (SerialDevice *)cookie;
740 	return device->DeSelect(event, sync);
741 }
742 
743 
744 /* pc_serial_close - handle close() calls */
745 status_t
pc_serial_close(void * cookie)746 pc_serial_close(void *cookie)
747 {
748 	TRACE_FUNCALLS("> pc_serial_close(0x%08x)\n", cookie);
749 	SerialDevice *device = (SerialDevice *)cookie;
750 	return device->Close();
751 }
752 
753 
754 /* pc_serial_free - called after last device is closed, and all i/o complete. */
755 status_t
pc_serial_free(void * cookie)756 pc_serial_free(void *cookie)
757 {
758 	TRACE_FUNCALLS("> pc_serial_free(0x%08x)\n", cookie);
759 	SerialDevice *device = (SerialDevice *)cookie;
760 	acquire_sem(gDriverLock);
761 	status_t status = device->Free();
762 	if (device->IsRemoved()) {
763 		for (int32 i = 0; i < DEVICES_COUNT; i++) {
764 			if (gSerialDevices[i] == device) {
765 				// the device is removed already but as it was open the
766 				// removed hook has not deleted the object
767 				delete device;
768 				gSerialDevices[i] = NULL;
769 				break;
770 			}
771 		}
772 	}
773 
774 	release_sem(gDriverLock);
775 	return status;
776 }
777 
778 
779 /* publish_devices - null-terminated array of devices supported by this driver. */
780 const char **
publish_devices()781 publish_devices()
782 {
783 	TRACE_FUNCALLS("> publish_devices()\n");
784 	for (int32 i = 0; gDeviceNames[i]; i++)
785 		free(gDeviceNames[i]);
786 
787 	int j = 0;
788 	acquire_sem(gDriverLock);
789 	for(int i = 0; i < DEVICES_COUNT; i++) {
790 		if (gSerialDevices[i]) {
791 			gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4);
792 			if (gDeviceNames[j]) {
793 				sprintf(gDeviceNames[j], "%s%d", sDeviceBaseName, i);
794 				j++;
795 			} else
796 				TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
797 		}
798 	}
799 
800 	gDeviceNames[j] = NULL;
801 	release_sem(gDriverLock);
802 	return (const char **)&gDeviceNames[0];
803 }
804 
805 
806 /* find_device - return poiter to device hooks structure for a given device */
807 device_hooks *
find_device(const char * name)808 find_device(const char *name)
809 {
810 	static device_hooks deviceHooks = {
811 		pc_serial_open,			/* -> open entry point */
812 		pc_serial_close,			/* -> close entry point */
813 		pc_serial_free,			/* -> free cookie */
814 		pc_serial_control,			/* -> control entry point */
815 		pc_serial_read,			/* -> read entry point */
816 		pc_serial_write,			/* -> write entry point */
817 		pc_serial_select,			/* -> select entry point */
818 		pc_serial_deselect			/* -> deselect entry point */
819 	};
820 
821 	TRACE_FUNCALLS("> find_device(%s)\n", name);
822 	return &deviceHooks;
823 }
824