static status_t acpi_namespace_open(void *_cookie, const char* path, int flags, void** cookie) { acpi_ns_device_info *device = (acpi_ns_device_info *)_cookie; dprintf("\nacpi_ns_dump: device_open\n"); *cookie = device; RingBuffer *ringBuffer = new RingBuffer(1024); if (ringBuffer == NULL) return B_NO_MEMORY; device->read_sem = create_sem(0, "read_sem"); if (device->read_sem < B_OK) { delete ringBuffer; return device->read_sem; } device->thread = spawn_kernel_thread(acpi_namespace_dump, "acpi dumper", B_NORMAL_PRIORITY, device); if (device->thread < 0) { delete ringBuffer; delete_sem(device->read_sem); return device->thread; } device->buffer = ringBuffer; resume_thread(device->thread); return B_OK; }
status_t JoystickProtocolHandler::Open(uint32 flags, uint32 *cookie) { if (fCurrentValues.data == NULL) return B_NO_INIT; status_t result = mutex_lock(&fUpdateLock); if (result != B_OK) return result; if (fUpdateThread < 0) { fUpdateThread = spawn_kernel_thread(_UpdateThread, "joystick update", B_NORMAL_PRIORITY, (void *)this); if (fUpdateThread < 0) result = fUpdateThread; else resume_thread(fUpdateThread); } if (result == B_OK) fOpenCount++; mutex_unlock(&fUpdateLock); if (result != B_OK) return result; return ProtocolHandler::Open(flags, cookie); }
void __attribute__((noreturn)) kernel_main(unsigned int _memory_size) { struct vm_translation_map *init_map; struct process *init_proc; vm_page_init(_memory_size); init_map = vm_translation_map_init(); boot_init_heap((char*) KERNEL_HEAP_BASE + PAGE_STRUCTURES_SIZE(_memory_size)); vm_address_space_init(init_map); bootstrap_vm_cache(); bool_init_kernel_process(); boot_init_thread(); // Start other threads REGISTERS[REG_THREAD_RESUME] = 0xffffffff; spawn_kernel_thread("Grim Reaper", grim_reaper, 0); init_proc = exec_program("program.elf"); // Idle task for (;;) { if (list_is_empty(&init_proc->thread_list)) { kprintf("init process has exited, shutting down\n"); REGISTERS[REG_THREAD_HALT] = 0xffffffff; } reschedule(); } }
status_t ps2_service_init(void) { TRACE("ps2: ps2_service_init\n"); sServiceCmdBuffer = create_packet_buffer(sizeof(ps2_service_cmd) * 50); if (sServiceCmdBuffer == NULL) goto err1; sServiceSem = create_sem(0, "ps2 service"); if (sServiceSem < B_OK) goto err2; sServiceThread = spawn_kernel_thread(ps2_service_thread, "ps2 service", 20, NULL); if (sServiceThread < B_OK) goto err3; sServiceTerminate = false; resume_thread(sServiceThread); #ifdef DEBUG_PUBLISHING add_debugger_command("ps2republish", &ps2_republish, "republish a ps2 device (0-3 mouse, 4 keyb (default))"); #endif TRACE("ps2: ps2_service_init done\n"); return B_OK; err3: delete_sem(sServiceSem); err2: delete_packet_buffer(sServiceCmdBuffer); err1: TRACE("ps2: ps2_service_init failed\n"); return B_ERROR; }
status_t init_callout(void) { list_init(&sTimers); sTimeout = B_INFINITE_TIMEOUT; status_t status = B_OK; mutex_init(&sLock, "fbsd callout"); sWaitSem = create_sem(0, "fbsd callout wait"); if (sWaitSem < 0) { status = sWaitSem; goto err1; } sThread = spawn_kernel_thread(callout_thread, "fbsd callout", B_DISPLAY_PRIORITY, NULL); if (sThread < 0) { status = sThread; goto err2; } return resume_thread(sThread); err1: mutex_destroy(&sLock); err2: delete_sem(sWaitSem); return status; }
// _InitBroadcastListener status_t ServerManager::_InitBroadcastListener() { // create a socket fBroadcastListenerSocket = socket(AF_INET, SOCK_DGRAM, 0); if (fBroadcastListenerSocket < 0) return errno; // bind it to the port sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(kDefaultBroadcastPort); addr.sin_addr.s_addr = INADDR_ANY; if (bind(fBroadcastListenerSocket, (sockaddr*)&addr, sizeof(addr)) < 0) { ERROR("ServerManager::_InitBroadcastListener(): ERROR: bind()ing the " "broadcasting socket failed: %s\n", strerror(errno)); safe_closesocket(fBroadcastListenerSocket); return errno; } // spawn the thread #if USER fBroadcastListener = spawn_thread(&_BroadcastListenerEntry, "broadcast listener", B_NORMAL_PRIORITY, this); #else fBroadcastListener = spawn_kernel_thread(&_BroadcastListenerEntry, "broadcast listener", B_NORMAL_PRIORITY, this); #endif if (fBroadcastListener < 0) return fBroadcastListener; return B_OK; }
static status_t std_ops(int32 op, ...) { if (op == B_MODULE_INIT) { sRequestSem = create_sem(0, "run_on_exit_request"); if (sRequestSem < B_OK) return sRequestSem; thread_id thread = spawn_kernel_thread(&run_on_exit_loop, "run_on_exit_loop", B_NORMAL_PRIORITY, NULL); if (thread < B_OK) return thread; send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); add_debugger_command_etc("on_exit", &add_run_on_exit_command, "Adds a command to be run when leaving the kernel debugger", "<command> [<arguments>]\n" "Adds a command to be run when leaving the kernel debugger.\n", 0); return B_OK; } else if (op == B_MODULE_UNINIT) { remove_debugger_command("on_exit", &add_run_on_exit_command); // deleting the sem will also cause the thread to exit delete_sem(sRequestSem); sRequestSem = -1; return B_OK; } return B_BAD_VALUE; }
status_t ethernet_up(net_device *_device) { ethernet_device *device = (ethernet_device *)_device; device->fd = open(device->name, O_RDWR); if (device->fd < 0) return errno; uint64 dummy; if (ioctl(device->fd, ETHER_INIT, &dummy, sizeof(dummy)) < 0) goto err; if (ioctl(device->fd, ETHER_GETADDR, device->address.data, ETHER_ADDRESS_LENGTH) < 0) goto err; if (ioctl(device->fd, ETHER_GETFRAMESIZE, &device->frame_size, sizeof(uint32)) < 0) { // this call is obviously optional device->frame_size = ETHER_MAX_FRAME_SIZE; } if (update_link_state(device, false) == B_OK) { // device supports retrieval of the link state // Set the change notification semaphore; doesn't matter // if this is supported by the device or not ioctl(device->fd, ETHER_SET_LINK_STATE_SEM, &sLinkChangeSemaphore, sizeof(sem_id)); MutexLocker _(&sListLock); if (sCheckList.IsEmpty()) { // start thread sLinkCheckerThread = spawn_kernel_thread(ethernet_link_checker, "ethernet link state checker", B_LOW_PRIORITY, NULL); if (sLinkCheckerThread >= B_OK) resume_thread(sLinkCheckerThread); } sCheckList.Add(device); } device->address.length = ETHER_ADDRESS_LENGTH; device->mtu = device->frame_size - device->header_length; return B_OK; err: close(device->fd); device->fd = -1; return errno; }
DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread) { thread_id NativeThread; RT_ASSERT_PREEMPTIBLE(); NativeThread = spawn_kernel_thread(rtThreadNativeMain, pThreadInt->szName, B_NORMAL_PRIORITY, pThreadInt); if (NativeThread >= B_OK) { resume_thread(NativeThread); *pNativeThread = (RTNATIVETHREAD)NativeThread; return VINF_SUCCESS; } return RTErrConvertFromHaikuKernReturn(NativeThread); }
status_t low_resource_manager_init_post_thread(void) { sLowResourceWaitSem = create_sem(0, "low resource wait"); if (sLowResourceWaitSem < B_OK) return sLowResourceWaitSem; thread_id thread = spawn_kernel_thread(&low_resource_manager, "low resource manager", B_LOW_PRIORITY, NULL); send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); add_debugger_command("low_resource", &dump_handlers, "Dump list of low resource handlers"); return B_OK; }
// Run status_t Task::Run() { const char* name = (fName.GetLength() > 0 ? fName.GetString() : "task"); #if USER fThread = spawn_thread(&_ThreadEntry, name, B_NORMAL_PRIORITY, this); #else fThread = spawn_kernel_thread(&_ThreadEntry, name, B_NORMAL_PRIORITY, this); #endif if (fThread < 0) return fThread; fDone = false; resume_thread(fThread); return B_OK; }
status_t SerialDevice::Open(uint32 flags) { if (fDeviceOpen) return B_BUSY; if (fDeviceRemoved) return B_DEV_NOT_READY; gTTYModule->ttyinit(&fTTY, true); fTTYFile.tty = &fTTY; fTTYFile.flags = flags; ResetDevice(); struct ddrover *ddr = gTTYModule->ddrstart(NULL); if (!ddr) return B_NO_MEMORY; gTTYModule->ddacquire(ddr, &gSerialDomain); status_t status = gTTYModule->ttyopen(&fTTYFile, ddr, usb_serial_service); gTTYModule->ddrdone(ddr); if (status < B_OK) { TRACE_ALWAYS("open: failed to open tty\n"); return status; } fDeviceThread = spawn_kernel_thread(DeviceThread, "usb_serial device thread", B_NORMAL_PRIORITY, this); if (fDeviceThread < B_OK) { TRACE_ALWAYS("open: failed to spawn kernel thread\n"); return fDeviceThread; } resume_thread(fDeviceThread); fControlOut = CLS_LINE_DTR | CLS_LINE_RTS; SetControlLineState(fControlOut); status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer, fInterruptBufferSize, InterruptCallbackFunction, this); if (status < B_OK) TRACE_ALWAYS("failed to queue initial interrupt\n"); fDeviceOpen = true; return B_OK; }
void slab_init_post_thread() { new(&sMaintenanceQueue) MaintenanceQueue; sMaintenanceCondition.Init(&sMaintenanceQueue, "object cache maintainer"); thread_id objectCacheResizer = spawn_kernel_thread(object_cache_maintainer, "object cache resizer", B_URGENT_PRIORITY, NULL); if (objectCacheResizer < 0) { panic("slab_init_post_thread(): failed to spawn object cache resizer " "thread\n"); return; } resume_thread(objectCacheResizer); }
status_t Worker::Init() { if (fThreadCount > 0) { fThreads = new(std::nothrow) thread_id[fThreadCount]; if (fThreads == NULL) return B_NO_MEMORY; for (int32 i = 0; i < fThreadCount; i++) { fThreads[i] = spawn_kernel_thread(&_Worker, "worker", B_NORMAL_PRIORITY, this); resume_thread(fThreads[i]); } } return B_OK; }
status_t InitializeConnectionPurgeThread() { port_id fPort = find_port(BLUETOOTH_CONNECTION_SCHED_PORT); if (fPort == B_NAME_NOT_FOUND) { TRACE("%s: Creating connection purge port\n", __func__); fPort = create_port(16, BLUETOOTH_CONNECTION_SCHED_PORT); } // This thread has to catch up connections before first package is sent. sConnectionThread = spawn_kernel_thread(connection_thread, "bluetooth connection purge", B_URGENT_DISPLAY_PRIORITY, NULL); if (sConnectionThread >= B_OK) return resume_thread(sConnectionThread); else return B_ERROR; }
/*! \brief Send the given report message to the given thread. \param thread The report receiver. \param report The report message. \return \c false on error. */ bool KPPPReportManager::SendReport(thread_id thread, const ppp_report_packet *report) { if (!report) return false; if (thread == find_thread(NULL)) { report_sender_info *info = new report_sender_info; info->thread = thread; memcpy(&info->report, report, sizeof(ppp_report_packet)); resume_thread(spawn_kernel_thread(report_sender_thread, "PPP: ReportSender", B_NORMAL_PRIORITY, info)); return true; } send_data_with_timeout(thread, PPP_REPORT_CODE, &report, sizeof(report), PPP_REPORT_TIMEOUT); return true; }
acpi_status acpi_os_execute( u32 priority, acpi_osd_exec_callback function, void *context) { acpi_status status = AE_OK; struct acpi_os_dpc *dpc; thread_id id; ACPI_FUNCTION_TRACE ("os_queue_for_execution"); ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); if (!function) return_ACPI_STATUS (AE_BAD_PARAMETER); /* * Allocate/initialize DPC structure. Note that this memory will be * freed by the callee. The kernel handles the tq_struct list in a * way that allows us to also free its memory inside the callee. * Because we may want to schedule several tasks with different * parameters we can't use the approach some kernel code uses of * having a static tq_struct. * We can save time and code by allocating the DPC and tq_structs * from the same memory. */ dpc = kmalloc(sizeof(struct acpi_os_dpc), MEMF_KERNEL | MEMF_CLEAR ); if (!dpc) return_ACPI_STATUS (AE_NO_MEMORY); dpc->function = function; dpc->context = context; id = spawn_kernel_thread( "acpi_task", acpi_os_execute_deferred, 0, 4096, dpc ); wakeup_thread( id, false ); return_ACPI_STATUS ( status ); }
status_t initialize_timer(void) { sTimerCount = 0; sTimerNextId = 1; B_INITIALIZE_SPINLOCK(&sTimerSpinlock); sTimerThread = spawn_kernel_thread(timer_thread, "firewire timer", 80, 0); sTimerSem = create_sem(0, "firewire timer"); set_sem_owner(sTimerSem, B_SYSTEM_TEAM); if (sTimerSem < 0 || sTimerThread < 0) { delete_sem(sTimerSem); kill_thread(sTimerThread); return B_ERROR; } resume_thread(sTimerThread); return B_OK; }
status_t DPCQueue::Init(const char* name, int32 priority, uint32 reservedSlots) { // create function callbacks for (uint32 i = 0; i < reservedSlots; i++) { FunctionDPCCallback* callback = new(std::nothrow) FunctionDPCCallback(this); if (callback == NULL) return B_NO_MEMORY; fUnusedFunctionCallbacks.Add(callback); } // spawn the thread fThreadID = spawn_kernel_thread(&_ThreadEntry, name, priority, this); if (fThreadID < 0) return fThreadID; resume_thread(fThreadID); return B_OK; }
WorkQueue::WorkQueue() : fQueueSemaphore(create_sem(0, NULL)), fThreadCancel(create_sem(0, NULL)) { mutex_init(&fQueueLock, NULL); fThread = spawn_kernel_thread(&WorkQueue::LaunchWorkingThread, "NFSv4 Work Queue", B_NORMAL_PRIORITY, this); if (fThread < B_OK) { fInitError = fThread; return; } status_t result = resume_thread(fThread); if (result != B_OK) { kill_thread(fThread); fInitError = result; return; } fInitError = B_OK; }
static status_t std_ops(int32 op, ...) { if (op == B_MODULE_INIT) { sRequestSem = create_sem(0, "invalidate_loop_request"); if (sRequestSem < B_OK) return sRequestSem; thread_id thread = spawn_kernel_thread(&invalidate_loop, "invalidate_loop", B_NORMAL_PRIORITY, NULL); if (thread < B_OK) return thread; resume_thread(thread); return B_OK; } else if (op == B_MODULE_UNINIT) { // deleting the sem will also cause the thread to exit delete_sem(sRequestSem); sRequestSem = -1; return B_OK; } return B_BAD_VALUE; }
// MountRootVolume status_t VolumeManager::MountRootVolume(const char* device, const char* parameters, int32 len, Volume** volume) { // Store the uid/gid of the mounting user -- when running in userland // this is always the owner of the UserlandFS server, but we can't help // that. fMountUID = geteuid(); fMountGID = getegid(); // create the query manager fQueryManager = new(std::nothrow) QueryManager(this); if (!fQueryManager) return B_NO_MEMORY; status_t error = fQueryManager->Init(); if (error != B_OK) return error; // create volumes set fVolumes = new(std::nothrow) VolumeSet; if (!fVolumes) return B_NO_MEMORY; error = fVolumes->InitCheck(); if (error != B_OK) return error; // create node ID to volumes map fNodeIDs2Volumes = new(std::nothrow) NodeIDVolumeMap; if (!fNodeIDs2Volumes) return B_NO_MEMORY; error = fNodeIDs2Volumes->InitCheck(); if (error != B_OK) return error; // create the volume event queue fVolumeEvents = new VolumeEventQueue; if (!fVolumeEvents) return B_NO_MEMORY; error = fVolumeEvents->InitCheck(); if (error != B_OK) return error; // spawn the event deliverer #if USER fEventDeliverer = spawn_thread(&_EventDelivererEntry, "volume event deliverer", B_NORMAL_PRIORITY, this); #else fEventDeliverer = spawn_kernel_thread(&_EventDelivererEntry, "volume event deliverer", B_NORMAL_PRIORITY, this); #endif if (fEventDeliverer < 0) return fEventDeliverer; // create the root volume RootVolume* rootVolume = new(std::nothrow) RootVolume(this); if (!rootVolume) return B_NO_MEMORY; error = rootVolume->Init(); if (error != B_OK) { delete rootVolume; return error; } fRootID = rootVolume->GetRootID(); // add the root volume error = AddVolume(rootVolume); if (error != B_OK) { delete rootVolume; return error; } // mount the root volume error = rootVolume->Mount(device, fMountFlags, (const char*)parameters, len); if (error != B_OK) { rootVolume->SetUnmounting(true); PutVolume(rootVolume); return error; } rootVolume->AcquireReference(); *volume = rootVolume; // run the event deliverer resume_thread(fEventDeliverer); return B_OK; }
extern "C" int _start(kernel_args *bootKernelArgs, int currentCPU) { if (bootKernelArgs->kernel_args_size != sizeof(kernel_args) || bootKernelArgs->version != CURRENT_KERNEL_ARGS_VERSION) { // This is something we cannot handle right now - release kernels // should always be able to handle the kernel_args of earlier // released kernels. debug_early_boot_message("Version mismatch between boot loader and " "kernel!\n"); return -1; } smp_set_num_cpus(bootKernelArgs->num_cpus); // wait for all the cpus to get here smp_cpu_rendezvous(&sCpuRendezvous, currentCPU); // the passed in kernel args are in a non-allocated range of memory if (currentCPU == 0) memcpy(&sKernelArgs, bootKernelArgs, sizeof(kernel_args)); smp_cpu_rendezvous(&sCpuRendezvous2, currentCPU); // do any pre-booting cpu config cpu_preboot_init_percpu(&sKernelArgs, currentCPU); thread_preboot_init_percpu(&sKernelArgs, currentCPU); // if we're not a boot cpu, spin here until someone wakes us up if (smp_trap_non_boot_cpus(currentCPU, &sCpuRendezvous3)) { // init platform arch_platform_init(&sKernelArgs); // setup debug output debug_init(&sKernelArgs); set_dprintf_enabled(true); dprintf("Welcome to kernel debugger output!\n"); dprintf("Haiku revision: %lu\n", get_haiku_revision()); // init modules TRACE("init CPU\n"); cpu_init(&sKernelArgs); cpu_init_percpu(&sKernelArgs, currentCPU); TRACE("init interrupts\n"); int_init(&sKernelArgs); TRACE("init VM\n"); vm_init(&sKernelArgs); // Before vm_init_post_sem() is called, we have to make sure that // the boot loader allocated region is not used anymore boot_item_init(); debug_init_post_vm(&sKernelArgs); low_resource_manager_init(); // now we can use the heap and create areas arch_platform_init_post_vm(&sKernelArgs); lock_debug_init(); TRACE("init driver_settings\n"); driver_settings_init(&sKernelArgs); debug_init_post_settings(&sKernelArgs); TRACE("init notification services\n"); notifications_init(); TRACE("init teams\n"); team_init(&sKernelArgs); TRACE("init ELF loader\n"); elf_init(&sKernelArgs); TRACE("init modules\n"); module_init(&sKernelArgs); TRACE("init semaphores\n"); haiku_sem_init(&sKernelArgs); TRACE("init interrupts post vm\n"); int_init_post_vm(&sKernelArgs); cpu_init_post_vm(&sKernelArgs); commpage_init(); TRACE("init system info\n"); system_info_init(&sKernelArgs); TRACE("init SMP\n"); smp_init(&sKernelArgs); TRACE("init timer\n"); timer_init(&sKernelArgs); TRACE("init real time clock\n"); rtc_init(&sKernelArgs); TRACE("init condition variables\n"); condition_variable_init(); // now we can create and use semaphores TRACE("init VM semaphores\n"); vm_init_post_sem(&sKernelArgs); TRACE("init generic syscall\n"); generic_syscall_init(); smp_init_post_generic_syscalls(); TRACE("init scheduler\n"); scheduler_init(); TRACE("init threads\n"); thread_init(&sKernelArgs); TRACE("init kernel daemons\n"); kernel_daemon_init(); arch_platform_init_post_thread(&sKernelArgs); TRACE("init I/O interrupts\n"); int_init_io(&sKernelArgs); TRACE("init VM threads\n"); vm_init_post_thread(&sKernelArgs); low_resource_manager_init_post_thread(); TRACE("init VFS\n"); vfs_init(&sKernelArgs); #if ENABLE_SWAP_SUPPORT TRACE("init swap support\n"); swap_init(); #endif TRACE("init POSIX semaphores\n"); realtime_sem_init(); xsi_sem_init(); xsi_msg_init(); // Start a thread to finish initializing the rest of the system. Note, // it won't be scheduled before calling scheduler_start() (on any CPU). TRACE("spawning main2 thread\n"); thread_id thread = spawn_kernel_thread(&main2, "main2", B_NORMAL_PRIORITY, NULL); resume_thread(thread); // We're ready to start the scheduler and enable interrupts on all CPUs. scheduler_enable_scheduling(); // bring up the AP cpus in a lock step fashion TRACE("waking up AP cpus\n"); sCpuRendezvous = sCpuRendezvous2 = 0; smp_wake_up_non_boot_cpus(); smp_cpu_rendezvous(&sCpuRendezvous, 0); // wait until they're booted // exit the kernel startup phase (mutexes, etc work from now on out) TRACE("exiting kernel startup\n"); gKernelStartup = false; smp_cpu_rendezvous(&sCpuRendezvous2, 0); // release the AP cpus to go enter the scheduler TRACE("starting scheduler on cpu 0 and enabling interrupts\n"); scheduler_start(); enable_interrupts(); } else { // lets make sure we're in sync with the main cpu // the boot processor has probably been sending us // tlb sync messages all along the way, but we've // been ignoring them arch_cpu_global_TLB_invalidate(); // this is run for each non boot processor after they've been set loose cpu_init_percpu(&sKernelArgs, currentCPU); smp_per_cpu_init(&sKernelArgs, currentCPU); // wait for all other AP cpus to get to this point smp_cpu_rendezvous(&sCpuRendezvous, currentCPU); smp_cpu_rendezvous(&sCpuRendezvous2, currentCPU); // welcome to the machine scheduler_start(); enable_interrupts(); } #ifdef TRACE_BOOT // We disable interrupts for this dprintf(), since otherwise dprintf() // would acquires a mutex, which is something we must not do in an idle // thread, or otherwise the scheduler would be seriously unhappy. disable_interrupts(); TRACE("main: done... begin idle loop on cpu %d\n", currentCPU); enable_interrupts(); #endif for (;;) arch_cpu_idle(); return 0; }
status_t SerialDevice::Open(uint32 flags) { if (fDeviceOpen) return B_BUSY; if (fDeviceRemoved) return B_DEV_NOT_READY; fMasterTTY = gTTYModule->tty_create(usb_serial_service, true); if (fMasterTTY == NULL) { TRACE_ALWAYS("open: failed to init master tty\n"); return B_NO_MEMORY; } fSlaveTTY = gTTYModule->tty_create(usb_serial_service, false); if (fSlaveTTY == NULL) { TRACE_ALWAYS("open: failed to init slave tty\n"); gTTYModule->tty_destroy(fMasterTTY); return B_NO_MEMORY; } fSystemTTYCookie = gTTYModule->tty_create_cookie(fMasterTTY, fSlaveTTY, O_RDWR); if (fSystemTTYCookie == NULL) { TRACE_ALWAYS("open: failed to init system tty cookie\n"); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); return B_NO_MEMORY; } fDeviceTTYCookie = gTTYModule->tty_create_cookie(fSlaveTTY, fMasterTTY, O_RDWR); if (fDeviceTTYCookie == NULL) { TRACE_ALWAYS("open: failed to init device tty cookie\n"); gTTYModule->tty_destroy_cookie(fSystemTTYCookie); gTTYModule->tty_destroy(fMasterTTY); gTTYModule->tty_destroy(fSlaveTTY); return B_NO_MEMORY; } ResetDevice(); fStopThreads = false; fInputThread = spawn_kernel_thread(_InputThread, "usb_serial input thread", B_NORMAL_PRIORITY, this); if (fInputThread < 0) { TRACE_ALWAYS("open: failed to spawn input thread\n"); return fInputThread; } resume_thread(fInputThread); fControlOut = USB_CDC_CONTROL_SIGNAL_STATE_DTR | USB_CDC_CONTROL_SIGNAL_STATE_RTS; SetControlLineState(fControlOut); status_t status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer, fInterruptBufferSize, _InterruptCallbackFunction, this); if (status < B_OK) TRACE_ALWAYS("failed to queue initial interrupt\n"); // set our config (will propagate to the slave config as well in SetModes() gTTYModule->tty_control(fSystemTTYCookie, TCSETA, &fTTYConfig, sizeof(termios)); fDeviceOpen = true; return B_OK; }
Stack::Stack() : fExploreThread(-1), fFirstExploreDone(false), fStopThreads(false), fObjectIndex(1), fObjectMaxCount(1024), fObjectArray(NULL), fDriverList(NULL) { TRACE("stack init\n"); mutex_init(&fStackLock, "usb stack lock"); mutex_init(&fExploreLock, "usb explore lock"); size_t objectArraySize = fObjectMaxCount * sizeof(Object *); fObjectArray = (Object **)malloc(objectArraySize); if (fObjectArray == NULL) { TRACE_ERROR("failed to allocate object array\n"); return; } memset(fObjectArray, 0, objectArraySize); fAllocator = new(std::nothrow) PhysicalMemoryAllocator("USB Stack Allocator", 8, B_PAGE_SIZE * 4, 64); if (!fAllocator || fAllocator->InitCheck() < B_OK) { TRACE_ERROR("failed to allocate the allocator\n"); delete fAllocator; fAllocator = NULL; return; } // Check for host controller modules // While using a fixed list of names is inflexible it allows us to control // the order in which we try modules. There are controllers/BIOSes that // require UHCI/OHCI to be initialized before EHCI or otherwise they // refuse to publish any high-speed devices. // On other systems the ordering is probably ensured because the EHCI // controller is required to have a higher PCI function number than the // companion host controllers (per the EHCI specs) and it would therefore // be enumerated as the last item. As this does not apply to us we have to // ensure ordering using another method. const char *moduleNames[] = { "busses/usb/uhci", "busses/usb/ohci", "busses/usb/ehci", NULL }; TRACE("looking for host controller modules\n"); for (uint32 i = 0; moduleNames[i]; i++) { TRACE("looking for module %s\n", moduleNames[i]); usb_host_controller_info *module = NULL; if (get_module(moduleNames[i], (module_info **)&module) != B_OK) continue; TRACE("adding module %s\n", moduleNames[i]); if (module->add_to(this) < B_OK) { put_module(moduleNames[i]); continue; } TRACE("module %s successfully loaded\n", moduleNames[i]); } if (fBusManagers.Count() == 0) { TRACE_ERROR("no bus managers available\n"); return; } fExploreThread = spawn_kernel_thread(ExploreThread, "usb explore", B_LOW_PRIORITY, this); resume_thread(fExploreThread); // wait for the first explore to complete. this ensures that a driver that // is opening the module does not get rescanned while or before installing // its hooks. while (!fFirstExploreDone) snooze(100000); }
OHCI::OHCI(pci_info *info, Stack *stack) : BusManager(stack), fPCIInfo(info), fStack(stack), fOperationalRegisters(NULL), fRegisterArea(-1), fHccaArea(-1), fHcca(NULL), fInterruptEndpoints(NULL), fDummyControl(NULL), fDummyBulk(NULL), fDummyIsochronous(NULL), fFirstTransfer(NULL), fLastTransfer(NULL), fFinishTransfersSem(-1), fFinishThread(-1), fStopFinishThread(false), fProcessingPipe(NULL), fRootHub(NULL), fRootHubAddress(0), fPortCount(0) { if (!fInitOK) { TRACE_ERROR("bus manager failed to init\n"); return; } TRACE("constructing new OHCI host controller driver\n"); fInitOK = false; mutex_init(&fEndpointLock, "ohci endpoint lock"); // enable busmaster and memory mapped access uint16 command = sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device, fPCIInfo->function, PCI_command, 2); command &= ~PCI_command_io; command |= PCI_command_master | PCI_command_memory; sPCIModule->write_pci_config(fPCIInfo->bus, fPCIInfo->device, fPCIInfo->function, PCI_command, 2, command); // map the registers uint32 offset = sPCIModule->read_pci_config(fPCIInfo->bus, fPCIInfo->device, fPCIInfo->function, PCI_base_registers, 4); offset &= PCI_address_memory_32_mask; TRACE_ALWAYS("iospace offset: 0x%lx\n", offset); fRegisterArea = map_physical_memory("OHCI memory mapped registers", (void *)offset, B_PAGE_SIZE, B_ANY_KERNEL_BLOCK_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_READ_AREA | B_WRITE_AREA, (void **)&fOperationalRegisters); if (fRegisterArea < B_OK) { TRACE_ERROR("failed to map register memory\n"); return; } TRACE("mapped operational registers: %p\n", fOperationalRegisters); // Check the revision of the controller, which should be 10h uint32 revision = _ReadReg(OHCI_REVISION) & 0xff; TRACE("version %ld.%ld%s\n", OHCI_REVISION_HIGH(revision), OHCI_REVISION_LOW(revision), OHCI_REVISION_LEGACY(revision) ? ", legacy support" : ""); if (OHCI_REVISION_HIGH(revision) != 1 || OHCI_REVISION_LOW(revision) != 0) { TRACE_ERROR("unsupported OHCI revision\n"); return; } void *hccaPhysicalAddress; fHccaArea = fStack->AllocateArea((void **)&fHcca, &hccaPhysicalAddress, sizeof(ohci_hcca), "USB OHCI Host Controller Communication Area"); if (fHccaArea < B_OK) { TRACE_ERROR("unable to create the HCCA block area\n"); return; } memset(fHcca, 0, sizeof(ohci_hcca)); // Set Up Host controller // Dummy endpoints fDummyControl = _AllocateEndpoint(); if (!fDummyControl) return; fDummyBulk = _AllocateEndpoint(); if (!fDummyBulk) { _FreeEndpoint(fDummyControl); return; } fDummyIsochronous = _AllocateEndpoint(); if (!fDummyIsochronous) { _FreeEndpoint(fDummyControl); _FreeEndpoint(fDummyBulk); return; } // Static endpoints that get linked in the HCCA fInterruptEndpoints = new(std::nothrow) ohci_endpoint_descriptor *[OHCI_STATIC_ENDPOINT_COUNT]; if (!fInterruptEndpoints) { TRACE_ERROR("failed to allocate memory for interrupt endpoints\n"); _FreeEndpoint(fDummyControl); _FreeEndpoint(fDummyBulk); _FreeEndpoint(fDummyIsochronous); return; } for (int32 i = 0; i < OHCI_STATIC_ENDPOINT_COUNT; i++) { fInterruptEndpoints[i] = _AllocateEndpoint(); if (!fInterruptEndpoints[i]) { TRACE_ERROR("failed to allocate interrupt endpoint %ld", i); while (--i >= 0) _FreeEndpoint(fInterruptEndpoints[i]); _FreeEndpoint(fDummyBulk); _FreeEndpoint(fDummyControl); _FreeEndpoint(fDummyIsochronous); return; } } // build flat tree so that at each of the static interrupt endpoints // fInterruptEndpoints[i] == interrupt endpoint for interval 2^i uint32 interval = OHCI_BIGGEST_INTERVAL; uint32 intervalIndex = OHCI_STATIC_ENDPOINT_COUNT - 1; while (interval > 1) { uint32 insertIndex = interval / 2; while (insertIndex < OHCI_BIGGEST_INTERVAL) { fHcca->interrupt_table[insertIndex] = fInterruptEndpoints[intervalIndex]->physical_address; insertIndex += interval; } intervalIndex--; interval /= 2; } // setup the empty slot in the list and linking of all -> first fHcca->interrupt_table[0] = fInterruptEndpoints[0]->physical_address; for (int32 i = 1; i < OHCI_STATIC_ENDPOINT_COUNT; i++) { fInterruptEndpoints[i]->next_physical_endpoint = fInterruptEndpoints[0]->physical_address; fInterruptEndpoints[i]->next_logical_endpoint = fInterruptEndpoints[0]; } // Now link the first endpoint to the isochronous endpoint fInterruptEndpoints[0]->next_physical_endpoint = fDummyIsochronous->physical_address; // Determine in what context we are running (Kindly copied from FreeBSD) uint32 control = _ReadReg(OHCI_CONTROL); if (control & OHCI_INTERRUPT_ROUTING) { TRACE_ALWAYS("smm is in control of the host controller\n"); uint32 status = _ReadReg(OHCI_COMMAND_STATUS); _WriteReg(OHCI_COMMAND_STATUS, status | OHCI_OWNERSHIP_CHANGE_REQUEST); for (uint32 i = 0; i < 100 && (control & OHCI_INTERRUPT_ROUTING); i++) { snooze(1000); control = _ReadReg(OHCI_CONTROL); } if ((control & OHCI_INTERRUPT_ROUTING) != 0) { TRACE_ERROR("smm does not respond. resetting...\n"); _WriteReg(OHCI_CONTROL, OHCI_HC_FUNCTIONAL_STATE_RESET); snooze(USB_DELAY_BUS_RESET); } else TRACE_ALWAYS("ownership change successful\n"); } else { TRACE("cold started\n"); snooze(USB_DELAY_BUS_RESET); } // This reset should not be necessary according to the OHCI spec, but // without it some controllers do not start. _WriteReg(OHCI_CONTROL, OHCI_HC_FUNCTIONAL_STATE_RESET); snooze(USB_DELAY_BUS_RESET); // We now own the host controller and the bus has been reset uint32 frameInterval = _ReadReg(OHCI_FRAME_INTERVAL); uint32 intervalValue = OHCI_GET_INTERVAL_VALUE(frameInterval); // Disable interrupts right before we reset _WriteReg(OHCI_COMMAND_STATUS, OHCI_HOST_CONTROLLER_RESET); // Nominal time for a reset is 10 us uint32 reset = 0; for (uint32 i = 0; i < 10; i++) { spin(10); reset = _ReadReg(OHCI_COMMAND_STATUS) & OHCI_HOST_CONTROLLER_RESET; if (reset == 0) break; } if (reset) { TRACE_ERROR("error resetting the host controller (timeout)\n"); return; } // The controller is now in SUSPEND state, we have 2ms to go OPERATIONAL. // Interrupts are disabled. // Set up host controller register _WriteReg(OHCI_HCCA, (uint32)hccaPhysicalAddress); _WriteReg(OHCI_CONTROL_HEAD_ED, (uint32)fDummyControl->physical_address); _WriteReg(OHCI_BULK_HEAD_ED, (uint32)fDummyBulk->physical_address); // Disable all interrupts _WriteReg(OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTERRUPTS); // Switch on desired functional features control = _ReadReg(OHCI_CONTROL); control &= ~(OHCI_CONTROL_BULK_SERVICE_RATIO_MASK | OHCI_ENABLE_LIST | OHCI_HC_FUNCTIONAL_STATE_MASK | OHCI_INTERRUPT_ROUTING); control |= OHCI_ENABLE_LIST | OHCI_CONTROL_BULK_RATIO_1_4 | OHCI_HC_FUNCTIONAL_STATE_OPERATIONAL; // And finally start the controller _WriteReg(OHCI_CONTROL, control); // The controller is now OPERATIONAL. frameInterval = (_ReadReg(OHCI_FRAME_INTERVAL) & OHCI_FRAME_INTERVAL_TOGGLE) ^ OHCI_FRAME_INTERVAL_TOGGLE; frameInterval |= OHCI_FSMPS(intervalValue) | intervalValue; _WriteReg(OHCI_FRAME_INTERVAL, frameInterval); // 90% periodic uint32 periodic = OHCI_PERIODIC(intervalValue); _WriteReg(OHCI_PERIODIC_START, periodic); // Fiddle the No Over Current Protection bit to avoid chip bug uint32 desca = _ReadReg(OHCI_RH_DESCRIPTOR_A); _WriteReg(OHCI_RH_DESCRIPTOR_A, desca | OHCI_RH_NO_OVER_CURRENT_PROTECTION); _WriteReg(OHCI_RH_STATUS, OHCI_RH_LOCAL_POWER_STATUS_CHANGE); snooze(OHCI_ENABLE_POWER_DELAY); _WriteReg(OHCI_RH_DESCRIPTOR_A, desca); // The AMD756 requires a delay before re-reading the register, // otherwise it will occasionally report 0 ports. uint32 numberOfPorts = 0; for (uint32 i = 0; i < 10 && numberOfPorts == 0; i++) { snooze(OHCI_READ_DESC_DELAY); uint32 descriptor = _ReadReg(OHCI_RH_DESCRIPTOR_A); numberOfPorts = OHCI_RH_GET_PORT_COUNT(descriptor); } if (numberOfPorts > OHCI_MAX_PORT_COUNT) numberOfPorts = OHCI_MAX_PORT_COUNT; fPortCount = numberOfPorts; TRACE("port count is %d\n", fPortCount); // Create semaphore the finisher thread will wait for fFinishTransfersSem = create_sem(0, "OHCI Finish Transfers"); if (fFinishTransfersSem < B_OK) { TRACE_ERROR("failed to create semaphore\n"); return; } // Create the finisher service thread fFinishThread = spawn_kernel_thread(_FinishThread, "ohci finish thread", B_URGENT_DISPLAY_PRIORITY, (void *)this); resume_thread(fFinishThread); // Install the interrupt handler TRACE("installing interrupt handler\n"); install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line, _InterruptHandler, (void *)this, 0); // Enable interesting interrupts now that the handler is in place _WriteReg(OHCI_INTERRUPT_ENABLE, OHCI_NORMAL_INTERRUPTS | OHCI_MASTER_INTERRUPT_ENABLE); TRACE("OHCI host controller driver constructed\n"); fInitOK = true; }