xref: /haiku/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp (revision 778611c7e6a61b8ba072212756ce53eda826360a)
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 <malloc.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 
21 #include "Driver.h"
22 #include "SerialDevice.h"
23 
24 int32 api_version = B_CUR_DRIVER_API_VERSION;
25 static const char *sDeviceBaseName = DEVFS_BASE;
26 SerialDevice *gSerialDevices[DEVICES_COUNT];
27 char *gDeviceNames[DEVICES_COUNT + 1];
28 config_manager_for_driver_module_info *gConfigManagerModule = NULL;
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 		0,		//B1200
50 		0,		//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
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 // probe devices with config_manager
287 static status_t
288 scan_bus(bus_type bus)
289 {
290 	const char *bus_name = "Unknown";
291 	uint64 cookie = 0;
292 	//status_t status;
293 	struct {
294 		device_info di;
295 		pci_info pi;
296 	} big_info;
297 	struct device_info &dinfo = big_info.di;
298 
299 	switch (bus) {
300 	case B_ISA_BUS:
301 		bus_name = "ISA";
302 		break;
303 	case B_PCI_BUS:
304 		bus_name = "PCI";
305 		break;
306 	case B_PCMCIA_BUS:
307 	default:
308 		return EINVAL;
309 	}
310 	TRACE_ALWAYS("scanning %s bus...\n", bus_name);
311 
312 //XXX: clean up this mess
313 
314 	while ((gConfigManagerModule->get_next_device_info(bus,
315 		&cookie, &big_info.di, sizeof(big_info)) == B_OK)) {
316 		// skip disabled devices
317 		if ((dinfo.flags & B_DEVICE_INFO_ENABLED) == 0)
318 			continue;
319 		// skip non configured devices
320 		if ((dinfo.flags & B_DEVICE_INFO_CONFIGURED) == 0)
321 			continue;
322 		// and devices in error
323 		if (dinfo.config_status < B_OK)
324 			continue;
325 
326 
327 		/*
328 		TRACE_ALWAYS("device: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",
329 		dinfo.id[0], dinfo.id[1], dinfo.id[2], dinfo.id[3]);
330 		*/
331 
332 		/*
333 		if (bus == B_PCI_BUS) {
334 			pci_info *pcii = (pci_info *)(((char *)&dinfo) +
335 				dinfo.bus_dependent_info_offset);
336 			TRACE_ALWAYS("pci: %04x:%04x\n",
337 				pcii->vendor_id, pcii->device_id);
338 			if ((pcii->header_type & PCI_header_type_mask) ==
339 				PCI_header_type_generic) {
340 				TRACE_ALWAYS("subsys: %04x:%04x\n",
341 					pcii->u.h0.subsystem_vendor_id, pcii->u.h0.subsystem_id);
342 			}
343 		}
344 		*/
345 
346 		const struct serial_support_descriptor *supported = NULL;
347 		for (int i = 0; sSupportedDevices[i].name; i++) {
348 			if (sSupportedDevices[i].bus != bus)
349 				continue;
350 			if (sSupportedDevices[i].match.class_base != PCI_undefined &&
351 				sSupportedDevices[i].match.class_base != dinfo.devtype.base)
352 				continue;
353 			if (sSupportedDevices[i].match.class_sub != PCI_undefined &&
354 				sSupportedDevices[i].match.class_sub != dinfo.devtype.subtype)
355 				continue;
356 			if (sSupportedDevices[i].match.class_api != PCI_undefined &&
357 				sSupportedDevices[i].match.class_api != dinfo.devtype.interface)
358 				continue;
359 
360 #if 0
361 			// either this way
362 			if (bus == B_PCI_BUS) {
363 				pci_info *pcii = (pci_info *)(((char *)&dinfo) +
364 					dinfo.bus_dependent_info_offset);
365 				if (sSupportedDevices[i].match.vendor_id != PCI_INVAL &&
366 					sSupportedDevices[i].match.vendor_id != pcii->vendor_id)
367 					continue;
368 				if (sSupportedDevices[i].match.device_id != PCI_INVAL &&
369 					sSupportedDevices[i].match.device_id != pcii->device_id)
370 					continue;
371 			}
372 #endif
373 			// or this one:
374 			// .id[0] = vendor_id and .id[1] = device_id
375 			// .id[3?] = subsys_vendor_id and .id[2?] = subsys_device_id
376 			if (bus == B_PCI_BUS &&
377 				sSupportedDevices[i].match.vendor_id != PCI_INVAL &&
378 				sSupportedDevices[i].match.vendor_id != dinfo.id[0])
379 				continue;
380 
381 			if (bus == B_PCI_BUS &&
382 				sSupportedDevices[i].match.device_id != PCI_INVAL &&
383 				sSupportedDevices[i].match.device_id != dinfo.id[1])
384 				continue;
385 
386 
387 			supported = &sSupportedDevices[i];
388 			break;
389 		}
390 		if (supported == NULL)
391 			continue;
392 
393 		struct {
394 			struct device_configuration c;
395 			resource_descriptor res[16];
396 		} config;
397 		if (gConfigManagerModule->get_size_of_current_configuration_for(
398 			cookie) > (int)sizeof(config)) {
399 			TRACE_ALWAYS("config size too big for device\n");
400 			continue;
401 		}
402 
403 		if (gConfigManagerModule->get_current_configuration_for(cookie,
404 			&config.c, sizeof(config)) < B_OK) {
405 			TRACE_ALWAYS("can't get config for device\n");
406 			continue;
407 
408 		}
409 
410 		TRACE_ALWAYS("device %Ld resources: %d irq %d dma %d io %d mem\n",
411 			cookie,
412 			gConfigManagerModule->count_resource_descriptors_of_type(
413 				&config.c, B_IRQ_RESOURCE),
414 			gConfigManagerModule->count_resource_descriptors_of_type(
415 				&config.c, B_DMA_RESOURCE),
416 			gConfigManagerModule->count_resource_descriptors_of_type(
417 				&config.c, B_IO_PORT_RESOURCE),
418 			gConfigManagerModule->count_resource_descriptors_of_type(
419 				&config.c, B_MEMORY_RESOURCE));
420 
421 
422 		// we first need the IRQ
423 		resource_descriptor irqdesc;
424 		if (gConfigManagerModule->get_nth_resource_descriptor_of_type(
425 			&config.c, 0, B_IRQ_RESOURCE, &irqdesc, sizeof(irqdesc)) < B_OK) {
426 			TRACE_ALWAYS("can't find IRQ for device\n");
427 			continue;
428 		}
429 		int irq;
430 		// XXX: what about APIC lines ?
431 		for (irq = 0; irq < 32; irq++) {
432 			if (irqdesc.d.m.mask & (1 << irq))
433 				break;
434 		}
435 		//TRACE_ALWAYS("irq %d\n", irq);
436 		//TRACE_ALWAYS("irq: %lx,%lx,%lx\n", irqdesc.d.m.mask, irqdesc.d.m.flags, irqdesc.d.m.cookie);
437 
438 		TRACE_ALWAYS("found %s device %Ld [%x|%x|%x] "
439 			/*"ID: '%16.16s'"*/" irq: %d flags: %08lx status: %s\n",
440 			bus_name, cookie, dinfo.devtype.base, dinfo.devtype.subtype,
441 			dinfo.devtype.interface, /*dinfo.id,*/ irq, dinfo.flags,
442 			strerror(dinfo.config_status));
443 
444 		// force enable I/O ports on PCI devices
445 #if 0
446 		if (bus == B_PCI_BUS) {
447 			pci_info *pcii = (pci_info *)(((char *)&dinfo) +
448 				dinfo.bus_dependent_info_offset);
449 
450 			uint32 cmd = gPCIModule->read_pci_config(pcii->bus, pcii->device,
451 				pcii->function, PCI_command, 2);
452 			TRACE_ALWAYS("PCI_command: 0x%04lx\n", cmd);
453 			cmd |= PCI_command_io;
454 			gPCIModule->write_pci_config(pcii->bus, pcii->device,
455 				pcii->function, PCI_command, 2, cmd);
456 		}
457 #endif
458 
459 		resource_descriptor iodesc;
460 		SerialDevice *master = NULL;
461 
462 		//TODO: handle maxports
463 		//TODO: handle subsystem_id_mask
464 
465 		// instanciate devices on IO ports
466 		for (int i = 0;
467 			gConfigManagerModule->get_nth_resource_descriptor_of_type(
468 			&config.c, i, B_IO_PORT_RESOURCE, &iodesc, sizeof(iodesc)) == B_OK;
469 			i++) {
470 			TRACE_ALWAYS("io at 0x%04lx len 0x%04lx\n", iodesc.d.r.minbase,
471 				iodesc.d.r.len);
472 
473 			if (iodesc.d.r.len < supported->constraints.minsize)
474 				continue;
475 			if (iodesc.d.r.len > supported->constraints.maxsize)
476 				continue;
477 			SerialDevice *device;
478 			uint32 ioport = iodesc.d.r.minbase;
479 next_split:
480 			// no more to split
481 			if ((ioport - iodesc.d.r.minbase) >= iodesc.d.r.len)
482 				continue;
483 
484 			TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
485 				supported->name);
486 
487 
488 			device = new(std::nothrow) SerialDevice(supported, ioport, irq, master);
489 			if (device == NULL) {
490 				TRACE_ALWAYS("can't allocate device\n");
491 				continue;
492 			}
493 
494 			if (pc_serial_insert_device(device) < B_OK) {
495 				TRACE_ALWAYS("can't insert device\n");
496 				continue;
497 			}
498 			if (master == NULL)
499 				master = device;
500 
501 			ioport += supported->constraints.split;
502 			goto next_split;
503 			// try next part of the I/O range now
504 		}
505 		// we have at least one device
506 		if (master) {
507 			// hook up the irq
508 #if 0
509 			status = install_io_interrupt_handler(irq, pc_serial_interrupt,
510 				master, 0);
511 			TRACE_ALWAYS("installing irq %d handler: %s\n", irq, strerror(status));
512 #endif
513 		}
514 	}
515 	return B_OK;
516 }
517 
518 
519 // until we support ISA device enumeration from PnP BIOS or ACPI,
520 // we have to probe the 4 default COM ports...
521 status_t
522 scan_isa_hardcoded()
523 {
524 #ifdef HANDLE_ISA_COM
525 	int i;
526 
527 	for (i = 0; i < 4; i++) {
528 		// skip the port used for kernel debugging...
529 		if (sHardcodedPorts[i].ioBase == gKernelDebugPort)
530 			continue;
531 
532 		SerialDevice *device;
533 		device = new(std::nothrow) SerialDevice(&sSupportedDevices[0],
534 			sHardcodedPorts[i].ioBase, sHardcodedPorts[i].irq);
535 		if (device != NULL && device->Probe())
536 			pc_serial_insert_device(device);
537 		else
538 			delete device;
539 	}
540 #endif
541 	return B_OK;
542 }
543 
544 
545 // this version doesn't use config_manager, but can't probe the IRQ yet
546 status_t
547 scan_pci_alt()
548 {
549 	pci_info info;
550 	int ix;
551 	TRACE_ALWAYS("scanning PCI bus (alt)...\n");
552 
553 	// probe PCI devices
554 	for (ix = 0; (*gPCIModule->get_nth_pci_info)(ix, &info) == B_OK; ix++) {
555 		// sanity check
556 		if ((info.header_type & PCI_header_type_mask) != PCI_header_type_generic)
557 			continue;
558 		/*
559 		TRACE_ALWAYS("probing PCI device %2d [%x|%x|%x] %04x:%04x\n",
560 			ix, info.class_base, info.class_sub, info.class_api,
561 			info.vendor_id, info.device_id);
562 		*/
563 
564 		const struct serial_support_descriptor *supported = NULL;
565 		for (int i = 0; sSupportedDevices[i].name; i++) {
566 			if (sSupportedDevices[i].bus != B_PCI_BUS)
567 				continue;
568 			if (info.class_base != sSupportedDevices[i].match.class_base)
569 				continue;
570 			if (info.class_sub != sSupportedDevices[i].match.class_sub)
571 				continue;
572 			if (info.class_api != sSupportedDevices[i].match.class_api)
573 				continue;
574 			if (sSupportedDevices[i].match.vendor_id != PCI_INVAL
575 				&& info.vendor_id != sSupportedDevices[i].match.vendor_id)
576 				continue;
577 			if (sSupportedDevices[i].match.device_id != PCI_INVAL
578 				&& info.device_id != sSupportedDevices[i].match.device_id)
579 				continue;
580 			supported = &sSupportedDevices[i];
581 			break;
582 		}
583 		if (supported == NULL)
584 			continue;
585 
586 		TRACE_ALWAYS("found PCI device %2d [%x|%x|%x] %04x:%04x as %s\n",
587 			ix, info.class_base, info.class_sub, info.class_api,
588 			info.vendor_id, info.device_id, supported->name);
589 
590 		// XXX: interrupt_line doesn't seem to
591 		TRACE_ALWAYS("irq line %d, pin %d\n",
592 			info.u.h0.interrupt_line, info.u.h0.interrupt_pin);
593 		int irq = info.u.h0.interrupt_line;
594 
595 		SerialDevice *master = NULL;
596 
597 		uint8 portCount = 0;
598 		uint32 maxPorts = DEVICES_COUNT;
599 
600 		if (supported->constraints.maxports) {
601 			maxPorts = supported->constraints.maxports;
602 			TRACE_ALWAYS("card supports up to %d ports\n", maxPorts);
603 		}
604 		if (supported->constraints.subsystem_id_mask) {
605 			uint32 id = info.u.h0.subsystem_id;
606 			uint32 mask = supported->constraints.subsystem_id_mask;
607 			id &= mask;
608 			//TRACE_ALWAYS("mask: %lx, masked: %lx\n", mask, id);
609 			while (!(mask & 0x1)) {
610 				mask >>= 1;
611 				id >>= 1;
612 			}
613 			maxPorts = (uint8)id;
614 			TRACE_ALWAYS("subsystem id tells card has %d ports\n", maxPorts);
615 		}
616 
617 		// find I/O ports
618 		for (int r = 0; r < 6; r++) {
619 			uint32 regbase = info.u.h0.base_registers[r];
620 			uint32 reglen = info.u.h0.base_register_sizes[r];
621 
622 			/**/
623 			TRACE("ranges[%d] at 0x%08lx len 0x%lx flags 0x%02x\n", r,
624 				regbase, reglen, info.u.h0.base_register_flags[r]);
625 			/**/
626 
627 			// empty
628 			if (reglen == 0)
629 				continue;
630 
631 			// not I/O
632 			if ((info.u.h0.base_register_flags[r] & PCI_address_space) == 0)
633 				continue;
634 
635 			// the range for sure doesn't contain any UART
636 			if (supported->constraints.ignoremask & (1 << r)) {
637 				TRACE_ALWAYS("ignored regs at 0x%08lx len 0x%lx\n",
638 					regbase, reglen);
639 				continue;
640 			}
641 
642 			TRACE_ALWAYS("regs at 0x%08lx len 0x%lx\n",
643 				regbase, reglen);
644 			//&PCI_address_io_mask
645 
646 			if (reglen < supported->constraints.minsize)
647 				continue;
648 			if (reglen > supported->constraints.maxsize)
649 				continue;
650 
651 			SerialDevice *device;
652 			uint32 ioport = regbase;
653 next_split_alt:
654 			// no more to split
655 			if ((ioport - regbase) >= reglen)
656 				continue;
657 
658 			if (portCount >= maxPorts)
659 				break;
660 
661 			TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
662 				supported->name);
663 
664 
665 /**/
666 			device = new(std::nothrow) SerialDevice(supported, ioport, irq, master);
667 			if (device == NULL) {
668 				TRACE_ALWAYS("can't allocate device\n");
669 				continue;
670 			}
671 
672 			if (pc_serial_insert_device(device) < B_OK) {
673 				TRACE_ALWAYS("can't insert device\n");
674 				continue;
675 			}
676 /**/			if (master == NULL)
677 				master = device;
678 
679 			ioport += supported->constraints.split;
680 			portCount++;
681 			goto next_split_alt;
682 			// try next part of the I/O range now
683 
684 		}
685 	}
686 
687 	return B_OK;
688 }
689 
690 
691 static void
692 check_kernel_debug_port()
693 {
694 	void *handle;
695 	long int value;
696 
697 	handle = load_driver_settings("kernel");
698 	if (handle == NULL)
699 		return;
700 
701 	const char *str = get_driver_parameter(handle, "serial_debug_port",
702 		NULL, NULL);
703 	if (str != NULL) {
704 		value = strtol(str, NULL, 0);
705 		if (value >= 4) // XXX: actually should be MAX_SERIAL_PORTS...
706 			gKernelDebugPort = (uint32)value;
707 		else if (value >= 0) // XXX: we should use the kernel_arg's table...
708 			gKernelDebugPort = sHardcodedPorts[value].ioBase;
709 	}
710 
711 	/* TODO: actually handle this in the kernel debugger too!
712 	bool enabled = get_driver_boolean_parameter(handle, "serial_debug_output",
713 		false, true);
714 	if (!enabled)
715 		gKernelDebugPort = 0;
716 	*/
717 
718 	unload_driver_settings(handle);
719 }
720 
721 
722 //#pragma mark -
723 
724 
725 /* init_hardware - called once the first time the driver is loaded */
726 status_t
727 init_hardware()
728 {
729 	TRACE("init_hardware\n");
730 	return B_OK;
731 }
732 
733 
734 /* init_driver - called every time the driver is loaded. */
735 status_t
736 init_driver()
737 {
738 	status_t status;
739 	load_settings();
740 	create_log_file();
741 
742 	TRACE_FUNCALLS("> init_driver()\n");
743 
744 	status = get_module(B_DPC_MODULE_NAME, (module_info **)&gDPCModule);
745 	if (status < B_OK)
746 		goto err_dpc;
747 
748 	status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
749 	if (status < B_OK)
750 		goto err_tty;
751 
752 	status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCIModule);
753 	if (status < B_OK)
754 		goto err_pci;
755 
756 	status = get_module(B_ISA_MODULE_NAME, (module_info **)&gISAModule);
757 	if (status < B_OK)
758 		goto err_isa;
759 
760 	status = get_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME,
761 		(module_info **)&gConfigManagerModule);
762 	if (status < B_OK)
763 		goto err_cm;
764 
765 	status = gDPCModule->new_dpc_queue(&gDPCHandle, "pc_serial irq",
766 		B_REAL_TIME_PRIORITY);
767 	if (status != B_OK)
768 		goto err_dpcq;
769 
770 	for (int32 i = 0; i < DEVICES_COUNT; i++)
771 		gSerialDevices[i] = NULL;
772 
773 	gDeviceNames[0] = NULL;
774 
775 	gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
776 	if (gDriverLock < B_OK) {
777 		status = gDriverLock;
778 		goto err_sem;
779 	}
780 
781 	status = ENOENT;
782 
783 	check_kernel_debug_port();
784 
785 	(void)scan_bus;
786 	//scan_bus(B_ISA_BUS);
787 	//scan_bus(B_PCI_BUS);
788 	scan_isa_hardcoded();
789 	scan_pci_alt();
790 
791 	// XXX: ISA cards
792 	// XXX: pcmcia
793 
794 	TRACE_FUNCRET("< init_driver() returns\n");
795 	return B_OK;
796 
797 //err_none:
798 	delete_sem(gDriverLock);
799 err_sem:
800 	gDPCModule->delete_dpc_queue(gDPCHandle);
801 	gDPCHandle = NULL;
802 err_dpcq:
803 	put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
804 err_cm:
805 	put_module(B_ISA_MODULE_NAME);
806 err_isa:
807 	put_module(B_PCI_MODULE_NAME);
808 err_pci:
809 	put_module(B_TTY_MODULE_NAME);
810 err_tty:
811 	put_module(B_DPC_MODULE_NAME);
812 err_dpc:
813 	TRACE_FUNCRET("< init_driver() returns %s\n", strerror(status));
814 	return status;
815 }
816 
817 
818 /* uninit_driver - called every time the driver is unloaded */
819 void
820 uninit_driver()
821 {
822 	TRACE_FUNCALLS("> uninit_driver()\n");
823 
824 	//gUSBModule->uninstall_notify(DRIVER_NAME);
825 	acquire_sem(gDriverLock);
826 
827 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
828 		if (gSerialDevices[i]) {
829 			/*
830 			if (gSerialDevices[i]->Master() == gSerialDevices[i])
831 				remove_io_interrupt_handler(gSerialDevices[i]->IRQ(),
832 					pc_serial_interrupt, gSerialDevices[i]);
833 			*/
834 			delete gSerialDevices[i];
835 			gSerialDevices[i] = NULL;
836 		}
837 	}
838 
839 	for (int32 i = 0; gDeviceNames[i]; i++)
840 		free(gDeviceNames[i]);
841 
842 	delete_sem(gDriverLock);
843 	gDPCModule->delete_dpc_queue(gDPCHandle);
844 	gDPCHandle = NULL;
845 	put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
846 	put_module(B_ISA_MODULE_NAME);
847 	put_module(B_PCI_MODULE_NAME);
848 	put_module(B_TTY_MODULE_NAME);
849 	put_module(B_DPC_MODULE_NAME);
850 
851 	TRACE_FUNCRET("< uninit_driver() returns\n");
852 }
853 
854 
855 bool
856 pc_serial_service(struct tty *tty, uint32 op, void *buffer, size_t length)
857 {
858 	TRACE_FUNCALLS("> pc_serial_service(%p, 0x%08lx, %p, %lu)\n", tty,
859 		op, buffer, length);
860 
861 
862 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
863 		if (gSerialDevices[i]
864 			&& gSerialDevices[i]->Service(tty, op, buffer, length)) {
865 			TRACE_FUNCRET("< pc_serial_service() returns: true\n");
866 			return true;
867 		}
868 	}
869 
870 	TRACE_FUNCRET("< pc_serial_service() returns: false\n");
871 	return false;
872 }
873 
874 
875 static void
876 pc_serial_dpc(void *arg)
877 {
878 	SerialDevice *master = (SerialDevice *)arg;
879 	TRACE_FUNCALLS("> pc_serial_dpc(%p)\n", arg);
880 	master->InterruptHandler();
881 }
882 
883 
884 int32
885 pc_serial_interrupt(void *arg)
886 {
887 	SerialDevice *device = (SerialDevice *)arg;
888 	TRACE_FUNCALLS("> pc_serial_interrupt(%p)\n", arg);
889 
890 	if (!device)
891 		return B_UNHANDLED_INTERRUPT;
892 
893 	if (device->IsInterruptPending()) {
894 		status_t err;
895 		err = gDPCModule->queue_dpc(gDPCHandle, pc_serial_dpc, device);
896 		if (err != B_OK)
897 			dprintf(DRIVER_NAME ": error queing irq: %s\n", strerror(err));
898 		else {
899 			TRACE_FUNCRET("< pc_serial_interrupt() returns: resched\n");
900 			return B_INVOKE_SCHEDULER;
901 		}
902 	}
903 
904 	TRACE_FUNCRET("< pc_serial_interrupt() returns: unhandled\n");
905 	return B_UNHANDLED_INTERRUPT;
906 }
907 
908 
909 /* pc_serial_open - handle open() calls */
910 status_t
911 pc_serial_open(const char *name, uint32 flags, void **cookie)
912 {
913 	TRACE_FUNCALLS("> pc_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
914 	acquire_sem(gDriverLock);
915 	status_t status = ENODEV;
916 
917 	*cookie = NULL;
918 	int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
919 	if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
920 		status = gSerialDevices[i]->Open(flags);
921 		*cookie = gSerialDevices[i];
922 	}
923 
924 	release_sem(gDriverLock);
925 	TRACE_FUNCRET("< pc_serial_open() returns: 0x%08x\n", status);
926 	return status;
927 }
928 
929 
930 /* pc_serial_read - handle read() calls */
931 status_t
932 pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
933 {
934 	TRACE_FUNCALLS("> pc_serial_read(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
935 		position, buffer, *numBytes);
936 	SerialDevice *device = (SerialDevice *)cookie;
937 	return device->Read((char *)buffer, numBytes);
938 }
939 
940 
941 /* pc_serial_write - handle write() calls */
942 status_t
943 pc_serial_write(void *cookie, off_t position, const void *buffer,
944 	size_t *numBytes)
945 {
946 	TRACE_FUNCALLS("> pc_serial_write(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
947 		position, buffer, *numBytes);
948 	SerialDevice *device = (SerialDevice *)cookie;
949 	return device->Write((const char *)buffer, numBytes);
950 }
951 
952 
953 /* pc_serial_control - handle ioctl calls */
954 status_t
955 pc_serial_control(void *cookie, uint32 op, void *arg, size_t length)
956 {
957 	TRACE_FUNCALLS("> pc_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
958 		cookie, op, arg, length);
959 	SerialDevice *device = (SerialDevice *)cookie;
960 	return device->Control(op, arg, length);
961 }
962 
963 
964 /* pc_serial_select - handle select start */
965 status_t
966 pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
967 {
968 	TRACE_FUNCALLS("> pc_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
969 		cookie, event, ref, sync);
970 	SerialDevice *device = (SerialDevice *)cookie;
971 	return device->Select(event, ref, sync);
972 }
973 
974 
975 /* pc_serial_deselect - handle select exit */
976 status_t
977 pc_serial_deselect(void *cookie, uint8 event, selectsync *sync)
978 {
979 	TRACE_FUNCALLS("> pc_serial_deselect(0x%08x, 0x%08x, %p)\n",
980 		cookie, event, sync);
981 	SerialDevice *device = (SerialDevice *)cookie;
982 	return device->DeSelect(event, sync);
983 }
984 
985 
986 /* pc_serial_close - handle close() calls */
987 status_t
988 pc_serial_close(void *cookie)
989 {
990 	TRACE_FUNCALLS("> pc_serial_close(0x%08x)\n", cookie);
991 	SerialDevice *device = (SerialDevice *)cookie;
992 	return device->Close();
993 }
994 
995 
996 /* pc_serial_free - called after last device is closed, and all i/o complete. */
997 status_t
998 pc_serial_free(void *cookie)
999 {
1000 	TRACE_FUNCALLS("> pc_serial_free(0x%08x)\n", cookie);
1001 	SerialDevice *device = (SerialDevice *)cookie;
1002 	acquire_sem(gDriverLock);
1003 	status_t status = device->Free();
1004 	if (device->IsRemoved()) {
1005 		for (int32 i = 0; i < DEVICES_COUNT; i++) {
1006 			if (gSerialDevices[i] == device) {
1007 				// the device is removed already but as it was open the
1008 				// removed hook has not deleted the object
1009 				delete device;
1010 				gSerialDevices[i] = NULL;
1011 				break;
1012 			}
1013 		}
1014 	}
1015 
1016 	release_sem(gDriverLock);
1017 	return status;
1018 }
1019 
1020 
1021 /* publish_devices - null-terminated array of devices supported by this driver. */
1022 const char **
1023 publish_devices()
1024 {
1025 	TRACE_FUNCALLS("> publish_devices()\n");
1026 	for (int32 i = 0; gDeviceNames[i]; i++)
1027 		free(gDeviceNames[i]);
1028 
1029 	int j = 0;
1030 	acquire_sem(gDriverLock);
1031 	for(int i = 0; i < DEVICES_COUNT; i++) {
1032 		if (gSerialDevices[i]) {
1033 			gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4);
1034 			if (gDeviceNames[j]) {
1035 				sprintf(gDeviceNames[j], "%s%d", sDeviceBaseName, i);
1036 				j++;
1037 			} else
1038 				TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
1039 		}
1040 	}
1041 
1042 	gDeviceNames[j] = NULL;
1043 	release_sem(gDriverLock);
1044 	return (const char **)&gDeviceNames[0];
1045 }
1046 
1047 
1048 /* find_device - return poiter to device hooks structure for a given device */
1049 device_hooks *
1050 find_device(const char *name)
1051 {
1052 	static device_hooks deviceHooks = {
1053 		pc_serial_open,			/* -> open entry point */
1054 		pc_serial_close,			/* -> close entry point */
1055 		pc_serial_free,			/* -> free cookie */
1056 		pc_serial_control,			/* -> control entry point */
1057 		pc_serial_read,			/* -> read entry point */
1058 		pc_serial_write,			/* -> write entry point */
1059 		pc_serial_select,			/* -> select entry point */
1060 		pc_serial_deselect			/* -> deselect entry point */
1061 	};
1062 
1063 	TRACE_FUNCALLS("> find_device(%s)\n", name);
1064 	return &deviceHooks;
1065 }
1066