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