xref: /haiku/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp (revision 4bd0c1066b227cec4b79883bdef697c7a27f2e90)
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 config_manager_for_driver_module_info *gConfigManagerModule = NULL;
30 isa_module_info *gISAModule = NULL;
31 pci_module_info *gPCIModule = NULL;
32 tty_module_info *gTTYModule = NULL;
33 dpc_module_info *gDPCModule = NULL;
34 void* gDPCHandle = NULL;
35 sem_id gDriverLock = -1;
36 bool gHandleISA = false;
37 uint32 gKernelDebugPort = 0x3f8;
38 
39 // 24 MHz clock
40 static const uint32 sDefaultRates[] = {
41 		0,		//B0
42 		2304,	//B50
43 		1536,	//B75
44 		1047,	//B110
45 		857,	//B134
46 		768,	//B150
47 		512,	//B200
48 		384,	//B300
49 		192,	//B600
50 		0,		//B1200
51 		0,		//B1800
52 		48,		//B2400
53 		24,		//B4800
54 		12,		//B9600
55 		6,		//B19200
56 		3,		//B38400
57 		2,		//B57600
58 		1,		//B115200
59 		0,		//B230400
60 		4,		//B31250
61 		0, //921600 !?
62 };
63 
64 // 8MHz clock on serial3 and 4 on the BeBox
65 #if 0
66 static const uint32 sBeBoxRates[] = {
67 		0,		//B0
68 		//... TODO
69 };
70 #endif
71 
72 // XXX: should really be generated from metadata (CSV ?)
73 
74 static const struct serial_support_descriptor sSupportedDevices[] = {
75 
76 #ifdef HANDLE_ISA_COM
77 	// ISA devices
78 	{ B_ISA_BUS, "Generic 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
79 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550 } },
80 #endif
81 	// PCI devices
82 
83 	// vendor/device matches first
84 
85 /*
86 	// vendor: OxfordSemi
87 #define VN "OxfordSemi"
88 	// http://www.softio.com/ox16pci954ds.pdf
89 	{ B_PCI_BUS, "OxfordSemi 16950 Serial Port", sDefaultRates, NULL, { 32, 32, 8 },
90 	  { PCI_simple_communications, PCI_serial, PCI_serial_16950,
91 		0x1415, 0x9501, PCI_INVAL, PCI_INVAL } },
92 
93 	// http://www.softio.com/ox16pci952ds.pdf
94 	{ B_PCI_BUS, "OxfordSemi 16950 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
95 	  { PCI_simple_communications, PCI_serial, PCI_serial_16950,
96 		0x1415, 0x9521, PCI_INVAL, PCI_INVAL } },
97 */
98 
99 
100 	// vendor: NetMos
101 #define VN "MosChip"
102 
103 	// used in Manhattan cards
104 	// 1 function / port
105 	// http://www.moschip.com/data/products/MCS9865/Data%20Sheet_9865.pdf
106 	{ B_PCI_BUS, VN" 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8, 0, 0, 0 },
107 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550,
108 		0x9710, 0x9865, PCI_INVAL, PCI_INVAL } },
109 
110 	// single function with all ports
111 	// only BAR 0 & 1 are UART
112 	// http://www.moschip.com/data/products/NM9835/Data%20Sheet_9835.pdf
113 	{ B_PCI_BUS, VN" 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8, (uint8)~0x3, 2, 0x000f },
114 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550,
115 		0x9710, 0x9835, PCI_INVAL, PCI_INVAL } },
116 
117 #undef VN
118 
119 
120 
121 	// generic fallback matches
122 	/*
123 	{ B_PCI_BUS, "Generic XT Serial Port", NULL },
124 	  { PCI_INVAL, PCI_INVAL, PCI_simple_communications,
125 		PCI_serial, PCI_serial_xt, PCI_INVAL, PCI_INVAL } },
126 
127 	{ B_PCI_BUS, "Generic 16450 Serial Port", NULL },
128 	  { PCI_INVAL, PCI_INVAL, PCI_simple_communications,
129 		PCI_serial, PCI_serial_16450, PCI_INVAL, PCI_INVAL } },
130 
131 	*/
132 	{ B_PCI_BUS, "Generic 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
133 	  { PCI_simple_communications, PCI_serial, PCI_serial_16550,
134 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
135 
136 	{ B_PCI_BUS, "Generic 16650 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
137 	  { PCI_simple_communications, PCI_serial, PCI_serial_16650,
138 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
139 
140 	{ B_PCI_BUS, "Generic 16750 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
141 	  { PCI_simple_communications, PCI_serial, PCI_serial_16750,
142 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
143 
144 	{ B_PCI_BUS, "Generic 16850 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
145 	  { PCI_simple_communications, PCI_serial, PCI_serial_16850,
146 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
147 
148 	{ B_PCI_BUS, "Generic 16950 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
149 	  { PCI_simple_communications, PCI_serial, PCI_serial_16950,
150 		PCI_INVAL, PCI_INVAL, PCI_INVAL, PCI_INVAL } },
151 
152 	// non PCI_serial devices
153 
154 	// beos zz driver supported that one
155 	{ B_PCI_BUS, "Lucent Modem", sDefaultRates, NULL, { 8, 8, 8 },
156 	  { PCI_simple_communications, PCI_simple_communications_other, 0x00,
157 		0x11C1, 0x0480, PCI_INVAL, PCI_INVAL } },
158 
159 	{ B_PCI_BUS, NULL, NULL, NULL, {0}, {0} }
160 };
161 
162 
163 // hardcoded ISA ports
164 static struct isa_ports {
165 	uint32 ioBase;
166 	uint32 irq;
167 } sHardcodedPorts[] = {
168 	{ 0x3f8, 4 },
169 	{ 0x2f8, 3 },
170 	{ 0x3e8, 4 },
171 	{ 0x2e8, 3 },
172 };
173 
174 #if 0
175 status_t
176 pc_serial_device_added(pc_device device, void **cookie)
177 {
178 	TRACE_FUNCALLS("> pc_serial_device_added(0x%08x, 0x%08x)\n", device, cookie);
179 
180 	status_t status = B_OK;
181 	const pc_device_descriptor *descriptor
182 		= gUSBModule->get_device_descriptor(device);
183 
184 	TRACE_ALWAYS("probing device: 0x%04x/0x%04x\n", descriptor->vendor_id,
185 		descriptor->product_id);
186 
187 	*cookie = NULL;
188 	SerialDevice *serialDevice = SerialDevice::MakeDevice(device,
189 		descriptor->vendor_id, descriptor->product_id);
190 
191 	const pc_configuration_info *configuration
192 		= gUSBModule->get_nth_configuration(device, 0);
193 
194 	if (!configuration)
195 		return B_ERROR;
196 
197 	status = serialDevice->AddDevice(configuration);
198 	if (status < B_OK) {
199 		delete serialDevice;
200 		return status;
201 	}
202 
203 	acquire_sem(gDriverLock);
204 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
205 		if (gSerialDevices[i] != NULL)
206 			continue;
207 
208 		status = serialDevice->Init();
209 		if (status < B_OK) {
210 			delete serialDevice;
211 			return status;
212 		}
213 
214 		gSerialDevices[i] = serialDevice;
215 		*cookie = serialDevice;
216 
217 		release_sem(gDriverLock);
218 		TRACE_ALWAYS("%s (0x%04x/0x%04x) added\n", serialDevice->Description(),
219 			descriptor->vendor_id, descriptor->product_id);
220 		return B_OK;
221 	}
222 
223 	release_sem(gDriverLock);
224 	return B_ERROR;
225 }
226 
227 
228 status_t
229 pc_serial_device_removed(void *cookie)
230 {
231 	TRACE_FUNCALLS("> pc_serial_device_removed(0x%08x)\n", cookie);
232 
233 	acquire_sem(gDriverLock);
234 
235 	SerialDevice *device = (SerialDevice *)cookie;
236 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
237 		if (gSerialDevices[i] == device) {
238 			if (device->IsOpen()) {
239 				// the device will be deleted upon being freed
240 				device->Removed();
241 			} else {
242 				delete device;
243 				gSerialDevices[i] = NULL;
244 			}
245 			break;
246 		}
247 	}
248 
249 	release_sem(gDriverLock);
250 	TRACE_FUNCRET("< pc_serial_device_removed() returns\n");
251 	return B_OK;
252 }
253 #endif
254 
255 //#pragma mark -
256 
257 status_t
258 pc_serial_insert_device(SerialDevice *device)
259 {
260 	status_t status = B_OK;
261 
262 	//XXX fix leaks!
263 	acquire_sem(gDriverLock);
264 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
265 		if (gSerialDevices[i] != NULL)
266 			continue;
267 
268 		status = device->Init();
269 		if (status < B_OK) {
270 			delete device;
271 			//return status;
272 			break;
273 		}
274 
275 		gSerialDevices[i] = device;
276 
277 		release_sem(gDriverLock);
278 		TRACE_ALWAYS("%s added\n", device->Description());
279 		return B_OK;
280 	}
281 
282 	release_sem(gDriverLock);
283 	return B_ERROR;
284 }
285 
286 
287 // probe devices with config_manager
288 static status_t
289 scan_bus(bus_type bus)
290 {
291 	const char *bus_name = "Unknown";
292 	uint64 cookie = 0;
293 	//status_t status;
294 	struct {
295 		device_info di;
296 		pci_info pi;
297 	} big_info;
298 	struct device_info &dinfo = big_info.di;
299 
300 	switch (bus) {
301 	case B_ISA_BUS:
302 		bus_name = "ISA";
303 		break;
304 	case B_PCI_BUS:
305 		bus_name = "PCI";
306 		break;
307 	case B_PCMCIA_BUS:
308 	default:
309 		return EINVAL;
310 	}
311 	TRACE_ALWAYS("scanning %s bus...\n", bus_name);
312 
313 //XXX: clean up this mess
314 
315 	while ((gConfigManagerModule->get_next_device_info(bus,
316 		&cookie, &big_info.di, sizeof(big_info)) == B_OK)) {
317 		// skip disabled devices
318 		if ((dinfo.flags & B_DEVICE_INFO_ENABLED) == 0)
319 			continue;
320 		// skip non configured devices
321 		if ((dinfo.flags & B_DEVICE_INFO_CONFIGURED) == 0)
322 			continue;
323 		// and devices in error
324 		if (dinfo.config_status < B_OK)
325 			continue;
326 
327 
328 		/*
329 		TRACE_ALWAYS("device: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",
330 		dinfo.id[0], dinfo.id[1], dinfo.id[2], dinfo.id[3]);
331 		*/
332 
333 		/*
334 		if (bus == B_PCI_BUS) {
335 			pci_info *pcii = (pci_info *)(((char *)&dinfo) +
336 				dinfo.bus_dependent_info_offset);
337 			TRACE_ALWAYS("pci: %04x:%04x\n",
338 				pcii->vendor_id, pcii->device_id);
339 			if ((pcii->header_type & PCI_header_type_mask) ==
340 				PCI_header_type_generic) {
341 				TRACE_ALWAYS("subsys: %04x:%04x\n",
342 					pcii->u.h0.subsystem_vendor_id, pcii->u.h0.subsystem_id);
343 			}
344 		}
345 		*/
346 
347 		const struct serial_support_descriptor *supported = NULL;
348 		for (int i = 0; sSupportedDevices[i].name; i++) {
349 			if (sSupportedDevices[i].bus != bus)
350 				continue;
351 			if (sSupportedDevices[i].match.class_base != PCI_undefined &&
352 				sSupportedDevices[i].match.class_base != dinfo.devtype.base)
353 				continue;
354 			if (sSupportedDevices[i].match.class_sub != PCI_undefined &&
355 				sSupportedDevices[i].match.class_sub != dinfo.devtype.subtype)
356 				continue;
357 			if (sSupportedDevices[i].match.class_api != PCI_undefined &&
358 				sSupportedDevices[i].match.class_api != dinfo.devtype.interface)
359 				continue;
360 
361 #if 0
362 			// either this way
363 			if (bus == B_PCI_BUS) {
364 				pci_info *pcii = (pci_info *)(((char *)&dinfo) +
365 					dinfo.bus_dependent_info_offset);
366 				if (sSupportedDevices[i].match.vendor_id != PCI_INVAL &&
367 					sSupportedDevices[i].match.vendor_id != pcii->vendor_id)
368 					continue;
369 				if (sSupportedDevices[i].match.device_id != PCI_INVAL &&
370 					sSupportedDevices[i].match.device_id != pcii->device_id)
371 					continue;
372 			}
373 #endif
374 			// or this one:
375 			// .id[0] = vendor_id and .id[1] = device_id
376 			// .id[3?] = subsys_vendor_id and .id[2?] = subsys_device_id
377 			if (bus == B_PCI_BUS &&
378 				sSupportedDevices[i].match.vendor_id != PCI_INVAL &&
379 				sSupportedDevices[i].match.vendor_id != dinfo.id[0])
380 				continue;
381 
382 			if (bus == B_PCI_BUS &&
383 				sSupportedDevices[i].match.device_id != PCI_INVAL &&
384 				sSupportedDevices[i].match.device_id != dinfo.id[1])
385 				continue;
386 
387 
388 			supported = &sSupportedDevices[i];
389 			break;
390 		}
391 		if (supported == NULL)
392 			continue;
393 
394 		struct {
395 			struct device_configuration c;
396 			resource_descriptor res[16];
397 		} config;
398 		if (gConfigManagerModule->get_size_of_current_configuration_for(
399 			cookie) > (int)sizeof(config)) {
400 			TRACE_ALWAYS("config size too big for device\n");
401 			continue;
402 		}
403 
404 		if (gConfigManagerModule->get_current_configuration_for(cookie,
405 			&config.c, sizeof(config)) < B_OK) {
406 			TRACE_ALWAYS("can't get config for device\n");
407 			continue;
408 
409 		}
410 
411 		TRACE_ALWAYS("device %Ld resources: %d irq %d dma %d io %d mem\n",
412 			cookie,
413 			gConfigManagerModule->count_resource_descriptors_of_type(
414 				&config.c, B_IRQ_RESOURCE),
415 			gConfigManagerModule->count_resource_descriptors_of_type(
416 				&config.c, B_DMA_RESOURCE),
417 			gConfigManagerModule->count_resource_descriptors_of_type(
418 				&config.c, B_IO_PORT_RESOURCE),
419 			gConfigManagerModule->count_resource_descriptors_of_type(
420 				&config.c, B_MEMORY_RESOURCE));
421 
422 
423 		// we first need the IRQ
424 		resource_descriptor irqdesc;
425 		if (gConfigManagerModule->get_nth_resource_descriptor_of_type(
426 			&config.c, 0, B_IRQ_RESOURCE, &irqdesc, sizeof(irqdesc)) < B_OK) {
427 			TRACE_ALWAYS("can't find IRQ for device\n");
428 			continue;
429 		}
430 		int irq;
431 		// XXX: what about APIC lines ?
432 		for (irq = 0; irq < 32; irq++) {
433 			if (irqdesc.d.m.mask & (1 << irq))
434 				break;
435 		}
436 		//TRACE_ALWAYS("irq %d\n", irq);
437 		//TRACE_ALWAYS("irq: %lx,%lx,%lx\n", irqdesc.d.m.mask, irqdesc.d.m.flags, irqdesc.d.m.cookie);
438 
439 		TRACE_ALWAYS("found %s device %Ld [%x|%x|%x] "
440 			/*"ID: '%16.16s'"*/" irq: %d flags: %08lx status: %s\n",
441 			bus_name, cookie, dinfo.devtype.base, dinfo.devtype.subtype,
442 			dinfo.devtype.interface, /*dinfo.id,*/ irq, dinfo.flags,
443 			strerror(dinfo.config_status));
444 
445 		// force enable I/O ports on PCI devices
446 #if 0
447 		if (bus == B_PCI_BUS) {
448 			pci_info *pcii = (pci_info *)(((char *)&dinfo) +
449 				dinfo.bus_dependent_info_offset);
450 
451 			uint32 cmd = gPCIModule->read_pci_config(pcii->bus, pcii->device,
452 				pcii->function, PCI_command, 2);
453 			TRACE_ALWAYS("PCI_command: 0x%04lx\n", cmd);
454 			cmd |= PCI_command_io;
455 			gPCIModule->write_pci_config(pcii->bus, pcii->device,
456 				pcii->function, PCI_command, 2, cmd);
457 		}
458 #endif
459 
460 		resource_descriptor iodesc;
461 		SerialDevice *master = NULL;
462 
463 		//TODO: handle maxports
464 		//TODO: handle subsystem_id_mask
465 
466 		// instanciate devices on IO ports
467 		for (int i = 0;
468 			gConfigManagerModule->get_nth_resource_descriptor_of_type(
469 			&config.c, i, B_IO_PORT_RESOURCE, &iodesc, sizeof(iodesc)) == B_OK;
470 			i++) {
471 			TRACE_ALWAYS("io at 0x%04lx len 0x%04lx\n", iodesc.d.r.minbase,
472 				iodesc.d.r.len);
473 
474 			if (iodesc.d.r.len < supported->constraints.minsize)
475 				continue;
476 			if (iodesc.d.r.len > supported->constraints.maxsize)
477 				continue;
478 			SerialDevice *device;
479 			uint32 ioport = iodesc.d.r.minbase;
480 next_split:
481 			// no more to split
482 			if ((ioport - iodesc.d.r.minbase) >= iodesc.d.r.len)
483 				continue;
484 
485 			TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
486 				supported->name);
487 
488 
489 			device = new(std::nothrow) SerialDevice(supported, ioport, irq, master);
490 			if (device == NULL) {
491 				TRACE_ALWAYS("can't allocate device\n");
492 				continue;
493 			}
494 
495 			if (pc_serial_insert_device(device) < B_OK) {
496 				TRACE_ALWAYS("can't insert device\n");
497 				continue;
498 			}
499 			if (master == NULL)
500 				master = device;
501 
502 			ioport += supported->constraints.split;
503 			goto next_split;
504 			// try next part of the I/O range now
505 		}
506 		// we have at least one device
507 		if (master) {
508 			// hook up the irq
509 #if 0
510 			status = install_io_interrupt_handler(irq, pc_serial_interrupt,
511 				master, 0);
512 			TRACE_ALWAYS("installing irq %d handler: %s\n", irq, strerror(status));
513 #endif
514 		}
515 	}
516 	return B_OK;
517 }
518 
519 
520 // until we support ISA device enumeration from PnP BIOS or ACPI,
521 // we have to probe the 4 default COM ports...
522 status_t
523 scan_isa_hardcoded()
524 {
525 #ifdef HANDLE_ISA_COM
526 	int i;
527 	bool serialDebug = get_safemode_boolean("serial_debug_output", true);
528 
529 	for (i = 0; i < 4; i++) {
530 		// skip the port used for kernel debugging...
531 		if (serialDebug && sHardcodedPorts[i].ioBase == gKernelDebugPort) {
532 			TRACE_ALWAYS("Skipping port %d as it is used for kernel debug.\n", i);
533 			continue;
534 		}
535 
536 		SerialDevice *device;
537 		device = new(std::nothrow) SerialDevice(&sSupportedDevices[0],
538 			sHardcodedPorts[i].ioBase, sHardcodedPorts[i].irq);
539 		if (device != NULL && device->Probe())
540 			pc_serial_insert_device(device);
541 		else
542 			delete device;
543 	}
544 #endif
545 	return B_OK;
546 }
547 
548 
549 // this version doesn't use config_manager, but can't probe the IRQ yet
550 status_t
551 scan_pci_alt()
552 {
553 	pci_info info;
554 	int ix;
555 	TRACE_ALWAYS("scanning PCI bus (alt)...\n");
556 
557 	// probe PCI devices
558 	for (ix = 0; (*gPCIModule->get_nth_pci_info)(ix, &info) == B_OK; ix++) {
559 		// sanity check
560 		if ((info.header_type & PCI_header_type_mask) != PCI_header_type_generic)
561 			continue;
562 		/*
563 		TRACE_ALWAYS("probing PCI device %2d [%x|%x|%x] %04x:%04x\n",
564 			ix, info.class_base, info.class_sub, info.class_api,
565 			info.vendor_id, info.device_id);
566 		*/
567 
568 		const struct serial_support_descriptor *supported = NULL;
569 		for (int i = 0; sSupportedDevices[i].name; i++) {
570 			if (sSupportedDevices[i].bus != B_PCI_BUS)
571 				continue;
572 			if (info.class_base != sSupportedDevices[i].match.class_base)
573 				continue;
574 			if (info.class_sub != sSupportedDevices[i].match.class_sub)
575 				continue;
576 			if (info.class_api != sSupportedDevices[i].match.class_api)
577 				continue;
578 			if (sSupportedDevices[i].match.vendor_id != PCI_INVAL
579 				&& info.vendor_id != sSupportedDevices[i].match.vendor_id)
580 				continue;
581 			if (sSupportedDevices[i].match.device_id != PCI_INVAL
582 				&& info.device_id != sSupportedDevices[i].match.device_id)
583 				continue;
584 			supported = &sSupportedDevices[i];
585 			break;
586 		}
587 		if (supported == NULL)
588 			continue;
589 
590 		TRACE_ALWAYS("found PCI device %2d [%x|%x|%x] %04x:%04x as %s\n",
591 			ix, info.class_base, info.class_sub, info.class_api,
592 			info.vendor_id, info.device_id, supported->name);
593 
594 		// XXX: interrupt_line doesn't seem to
595 		TRACE_ALWAYS("irq line %d, pin %d\n",
596 			info.u.h0.interrupt_line, info.u.h0.interrupt_pin);
597 		int irq = info.u.h0.interrupt_line;
598 
599 		SerialDevice *master = NULL;
600 
601 		uint8 portCount = 0;
602 		uint32 maxPorts = DEVICES_COUNT;
603 
604 		if (supported->constraints.maxports) {
605 			maxPorts = supported->constraints.maxports;
606 			TRACE_ALWAYS("card supports up to %d ports\n", maxPorts);
607 		}
608 		if (supported->constraints.subsystem_id_mask) {
609 			uint32 id = info.u.h0.subsystem_id;
610 			uint32 mask = supported->constraints.subsystem_id_mask;
611 			id &= mask;
612 			//TRACE_ALWAYS("mask: %lx, masked: %lx\n", mask, id);
613 			while (!(mask & 0x1)) {
614 				mask >>= 1;
615 				id >>= 1;
616 			}
617 			maxPorts = (uint8)id;
618 			TRACE_ALWAYS("subsystem id tells card has %d ports\n", maxPorts);
619 		}
620 
621 		// find I/O ports
622 		for (int r = 0; r < 6; r++) {
623 			uint32 regbase = info.u.h0.base_registers[r];
624 			uint32 reglen = info.u.h0.base_register_sizes[r];
625 
626 			/**/
627 			TRACE("ranges[%d] at 0x%08lx len 0x%lx flags 0x%02x\n", r,
628 				regbase, reglen, info.u.h0.base_register_flags[r]);
629 			/**/
630 
631 			// empty
632 			if (reglen == 0)
633 				continue;
634 
635 			// not I/O
636 			if ((info.u.h0.base_register_flags[r] & PCI_address_space) == 0)
637 				continue;
638 
639 			// the range for sure doesn't contain any UART
640 			if (supported->constraints.ignoremask & (1 << r)) {
641 				TRACE_ALWAYS("ignored regs at 0x%08lx len 0x%lx\n",
642 					regbase, reglen);
643 				continue;
644 			}
645 
646 			TRACE_ALWAYS("regs at 0x%08lx len 0x%lx\n",
647 				regbase, reglen);
648 			//&PCI_address_io_mask
649 
650 			if (reglen < supported->constraints.minsize)
651 				continue;
652 			if (reglen > supported->constraints.maxsize)
653 				continue;
654 
655 			SerialDevice *device;
656 			uint32 ioport = regbase;
657 next_split_alt:
658 			// no more to split
659 			if ((ioport - regbase) >= reglen)
660 				continue;
661 
662 			if (portCount >= maxPorts)
663 				break;
664 
665 			TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
666 				supported->name);
667 
668 
669 /**/
670 			device = new(std::nothrow) SerialDevice(supported, ioport, irq, master);
671 			if (device == NULL) {
672 				TRACE_ALWAYS("can't allocate device\n");
673 				continue;
674 			}
675 
676 			if (pc_serial_insert_device(device) < B_OK) {
677 				TRACE_ALWAYS("can't insert device\n");
678 				continue;
679 			}
680 /**/			if (master == NULL)
681 				master = device;
682 
683 			ioport += supported->constraints.split;
684 			portCount++;
685 			goto next_split_alt;
686 			// try next part of the I/O range now
687 
688 		}
689 	}
690 
691 	return B_OK;
692 }
693 
694 
695 static void
696 check_kernel_debug_port()
697 {
698 	void *handle;
699 	long int value;
700 
701 	handle = load_driver_settings("kernel");
702 	if (handle == NULL)
703 		return;
704 
705 	const char *str = get_driver_parameter(handle, "serial_debug_port",
706 		NULL, NULL);
707 	if (str != NULL) {
708 		value = strtol(str, NULL, 0);
709 		if (value >= 4) // XXX: actually should be MAX_SERIAL_PORTS...
710 			gKernelDebugPort = (uint32)value;
711 		else if (value >= 0) // XXX: we should use the kernel_arg's table...
712 			gKernelDebugPort = sHardcodedPorts[value].ioBase;
713 	}
714 
715 	/* TODO: actually handle this in the kernel debugger too!
716 	bool enabled = get_driver_boolean_parameter(handle, "serial_debug_output",
717 		false, true);
718 	if (!enabled)
719 		gKernelDebugPort = 0;
720 	*/
721 
722 	unload_driver_settings(handle);
723 }
724 
725 
726 //#pragma mark -
727 
728 
729 /* init_hardware - called once the first time the driver is loaded */
730 status_t
731 init_hardware()
732 {
733 	TRACE("init_hardware\n");
734 	return B_OK;
735 }
736 
737 
738 /* init_driver - called every time the driver is loaded. */
739 status_t
740 init_driver()
741 {
742 	status_t status;
743 	load_settings();
744 	create_log_file();
745 
746 	TRACE_FUNCALLS("> init_driver()\n");
747 
748 	status = get_module(B_DPC_MODULE_NAME, (module_info **)&gDPCModule);
749 	if (status < B_OK)
750 		goto err_dpc;
751 
752 	status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
753 	if (status < B_OK)
754 		goto err_tty;
755 
756 	status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCIModule);
757 	if (status < B_OK)
758 		goto err_pci;
759 
760 	status = get_module(B_ISA_MODULE_NAME, (module_info **)&gISAModule);
761 	if (status < B_OK)
762 		goto err_isa;
763 
764 	status = get_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME,
765 		(module_info **)&gConfigManagerModule);
766 	if (status < B_OK)
767 		goto err_cm;
768 
769 	status = gDPCModule->new_dpc_queue(&gDPCHandle, "pc_serial irq",
770 		B_REAL_TIME_PRIORITY);
771 	if (status != B_OK)
772 		goto err_dpcq;
773 
774 	for (int32 i = 0; i < DEVICES_COUNT; i++)
775 		gSerialDevices[i] = NULL;
776 
777 	gDeviceNames[0] = NULL;
778 
779 	gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
780 	if (gDriverLock < B_OK) {
781 		status = gDriverLock;
782 		goto err_sem;
783 	}
784 
785 	status = ENOENT;
786 
787 	check_kernel_debug_port();
788 
789 	(void)scan_bus;
790 	//scan_bus(B_ISA_BUS);
791 	//scan_bus(B_PCI_BUS);
792 	scan_isa_hardcoded();
793 	scan_pci_alt();
794 
795 	// XXX: ISA cards
796 	// XXX: pcmcia
797 
798 	TRACE_FUNCRET("< init_driver() returns\n");
799 	return B_OK;
800 
801 //err_none:
802 	delete_sem(gDriverLock);
803 err_sem:
804 	gDPCModule->delete_dpc_queue(gDPCHandle);
805 	gDPCHandle = NULL;
806 err_dpcq:
807 	put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
808 err_cm:
809 	put_module(B_ISA_MODULE_NAME);
810 err_isa:
811 	put_module(B_PCI_MODULE_NAME);
812 err_pci:
813 	put_module(B_TTY_MODULE_NAME);
814 err_tty:
815 	put_module(B_DPC_MODULE_NAME);
816 err_dpc:
817 	TRACE_FUNCRET("< init_driver() returns %s\n", strerror(status));
818 	return status;
819 }
820 
821 
822 /* uninit_driver - called every time the driver is unloaded */
823 void
824 uninit_driver()
825 {
826 	TRACE_FUNCALLS("> uninit_driver()\n");
827 
828 	//gUSBModule->uninstall_notify(DRIVER_NAME);
829 	acquire_sem(gDriverLock);
830 
831 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
832 		if (gSerialDevices[i]) {
833 			/*
834 			if (gSerialDevices[i]->Master() == gSerialDevices[i])
835 				remove_io_interrupt_handler(gSerialDevices[i]->IRQ(),
836 					pc_serial_interrupt, gSerialDevices[i]);
837 			*/
838 			delete gSerialDevices[i];
839 			gSerialDevices[i] = NULL;
840 		}
841 	}
842 
843 	for (int32 i = 0; gDeviceNames[i]; i++)
844 		free(gDeviceNames[i]);
845 
846 	delete_sem(gDriverLock);
847 	gDPCModule->delete_dpc_queue(gDPCHandle);
848 	gDPCHandle = NULL;
849 	put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
850 	put_module(B_ISA_MODULE_NAME);
851 	put_module(B_PCI_MODULE_NAME);
852 	put_module(B_TTY_MODULE_NAME);
853 	put_module(B_DPC_MODULE_NAME);
854 
855 	TRACE_FUNCRET("< uninit_driver() returns\n");
856 }
857 
858 
859 bool
860 pc_serial_service(struct tty *tty, uint32 op, void *buffer, size_t length)
861 {
862 	TRACE_FUNCALLS("> pc_serial_service(%p, 0x%08lx, %p, %lu)\n", tty,
863 		op, buffer, length);
864 
865 
866 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
867 		if (gSerialDevices[i]
868 			&& gSerialDevices[i]->Service(tty, op, buffer, length)) {
869 			TRACE_FUNCRET("< pc_serial_service() returns: true\n");
870 			return true;
871 		}
872 	}
873 
874 	TRACE_FUNCRET("< pc_serial_service() returns: false\n");
875 	return false;
876 }
877 
878 
879 static void
880 pc_serial_dpc(void *arg)
881 {
882 	SerialDevice *master = (SerialDevice *)arg;
883 	TRACE_FUNCALLS("> pc_serial_dpc(%p)\n", arg);
884 	master->InterruptHandler();
885 }
886 
887 
888 int32
889 pc_serial_interrupt(void *arg)
890 {
891 	SerialDevice *device = (SerialDevice *)arg;
892 	TRACE_FUNCALLS("> pc_serial_interrupt(%p)\n", arg);
893 
894 	if (!device)
895 		return B_UNHANDLED_INTERRUPT;
896 
897 	if (device->IsInterruptPending()) {
898 		status_t err;
899 		err = gDPCModule->queue_dpc(gDPCHandle, pc_serial_dpc, device);
900 		if (err != B_OK)
901 			dprintf(DRIVER_NAME ": error queing irq: %s\n", strerror(err));
902 		else {
903 			TRACE_FUNCRET("< pc_serial_interrupt() returns: resched\n");
904 			return B_INVOKE_SCHEDULER;
905 		}
906 	}
907 
908 	TRACE_FUNCRET("< pc_serial_interrupt() returns: unhandled\n");
909 	return B_UNHANDLED_INTERRUPT;
910 }
911 
912 
913 /* pc_serial_open - handle open() calls */
914 status_t
915 pc_serial_open(const char *name, uint32 flags, void **cookie)
916 {
917 	TRACE_FUNCALLS("> pc_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
918 	acquire_sem(gDriverLock);
919 	status_t status = ENODEV;
920 
921 	*cookie = NULL;
922 	int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
923 	if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
924 		status = gSerialDevices[i]->Open(flags);
925 		*cookie = gSerialDevices[i];
926 	}
927 
928 	release_sem(gDriverLock);
929 	TRACE_FUNCRET("< pc_serial_open() returns: 0x%08x\n", status);
930 	return status;
931 }
932 
933 
934 /* pc_serial_read - handle read() calls */
935 status_t
936 pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
937 {
938 	TRACE_FUNCALLS("> pc_serial_read(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
939 		position, buffer, *numBytes);
940 	SerialDevice *device = (SerialDevice *)cookie;
941 	return device->Read((char *)buffer, numBytes);
942 }
943 
944 
945 /* pc_serial_write - handle write() calls */
946 status_t
947 pc_serial_write(void *cookie, off_t position, const void *buffer,
948 	size_t *numBytes)
949 {
950 	TRACE_FUNCALLS("> pc_serial_write(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
951 		position, buffer, *numBytes);
952 	SerialDevice *device = (SerialDevice *)cookie;
953 	return device->Write((const char *)buffer, numBytes);
954 }
955 
956 
957 /* pc_serial_control - handle ioctl calls */
958 status_t
959 pc_serial_control(void *cookie, uint32 op, void *arg, size_t length)
960 {
961 	TRACE_FUNCALLS("> pc_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
962 		cookie, op, arg, length);
963 	SerialDevice *device = (SerialDevice *)cookie;
964 	return device->Control(op, arg, length);
965 }
966 
967 
968 /* pc_serial_select - handle select start */
969 status_t
970 pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
971 {
972 	TRACE_FUNCALLS("> pc_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
973 		cookie, event, ref, sync);
974 	SerialDevice *device = (SerialDevice *)cookie;
975 	return device->Select(event, ref, sync);
976 }
977 
978 
979 /* pc_serial_deselect - handle select exit */
980 status_t
981 pc_serial_deselect(void *cookie, uint8 event, selectsync *sync)
982 {
983 	TRACE_FUNCALLS("> pc_serial_deselect(0x%08x, 0x%08x, %p)\n",
984 		cookie, event, sync);
985 	SerialDevice *device = (SerialDevice *)cookie;
986 	return device->DeSelect(event, sync);
987 }
988 
989 
990 /* pc_serial_close - handle close() calls */
991 status_t
992 pc_serial_close(void *cookie)
993 {
994 	TRACE_FUNCALLS("> pc_serial_close(0x%08x)\n", cookie);
995 	SerialDevice *device = (SerialDevice *)cookie;
996 	return device->Close();
997 }
998 
999 
1000 /* pc_serial_free - called after last device is closed, and all i/o complete. */
1001 status_t
1002 pc_serial_free(void *cookie)
1003 {
1004 	TRACE_FUNCALLS("> pc_serial_free(0x%08x)\n", cookie);
1005 	SerialDevice *device = (SerialDevice *)cookie;
1006 	acquire_sem(gDriverLock);
1007 	status_t status = device->Free();
1008 	if (device->IsRemoved()) {
1009 		for (int32 i = 0; i < DEVICES_COUNT; i++) {
1010 			if (gSerialDevices[i] == device) {
1011 				// the device is removed already but as it was open the
1012 				// removed hook has not deleted the object
1013 				delete device;
1014 				gSerialDevices[i] = NULL;
1015 				break;
1016 			}
1017 		}
1018 	}
1019 
1020 	release_sem(gDriverLock);
1021 	return status;
1022 }
1023 
1024 
1025 /* publish_devices - null-terminated array of devices supported by this driver. */
1026 const char **
1027 publish_devices()
1028 {
1029 	TRACE_FUNCALLS("> publish_devices()\n");
1030 	for (int32 i = 0; gDeviceNames[i]; i++)
1031 		free(gDeviceNames[i]);
1032 
1033 	int j = 0;
1034 	acquire_sem(gDriverLock);
1035 	for(int i = 0; i < DEVICES_COUNT; i++) {
1036 		if (gSerialDevices[i]) {
1037 			gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4);
1038 			if (gDeviceNames[j]) {
1039 				sprintf(gDeviceNames[j], "%s%d", sDeviceBaseName, i);
1040 				j++;
1041 			} else
1042 				TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
1043 		}
1044 	}
1045 
1046 	gDeviceNames[j] = NULL;
1047 	release_sem(gDriverLock);
1048 	return (const char **)&gDeviceNames[0];
1049 }
1050 
1051 
1052 /* find_device - return poiter to device hooks structure for a given device */
1053 device_hooks *
1054 find_device(const char *name)
1055 {
1056 	static device_hooks deviceHooks = {
1057 		pc_serial_open,			/* -> open entry point */
1058 		pc_serial_close,			/* -> close entry point */
1059 		pc_serial_free,			/* -> free cookie */
1060 		pc_serial_control,			/* -> control entry point */
1061 		pc_serial_read,			/* -> read entry point */
1062 		pc_serial_write,			/* -> write entry point */
1063 		pc_serial_select,			/* -> select entry point */
1064 		pc_serial_deselect			/* -> deselect entry point */
1065 	};
1066 
1067 	TRACE_FUNCALLS("> find_device(%s)\n", name);
1068 	return &deviceHooks;
1069 }
1070