xref: /haiku/src/add-ons/kernel/drivers/ports/pc_serial/Driver.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 			TRACE_ALWAYS("Skipping port %d as it is used for kernel debug.\n", i);
531 			continue;
532 		}
533 
534 		SerialDevice *device;
535 		device = new(std::nothrow) SerialDevice(&sSupportedDevices[0],
536 			sHardcodedPorts[i].ioBase, sHardcodedPorts[i].irq);
537 		if (device != NULL && device->Probe())
538 			pc_serial_insert_device(device);
539 		else
540 			delete device;
541 	}
542 #endif
543 	return B_OK;
544 }
545 
546 
547 // this version doesn't use config_manager, but can't probe the IRQ yet
548 status_t
549 scan_pci_alt()
550 {
551 	pci_info info;
552 	int ix;
553 	TRACE_ALWAYS("scanning PCI bus (alt)...\n");
554 
555 	// probe PCI devices
556 	for (ix = 0; (*gPCIModule->get_nth_pci_info)(ix, &info) == B_OK; ix++) {
557 		// sanity check
558 		if ((info.header_type & PCI_header_type_mask) != PCI_header_type_generic)
559 			continue;
560 		/*
561 		TRACE_ALWAYS("probing PCI device %2d [%x|%x|%x] %04x:%04x\n",
562 			ix, info.class_base, info.class_sub, info.class_api,
563 			info.vendor_id, info.device_id);
564 		*/
565 
566 		const struct serial_support_descriptor *supported = NULL;
567 		for (int i = 0; sSupportedDevices[i].name; i++) {
568 			if (sSupportedDevices[i].bus != B_PCI_BUS)
569 				continue;
570 			if (info.class_base != sSupportedDevices[i].match.class_base)
571 				continue;
572 			if (info.class_sub != sSupportedDevices[i].match.class_sub)
573 				continue;
574 			if (info.class_api != sSupportedDevices[i].match.class_api)
575 				continue;
576 			if (sSupportedDevices[i].match.vendor_id != PCI_INVAL
577 				&& info.vendor_id != sSupportedDevices[i].match.vendor_id)
578 				continue;
579 			if (sSupportedDevices[i].match.device_id != PCI_INVAL
580 				&& info.device_id != sSupportedDevices[i].match.device_id)
581 				continue;
582 			supported = &sSupportedDevices[i];
583 			break;
584 		}
585 		if (supported == NULL)
586 			continue;
587 
588 		TRACE_ALWAYS("found PCI device %2d [%x|%x|%x] %04x:%04x as %s\n",
589 			ix, info.class_base, info.class_sub, info.class_api,
590 			info.vendor_id, info.device_id, supported->name);
591 
592 		// XXX: interrupt_line doesn't seem to
593 		TRACE_ALWAYS("irq line %d, pin %d\n",
594 			info.u.h0.interrupt_line, info.u.h0.interrupt_pin);
595 		int irq = info.u.h0.interrupt_line;
596 
597 		SerialDevice *master = NULL;
598 
599 		uint8 portCount = 0;
600 		uint32 maxPorts = DEVICES_COUNT;
601 
602 		if (supported->constraints.maxports) {
603 			maxPorts = supported->constraints.maxports;
604 			TRACE_ALWAYS("card supports up to %d ports\n", maxPorts);
605 		}
606 		if (supported->constraints.subsystem_id_mask) {
607 			uint32 id = info.u.h0.subsystem_id;
608 			uint32 mask = supported->constraints.subsystem_id_mask;
609 			id &= mask;
610 			//TRACE_ALWAYS("mask: %lx, masked: %lx\n", mask, id);
611 			while (!(mask & 0x1)) {
612 				mask >>= 1;
613 				id >>= 1;
614 			}
615 			maxPorts = (uint8)id;
616 			TRACE_ALWAYS("subsystem id tells card has %d ports\n", maxPorts);
617 		}
618 
619 		// find I/O ports
620 		for (int r = 0; r < 6; r++) {
621 			uint32 regbase = info.u.h0.base_registers[r];
622 			uint32 reglen = info.u.h0.base_register_sizes[r];
623 
624 			/**/
625 			TRACE("ranges[%d] at 0x%08lx len 0x%lx flags 0x%02x\n", r,
626 				regbase, reglen, info.u.h0.base_register_flags[r]);
627 			/**/
628 
629 			// empty
630 			if (reglen == 0)
631 				continue;
632 
633 			// not I/O
634 			if ((info.u.h0.base_register_flags[r] & PCI_address_space) == 0)
635 				continue;
636 
637 			// the range for sure doesn't contain any UART
638 			if (supported->constraints.ignoremask & (1 << r)) {
639 				TRACE_ALWAYS("ignored regs at 0x%08lx len 0x%lx\n",
640 					regbase, reglen);
641 				continue;
642 			}
643 
644 			TRACE_ALWAYS("regs at 0x%08lx len 0x%lx\n",
645 				regbase, reglen);
646 			//&PCI_address_io_mask
647 
648 			if (reglen < supported->constraints.minsize)
649 				continue;
650 			if (reglen > supported->constraints.maxsize)
651 				continue;
652 
653 			SerialDevice *device;
654 			uint32 ioport = regbase;
655 next_split_alt:
656 			// no more to split
657 			if ((ioport - regbase) >= reglen)
658 				continue;
659 
660 			if (portCount >= maxPorts)
661 				break;
662 
663 			TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
664 				supported->name);
665 
666 
667 /**/
668 			device = new(std::nothrow) SerialDevice(supported, ioport, irq, master);
669 			if (device == NULL) {
670 				TRACE_ALWAYS("can't allocate device\n");
671 				continue;
672 			}
673 
674 			if (pc_serial_insert_device(device) < B_OK) {
675 				TRACE_ALWAYS("can't insert device\n");
676 				continue;
677 			}
678 /**/			if (master == NULL)
679 				master = device;
680 
681 			ioport += supported->constraints.split;
682 			portCount++;
683 			goto next_split_alt;
684 			// try next part of the I/O range now
685 
686 		}
687 	}
688 
689 	return B_OK;
690 }
691 
692 
693 static void
694 check_kernel_debug_port()
695 {
696 	void *handle;
697 	long int value;
698 
699 	handle = load_driver_settings("kernel");
700 	if (handle == NULL)
701 		return;
702 
703 	const char *str = get_driver_parameter(handle, "serial_debug_port",
704 		NULL, NULL);
705 	if (str != NULL) {
706 		value = strtol(str, NULL, 0);
707 		if (value >= 4) // XXX: actually should be MAX_SERIAL_PORTS...
708 			gKernelDebugPort = (uint32)value;
709 		else if (value >= 0) // XXX: we should use the kernel_arg's table...
710 			gKernelDebugPort = sHardcodedPorts[value].ioBase;
711 	}
712 
713 	/* TODO: actually handle this in the kernel debugger too!
714 	bool enabled = get_driver_boolean_parameter(handle, "serial_debug_output",
715 		false, true);
716 	if (!enabled)
717 		gKernelDebugPort = 0;
718 	*/
719 
720 	unload_driver_settings(handle);
721 }
722 
723 
724 //#pragma mark -
725 
726 
727 /* init_hardware - called once the first time the driver is loaded */
728 status_t
729 init_hardware()
730 {
731 	TRACE("init_hardware\n");
732 	return B_OK;
733 }
734 
735 
736 /* init_driver - called every time the driver is loaded. */
737 status_t
738 init_driver()
739 {
740 	status_t status;
741 	load_settings();
742 	create_log_file();
743 
744 	TRACE_FUNCALLS("> init_driver()\n");
745 
746 	status = get_module(B_DPC_MODULE_NAME, (module_info **)&gDPCModule);
747 	if (status < B_OK)
748 		goto err_dpc;
749 
750 	status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
751 	if (status < B_OK)
752 		goto err_tty;
753 
754 	status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCIModule);
755 	if (status < B_OK)
756 		goto err_pci;
757 
758 	status = get_module(B_ISA_MODULE_NAME, (module_info **)&gISAModule);
759 	if (status < B_OK)
760 		goto err_isa;
761 
762 	status = get_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME,
763 		(module_info **)&gConfigManagerModule);
764 	if (status < B_OK)
765 		goto err_cm;
766 
767 	status = gDPCModule->new_dpc_queue(&gDPCHandle, "pc_serial irq",
768 		B_REAL_TIME_PRIORITY);
769 	if (status != B_OK)
770 		goto err_dpcq;
771 
772 	for (int32 i = 0; i < DEVICES_COUNT; i++)
773 		gSerialDevices[i] = NULL;
774 
775 	gDeviceNames[0] = NULL;
776 
777 	gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
778 	if (gDriverLock < B_OK) {
779 		status = gDriverLock;
780 		goto err_sem;
781 	}
782 
783 	status = ENOENT;
784 
785 	check_kernel_debug_port();
786 
787 	(void)scan_bus;
788 	//scan_bus(B_ISA_BUS);
789 	//scan_bus(B_PCI_BUS);
790 	scan_isa_hardcoded();
791 	scan_pci_alt();
792 
793 	// XXX: ISA cards
794 	// XXX: pcmcia
795 
796 	TRACE_FUNCRET("< init_driver() returns\n");
797 	return B_OK;
798 
799 //err_none:
800 	delete_sem(gDriverLock);
801 err_sem:
802 	gDPCModule->delete_dpc_queue(gDPCHandle);
803 	gDPCHandle = NULL;
804 err_dpcq:
805 	put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
806 err_cm:
807 	put_module(B_ISA_MODULE_NAME);
808 err_isa:
809 	put_module(B_PCI_MODULE_NAME);
810 err_pci:
811 	put_module(B_TTY_MODULE_NAME);
812 err_tty:
813 	put_module(B_DPC_MODULE_NAME);
814 err_dpc:
815 	TRACE_FUNCRET("< init_driver() returns %s\n", strerror(status));
816 	return status;
817 }
818 
819 
820 /* uninit_driver - called every time the driver is unloaded */
821 void
822 uninit_driver()
823 {
824 	TRACE_FUNCALLS("> uninit_driver()\n");
825 
826 	//gUSBModule->uninstall_notify(DRIVER_NAME);
827 	acquire_sem(gDriverLock);
828 
829 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
830 		if (gSerialDevices[i]) {
831 			/*
832 			if (gSerialDevices[i]->Master() == gSerialDevices[i])
833 				remove_io_interrupt_handler(gSerialDevices[i]->IRQ(),
834 					pc_serial_interrupt, gSerialDevices[i]);
835 			*/
836 			delete gSerialDevices[i];
837 			gSerialDevices[i] = NULL;
838 		}
839 	}
840 
841 	for (int32 i = 0; gDeviceNames[i]; i++)
842 		free(gDeviceNames[i]);
843 
844 	delete_sem(gDriverLock);
845 	gDPCModule->delete_dpc_queue(gDPCHandle);
846 	gDPCHandle = NULL;
847 	put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
848 	put_module(B_ISA_MODULE_NAME);
849 	put_module(B_PCI_MODULE_NAME);
850 	put_module(B_TTY_MODULE_NAME);
851 	put_module(B_DPC_MODULE_NAME);
852 
853 	TRACE_FUNCRET("< uninit_driver() returns\n");
854 }
855 
856 
857 bool
858 pc_serial_service(struct tty *tty, uint32 op, void *buffer, size_t length)
859 {
860 	TRACE_FUNCALLS("> pc_serial_service(%p, 0x%08lx, %p, %lu)\n", tty,
861 		op, buffer, length);
862 
863 
864 	for (int32 i = 0; i < DEVICES_COUNT; i++) {
865 		if (gSerialDevices[i]
866 			&& gSerialDevices[i]->Service(tty, op, buffer, length)) {
867 			TRACE_FUNCRET("< pc_serial_service() returns: true\n");
868 			return true;
869 		}
870 	}
871 
872 	TRACE_FUNCRET("< pc_serial_service() returns: false\n");
873 	return false;
874 }
875 
876 
877 static void
878 pc_serial_dpc(void *arg)
879 {
880 	SerialDevice *master = (SerialDevice *)arg;
881 	TRACE_FUNCALLS("> pc_serial_dpc(%p)\n", arg);
882 	master->InterruptHandler();
883 }
884 
885 
886 int32
887 pc_serial_interrupt(void *arg)
888 {
889 	SerialDevice *device = (SerialDevice *)arg;
890 	TRACE_FUNCALLS("> pc_serial_interrupt(%p)\n", arg);
891 
892 	if (!device)
893 		return B_UNHANDLED_INTERRUPT;
894 
895 	if (device->IsInterruptPending()) {
896 		status_t err;
897 		err = gDPCModule->queue_dpc(gDPCHandle, pc_serial_dpc, device);
898 		if (err != B_OK)
899 			dprintf(DRIVER_NAME ": error queing irq: %s\n", strerror(err));
900 		else {
901 			TRACE_FUNCRET("< pc_serial_interrupt() returns: resched\n");
902 			return B_INVOKE_SCHEDULER;
903 		}
904 	}
905 
906 	TRACE_FUNCRET("< pc_serial_interrupt() returns: unhandled\n");
907 	return B_UNHANDLED_INTERRUPT;
908 }
909 
910 
911 /* pc_serial_open - handle open() calls */
912 status_t
913 pc_serial_open(const char *name, uint32 flags, void **cookie)
914 {
915 	TRACE_FUNCALLS("> pc_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
916 	acquire_sem(gDriverLock);
917 	status_t status = ENODEV;
918 
919 	*cookie = NULL;
920 	int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
921 	if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
922 		status = gSerialDevices[i]->Open(flags);
923 		*cookie = gSerialDevices[i];
924 	}
925 
926 	release_sem(gDriverLock);
927 	TRACE_FUNCRET("< pc_serial_open() returns: 0x%08x\n", status);
928 	return status;
929 }
930 
931 
932 /* pc_serial_read - handle read() calls */
933 status_t
934 pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
935 {
936 	TRACE_FUNCALLS("> pc_serial_read(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
937 		position, buffer, *numBytes);
938 	SerialDevice *device = (SerialDevice *)cookie;
939 	return device->Read((char *)buffer, numBytes);
940 }
941 
942 
943 /* pc_serial_write - handle write() calls */
944 status_t
945 pc_serial_write(void *cookie, off_t position, const void *buffer,
946 	size_t *numBytes)
947 {
948 	TRACE_FUNCALLS("> pc_serial_write(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
949 		position, buffer, *numBytes);
950 	SerialDevice *device = (SerialDevice *)cookie;
951 	return device->Write((const char *)buffer, numBytes);
952 }
953 
954 
955 /* pc_serial_control - handle ioctl calls */
956 status_t
957 pc_serial_control(void *cookie, uint32 op, void *arg, size_t length)
958 {
959 	TRACE_FUNCALLS("> pc_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
960 		cookie, op, arg, length);
961 	SerialDevice *device = (SerialDevice *)cookie;
962 	return device->Control(op, arg, length);
963 }
964 
965 
966 /* pc_serial_select - handle select start */
967 status_t
968 pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
969 {
970 	TRACE_FUNCALLS("> pc_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
971 		cookie, event, ref, sync);
972 	SerialDevice *device = (SerialDevice *)cookie;
973 	return device->Select(event, ref, sync);
974 }
975 
976 
977 /* pc_serial_deselect - handle select exit */
978 status_t
979 pc_serial_deselect(void *cookie, uint8 event, selectsync *sync)
980 {
981 	TRACE_FUNCALLS("> pc_serial_deselect(0x%08x, 0x%08x, %p)\n",
982 		cookie, event, sync);
983 	SerialDevice *device = (SerialDevice *)cookie;
984 	return device->DeSelect(event, sync);
985 }
986 
987 
988 /* pc_serial_close - handle close() calls */
989 status_t
990 pc_serial_close(void *cookie)
991 {
992 	TRACE_FUNCALLS("> pc_serial_close(0x%08x)\n", cookie);
993 	SerialDevice *device = (SerialDevice *)cookie;
994 	return device->Close();
995 }
996 
997 
998 /* pc_serial_free - called after last device is closed, and all i/o complete. */
999 status_t
1000 pc_serial_free(void *cookie)
1001 {
1002 	TRACE_FUNCALLS("> pc_serial_free(0x%08x)\n", cookie);
1003 	SerialDevice *device = (SerialDevice *)cookie;
1004 	acquire_sem(gDriverLock);
1005 	status_t status = device->Free();
1006 	if (device->IsRemoved()) {
1007 		for (int32 i = 0; i < DEVICES_COUNT; i++) {
1008 			if (gSerialDevices[i] == device) {
1009 				// the device is removed already but as it was open the
1010 				// removed hook has not deleted the object
1011 				delete device;
1012 				gSerialDevices[i] = NULL;
1013 				break;
1014 			}
1015 		}
1016 	}
1017 
1018 	release_sem(gDriverLock);
1019 	return status;
1020 }
1021 
1022 
1023 /* publish_devices - null-terminated array of devices supported by this driver. */
1024 const char **
1025 publish_devices()
1026 {
1027 	TRACE_FUNCALLS("> publish_devices()\n");
1028 	for (int32 i = 0; gDeviceNames[i]; i++)
1029 		free(gDeviceNames[i]);
1030 
1031 	int j = 0;
1032 	acquire_sem(gDriverLock);
1033 	for(int i = 0; i < DEVICES_COUNT; i++) {
1034 		if (gSerialDevices[i]) {
1035 			gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4);
1036 			if (gDeviceNames[j]) {
1037 				sprintf(gDeviceNames[j], "%s%d", sDeviceBaseName, i);
1038 				j++;
1039 			} else
1040 				TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
1041 		}
1042 	}
1043 
1044 	gDeviceNames[j] = NULL;
1045 	release_sem(gDriverLock);
1046 	return (const char **)&gDeviceNames[0];
1047 }
1048 
1049 
1050 /* find_device - return poiter to device hooks structure for a given device */
1051 device_hooks *
1052 find_device(const char *name)
1053 {
1054 	static device_hooks deviceHooks = {
1055 		pc_serial_open,			/* -> open entry point */
1056 		pc_serial_close,			/* -> close entry point */
1057 		pc_serial_free,			/* -> free cookie */
1058 		pc_serial_control,			/* -> control entry point */
1059 		pc_serial_read,			/* -> read entry point */
1060 		pc_serial_write,			/* -> write entry point */
1061 		pc_serial_select,			/* -> select entry point */
1062 		pc_serial_deselect			/* -> deselect entry point */
1063 	};
1064 
1065 	TRACE_FUNCALLS("> find_device(%s)\n", name);
1066 	return &deviceHooks;
1067 }
1068