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 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 // until we support ISA device enumeration from PnP BIOS or ACPI, 287 // we have to probe the 4 default COM ports... 288 status_t 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 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 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 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 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 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 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 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 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 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 690 pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes) 691 { 692 TRACE_FUNCALLS("> pc_serial_read(0x%08x, %Ld, 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 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, %Ld, 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 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 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 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 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 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 ** 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 * 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