1 /*
2 * Copyright 2009-2010, François Revol, <revol@free.fr>.
3 * Sponsored by TuneTracker Systems.
4 * Based on the Haiku usb_serial driver which is:
5 *
6 * Copyright (c) 2007-2008 by Michael Lotz
7 * Heavily based on the original usb_serial driver which is:
8 *
9 * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
10 * Distributed under the terms of the MIT License.
11 */
12 #include <KernelExport.h>
13 #include <dpc.h>
14 #include <Drivers.h>
15 #include <driver_settings.h>
16 #include <image.h>
17 #include <kernel/safemode.h>
18 #include <malloc.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21
22 #include "Driver.h"
23 #include "SerialDevice.h"
24
25 int32 api_version = B_CUR_DRIVER_API_VERSION;
26 static const char *sDeviceBaseName = DEVFS_BASE;
27 SerialDevice *gSerialDevices[DEVICES_COUNT];
28 char *gDeviceNames[DEVICES_COUNT + 1];
29 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 96, //B1200
50 64, //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
pc_serial_insert_device(SerialDevice * device)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 // until we support ISA device enumeration from PnP BIOS or ACPI,
287 // we have to probe the 4 default COM ports...
288 status_t
scan_isa_hardcoded()289 scan_isa_hardcoded()
290 {
291 #ifdef HANDLE_ISA_COM
292 int i;
293 bool serialDebug = get_safemode_boolean("serial_debug_output", true);
294
295 for (i = 0; i < 4; i++) {
296 // skip the port used for kernel debugging...
297 if (serialDebug && sHardcodedPorts[i].ioBase == gKernelDebugPort) {
298 TRACE_ALWAYS("Skipping port %d as it is used for kernel debug.\n", i);
299 continue;
300 }
301
302 SerialDevice *device;
303 device = new(std::nothrow) SerialDevice(&sSupportedDevices[0],
304 sHardcodedPorts[i].ioBase, sHardcodedPorts[i].irq);
305 if (device != NULL && device->Probe())
306 pc_serial_insert_device(device);
307 else
308 delete device;
309 }
310 #endif
311 return B_OK;
312 }
313
314
315 status_t
scan_pci()316 scan_pci()
317 {
318 pci_info info;
319 int ix;
320 TRACE_ALWAYS("scanning PCI bus (alt)...\n");
321
322 // probe PCI devices
323 for (ix = 0; (*gPCIModule->get_nth_pci_info)(ix, &info) == B_OK; ix++) {
324 // sanity check
325 if ((info.header_type & PCI_header_type_mask) != PCI_header_type_generic)
326 continue;
327 /*
328 TRACE_ALWAYS("probing PCI device %2d [%x|%x|%x] %04x:%04x\n",
329 ix, info.class_base, info.class_sub, info.class_api,
330 info.vendor_id, info.device_id);
331 */
332
333 const struct serial_support_descriptor *supported = NULL;
334 for (int i = 0; sSupportedDevices[i].name; i++) {
335 if (sSupportedDevices[i].bus != B_PCI_BUS)
336 continue;
337 if (info.class_base != sSupportedDevices[i].match.class_base)
338 continue;
339 if (info.class_sub != sSupportedDevices[i].match.class_sub)
340 continue;
341 if (info.class_api != sSupportedDevices[i].match.class_api)
342 continue;
343 if (sSupportedDevices[i].match.vendor_id != PCI_INVAL
344 && info.vendor_id != sSupportedDevices[i].match.vendor_id)
345 continue;
346 if (sSupportedDevices[i].match.device_id != PCI_INVAL
347 && info.device_id != sSupportedDevices[i].match.device_id)
348 continue;
349 supported = &sSupportedDevices[i];
350 break;
351 }
352 if (supported == NULL)
353 continue;
354
355 TRACE_ALWAYS("found PCI device %2d [%x|%x|%x] %04x:%04x as %s\n",
356 ix, info.class_base, info.class_sub, info.class_api,
357 info.vendor_id, info.device_id, supported->name);
358
359 // XXX: interrupt_line doesn't seem to
360 TRACE_ALWAYS("irq line %d, pin %d\n",
361 info.u.h0.interrupt_line, info.u.h0.interrupt_pin);
362 int irq = info.u.h0.interrupt_line;
363
364 SerialDevice *master = NULL;
365
366 uint8 portCount = 0;
367 uint32 maxPorts = DEVICES_COUNT;
368
369 if (supported->constraints.maxports) {
370 maxPorts = supported->constraints.maxports;
371 TRACE_ALWAYS("card supports up to %d ports\n", maxPorts);
372 }
373 if (supported->constraints.subsystem_id_mask) {
374 uint32 id = info.u.h0.subsystem_id;
375 uint32 mask = supported->constraints.subsystem_id_mask;
376 id &= mask;
377 //TRACE_ALWAYS("mask: %lx, masked: %lx\n", mask, id);
378 while (!(mask & 0x1)) {
379 mask >>= 1;
380 id >>= 1;
381 }
382 maxPorts = (uint8)id;
383 TRACE_ALWAYS("subsystem id tells card has %d ports\n", maxPorts);
384 }
385
386 // find I/O ports
387 for (int r = 0; r < 6; r++) {
388 uint32 regbase = info.u.h0.base_registers[r];
389 uint32 reglen = info.u.h0.base_register_sizes[r];
390
391 /**/
392 TRACE("ranges[%d] at 0x%08lx len 0x%lx flags 0x%02x\n", r,
393 regbase, reglen, info.u.h0.base_register_flags[r]);
394 /**/
395
396 // empty
397 if (reglen == 0)
398 continue;
399
400 // not I/O
401 if ((info.u.h0.base_register_flags[r] & PCI_address_space) == 0)
402 continue;
403
404 // the range for sure doesn't contain any UART
405 if (supported->constraints.ignoremask & (1 << r)) {
406 TRACE_ALWAYS("ignored regs at 0x%08lx len 0x%lx\n",
407 regbase, reglen);
408 continue;
409 }
410
411 TRACE_ALWAYS("regs at 0x%08lx len 0x%lx\n",
412 regbase, reglen);
413 //&PCI_address_io_mask
414
415 if (reglen < supported->constraints.minsize)
416 continue;
417 if (reglen > supported->constraints.maxsize)
418 continue;
419
420 SerialDevice *device;
421 uint32 ioport = regbase;
422 next_split_alt:
423 // no more to split
424 if ((ioport - regbase) >= reglen)
425 continue;
426
427 if (portCount >= maxPorts)
428 break;
429
430 TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
431 supported->name);
432
433
434 /**/
435 device = new(std::nothrow) SerialDevice(supported, ioport, irq, master);
436 if (device == NULL) {
437 TRACE_ALWAYS("can't allocate device\n");
438 continue;
439 }
440
441 if (pc_serial_insert_device(device) < B_OK) {
442 TRACE_ALWAYS("can't insert device\n");
443 continue;
444 }
445 /**/ if (master == NULL)
446 master = device;
447
448 ioport += supported->constraints.split;
449 portCount++;
450 goto next_split_alt;
451 // try next part of the I/O range now
452
453 }
454 }
455
456 return B_OK;
457 }
458
459
460 static void
check_kernel_debug_port()461 check_kernel_debug_port()
462 {
463 void *handle;
464 long int value;
465
466 handle = load_driver_settings("kernel");
467 if (handle == NULL)
468 return;
469
470 const char *str = get_driver_parameter(handle, "serial_debug_port",
471 NULL, NULL);
472 if (str != NULL) {
473 value = strtol(str, NULL, 0);
474 if (value >= 4) // XXX: actually should be MAX_SERIAL_PORTS...
475 gKernelDebugPort = (uint32)value;
476 else if (value >= 0) // XXX: we should use the kernel_arg's table...
477 gKernelDebugPort = sHardcodedPorts[value].ioBase;
478 }
479
480 /* TODO: actually handle this in the kernel debugger too!
481 bool enabled = get_driver_boolean_parameter(handle, "serial_debug_output",
482 false, true);
483 if (!enabled)
484 gKernelDebugPort = 0;
485 */
486
487 unload_driver_settings(handle);
488 }
489
490
491 //#pragma mark -
492
493
494 /* init_hardware - called once the first time the driver is loaded */
495 status_t
init_hardware()496 init_hardware()
497 {
498 TRACE("init_hardware\n");
499 return B_OK;
500 }
501
502
503 /* init_driver - called every time the driver is loaded. */
504 status_t
init_driver()505 init_driver()
506 {
507 status_t status;
508 load_settings();
509 create_log_file();
510
511 TRACE_FUNCALLS("> init_driver()\n");
512
513 status = get_module(B_DPC_MODULE_NAME, (module_info **)&gDPCModule);
514 if (status < B_OK)
515 goto err_dpc;
516
517 status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
518 if (status < B_OK)
519 goto err_tty;
520
521 status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCIModule);
522 if (status < B_OK)
523 goto err_pci;
524
525 status = get_module(B_ISA_MODULE_NAME, (module_info **)&gISAModule);
526 if (status < B_OK)
527 goto err_isa;
528
529 status = gDPCModule->new_dpc_queue(&gDPCHandle, "pc_serial irq",
530 B_REAL_TIME_PRIORITY);
531 if (status != B_OK)
532 goto err_dpcq;
533
534 for (int32 i = 0; i < DEVICES_COUNT; i++)
535 gSerialDevices[i] = NULL;
536
537 gDeviceNames[0] = NULL;
538
539 gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
540 if (gDriverLock < B_OK) {
541 status = gDriverLock;
542 goto err_sem;
543 }
544
545 status = ENOENT;
546
547 check_kernel_debug_port();
548
549 scan_isa_hardcoded();
550 scan_pci();
551
552 // XXX: ISA cards
553 // XXX: pcmcia
554
555 TRACE_FUNCRET("< init_driver() returns\n");
556 return B_OK;
557
558 //err_none:
559 delete_sem(gDriverLock);
560 err_sem:
561 gDPCModule->delete_dpc_queue(gDPCHandle);
562 gDPCHandle = NULL;
563 err_dpcq:
564 put_module(B_ISA_MODULE_NAME);
565 err_isa:
566 put_module(B_PCI_MODULE_NAME);
567 err_pci:
568 put_module(B_TTY_MODULE_NAME);
569 err_tty:
570 put_module(B_DPC_MODULE_NAME);
571 err_dpc:
572 TRACE_FUNCRET("< init_driver() returns %s\n", strerror(status));
573 return status;
574 }
575
576
577 /* uninit_driver - called every time the driver is unloaded */
578 void
uninit_driver()579 uninit_driver()
580 {
581 TRACE_FUNCALLS("> uninit_driver()\n");
582
583 //gUSBModule->uninstall_notify(DRIVER_NAME);
584 acquire_sem(gDriverLock);
585
586 for (int32 i = 0; i < DEVICES_COUNT; i++) {
587 if (gSerialDevices[i]) {
588 /*
589 if (gSerialDevices[i]->Master() == gSerialDevices[i])
590 remove_io_interrupt_handler(gSerialDevices[i]->IRQ(),
591 pc_serial_interrupt, gSerialDevices[i]);
592 */
593 delete gSerialDevices[i];
594 gSerialDevices[i] = NULL;
595 }
596 }
597
598 for (int32 i = 0; gDeviceNames[i]; i++)
599 free(gDeviceNames[i]);
600
601 delete_sem(gDriverLock);
602 gDPCModule->delete_dpc_queue(gDPCHandle);
603 gDPCHandle = NULL;
604 put_module(B_ISA_MODULE_NAME);
605 put_module(B_PCI_MODULE_NAME);
606 put_module(B_TTY_MODULE_NAME);
607 put_module(B_DPC_MODULE_NAME);
608
609 TRACE_FUNCRET("< uninit_driver() returns\n");
610 }
611
612
613 bool
pc_serial_service(struct tty * tty,uint32 op,void * buffer,size_t length)614 pc_serial_service(struct tty *tty, uint32 op, void *buffer, size_t length)
615 {
616 TRACE_FUNCALLS("> pc_serial_service(%p, 0x%08lx, %p, %lu)\n", tty,
617 op, buffer, length);
618
619
620 for (int32 i = 0; i < DEVICES_COUNT; i++) {
621 if (gSerialDevices[i]
622 && gSerialDevices[i]->Service(tty, op, buffer, length)) {
623 TRACE_FUNCRET("< pc_serial_service() returns: true\n");
624 return true;
625 }
626 }
627
628 TRACE_FUNCRET("< pc_serial_service() returns: false\n");
629 return false;
630 }
631
632
633 static void
pc_serial_dpc(void * arg)634 pc_serial_dpc(void *arg)
635 {
636 SerialDevice *master = (SerialDevice *)arg;
637 TRACE_FUNCALLS("> pc_serial_dpc(%p)\n", arg);
638 master->InterruptHandler();
639 }
640
641
642 int32
pc_serial_interrupt(void * arg)643 pc_serial_interrupt(void *arg)
644 {
645 SerialDevice *device = (SerialDevice *)arg;
646 TRACE_FUNCALLS("> pc_serial_interrupt(%p)\n", arg);
647
648 if (!device)
649 return B_UNHANDLED_INTERRUPT;
650
651 if (device->IsInterruptPending()) {
652 status_t err;
653 err = gDPCModule->queue_dpc(gDPCHandle, pc_serial_dpc, device);
654 if (err != B_OK)
655 dprintf(DRIVER_NAME ": error queing irq: %s\n", strerror(err));
656 else {
657 TRACE_FUNCRET("< pc_serial_interrupt() returns: resched\n");
658 return B_INVOKE_SCHEDULER;
659 }
660 }
661
662 TRACE_FUNCRET("< pc_serial_interrupt() returns: unhandled\n");
663 return B_UNHANDLED_INTERRUPT;
664 }
665
666
667 /* pc_serial_open - handle open() calls */
668 status_t
pc_serial_open(const char * name,uint32 flags,void ** cookie)669 pc_serial_open(const char *name, uint32 flags, void **cookie)
670 {
671 TRACE_FUNCALLS("> pc_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
672 acquire_sem(gDriverLock);
673 status_t status = ENODEV;
674
675 *cookie = NULL;
676 int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
677 if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
678 status = gSerialDevices[i]->Open(flags);
679 *cookie = gSerialDevices[i];
680 }
681
682 release_sem(gDriverLock);
683 TRACE_FUNCRET("< pc_serial_open() returns: 0x%08x\n", status);
684 return status;
685 }
686
687
688 /* pc_serial_read - handle read() calls */
689 status_t
pc_serial_read(void * cookie,off_t position,void * buffer,size_t * numBytes)690 pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
691 {
692 TRACE_FUNCALLS("> pc_serial_read(0x%08x, %lld, 0x%08x, %d)\n", cookie,
693 position, buffer, *numBytes);
694 SerialDevice *device = (SerialDevice *)cookie;
695 return device->Read((char *)buffer, numBytes);
696 }
697
698
699 /* pc_serial_write - handle write() calls */
700 status_t
pc_serial_write(void * cookie,off_t position,const void * buffer,size_t * numBytes)701 pc_serial_write(void *cookie, off_t position, const void *buffer,
702 size_t *numBytes)
703 {
704 TRACE_FUNCALLS("> pc_serial_write(0x%08x, %lld, 0x%08x, %d)\n", cookie,
705 position, buffer, *numBytes);
706 SerialDevice *device = (SerialDevice *)cookie;
707 return device->Write((const char *)buffer, numBytes);
708 }
709
710
711 /* pc_serial_control - handle ioctl calls */
712 status_t
pc_serial_control(void * cookie,uint32 op,void * arg,size_t length)713 pc_serial_control(void *cookie, uint32 op, void *arg, size_t length)
714 {
715 TRACE_FUNCALLS("> pc_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
716 cookie, op, arg, length);
717 SerialDevice *device = (SerialDevice *)cookie;
718 return device->Control(op, arg, length);
719 }
720
721
722 /* pc_serial_select - handle select start */
723 status_t
pc_serial_select(void * cookie,uint8 event,uint32 ref,selectsync * sync)724 pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
725 {
726 TRACE_FUNCALLS("> pc_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
727 cookie, event, ref, sync);
728 SerialDevice *device = (SerialDevice *)cookie;
729 return device->Select(event, ref, sync);
730 }
731
732
733 /* pc_serial_deselect - handle select exit */
734 status_t
pc_serial_deselect(void * cookie,uint8 event,selectsync * sync)735 pc_serial_deselect(void *cookie, uint8 event, selectsync *sync)
736 {
737 TRACE_FUNCALLS("> pc_serial_deselect(0x%08x, 0x%08x, %p)\n",
738 cookie, event, sync);
739 SerialDevice *device = (SerialDevice *)cookie;
740 return device->DeSelect(event, sync);
741 }
742
743
744 /* pc_serial_close - handle close() calls */
745 status_t
pc_serial_close(void * cookie)746 pc_serial_close(void *cookie)
747 {
748 TRACE_FUNCALLS("> pc_serial_close(0x%08x)\n", cookie);
749 SerialDevice *device = (SerialDevice *)cookie;
750 return device->Close();
751 }
752
753
754 /* pc_serial_free - called after last device is closed, and all i/o complete. */
755 status_t
pc_serial_free(void * cookie)756 pc_serial_free(void *cookie)
757 {
758 TRACE_FUNCALLS("> pc_serial_free(0x%08x)\n", cookie);
759 SerialDevice *device = (SerialDevice *)cookie;
760 acquire_sem(gDriverLock);
761 status_t status = device->Free();
762 if (device->IsRemoved()) {
763 for (int32 i = 0; i < DEVICES_COUNT; i++) {
764 if (gSerialDevices[i] == device) {
765 // the device is removed already but as it was open the
766 // removed hook has not deleted the object
767 delete device;
768 gSerialDevices[i] = NULL;
769 break;
770 }
771 }
772 }
773
774 release_sem(gDriverLock);
775 return status;
776 }
777
778
779 /* publish_devices - null-terminated array of devices supported by this driver. */
780 const char **
publish_devices()781 publish_devices()
782 {
783 TRACE_FUNCALLS("> publish_devices()\n");
784 for (int32 i = 0; gDeviceNames[i]; i++)
785 free(gDeviceNames[i]);
786
787 int j = 0;
788 acquire_sem(gDriverLock);
789 for(int i = 0; i < DEVICES_COUNT; i++) {
790 if (gSerialDevices[i]) {
791 gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4);
792 if (gDeviceNames[j]) {
793 sprintf(gDeviceNames[j], "%s%d", sDeviceBaseName, i);
794 j++;
795 } else
796 TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
797 }
798 }
799
800 gDeviceNames[j] = NULL;
801 release_sem(gDriverLock);
802 return (const char **)&gDeviceNames[0];
803 }
804
805
806 /* find_device - return poiter to device hooks structure for a given device */
807 device_hooks *
find_device(const char * name)808 find_device(const char *name)
809 {
810 static device_hooks deviceHooks = {
811 pc_serial_open, /* -> open entry point */
812 pc_serial_close, /* -> close entry point */
813 pc_serial_free, /* -> free cookie */
814 pc_serial_control, /* -> control entry point */
815 pc_serial_read, /* -> read entry point */
816 pc_serial_write, /* -> write entry point */
817 pc_serial_select, /* -> select entry point */
818 pc_serial_deselect /* -> deselect entry point */
819 };
820
821 TRACE_FUNCALLS("> find_device(%s)\n", name);
822 return &deviceHooks;
823 }
824