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