#include "watchos.h" #include "Event.h" #include "EventListener.h" #include "IRunnable.h" struct KernelHandle { kernel_handle_t handle; kernel_handle_destructor_t destructor; void* object; uint references; uint32_t well_known = 0; }; struct KernelTask { kernel_handle_t handle; Task* task; }; struct KernelEventSubscription { kernel_handle_t handle; well_known_handle_t source_mask; uint16_t event_mask; EventListener* listener; }; Kernel* g_kernel; void destruct_task(kernel_handle_t handle, void* object) { Task* task = static_cast(object); g_kernel->unregisterTask(task); delete static_cast(task); } void destruct_event(kernel_handle_t, void* object) { delete static_cast(object); } void destruct_event_subscription(kernel_handle_t, void* object) { KernelEventSubscription* sub = static_cast(object); g_kernel->unsubscribeEvent(sub); delete static_cast(sub); } Kernel::Kernel() { g_kernel = this; for (int i = 0; i < KERNEL_MAX_HANDLES; i++) { m_handle[i] = nullptr; } for (int i = 0; i < KERNEL_MAX_TASKS; i++) { m_task[i] = nullptr; } for (int i = 0; i < KERNEL_MAX_EVENT_SUBSCRIPTIONS; i++) { m_event_subscription[i] = nullptr; } } kernel_handle_t Kernel::allocHandle(byte type, void* object, kernel_handle_destructor_t destructor, uint32_t well_known) { for (int i = 0; i < KERNEL_MAX_HANDLES; i++) { if (m_handle[i] == nullptr) { m_handle[i] = new KernelHandle(); m_handle[i]->handle = (type << 24) | m_next_handle++; m_handle[i]->destructor = destructor; m_handle[i]->object = object; m_handle[i]->references = 0; m_handle[i]->well_known = well_known; return m_handle[i]->handle; } } watchos::panic("Exceeded KERNEL_MAX_HANDLES"); } void Kernel::freeHandle(kernel_handle_t handle) { for (int i = 0; i < KERNEL_MAX_HANDLES; i++) { if (m_handle[i] != nullptr && m_handle[i]->handle == handle) { if (m_handle[i]->destructor != nullptr) { (m_handle[i]->destructor)(handle, m_handle[i]->object); } delete m_handle[i]; m_handle[i] = nullptr; return; } } } void* Kernel::getHandle(kernel_handle_t handle) { for (int i = 0; i < KERNEL_MAX_HANDLES; i++) { if (m_handle[i] != nullptr && m_handle[i]->handle == handle) { return m_handle[i]->object; } } watchos::panic("Attempt to access invalid Kernel handle"); } kernel_handle_t Kernel::getHandleForWellKnown(well_known_handle_t well_known) { for (int i = 0; i < KERNEL_MAX_HANDLES; i++) { if (m_handle[i] != nullptr && m_handle[i]->well_known == well_known) { return m_handle[i]->handle; } } watchos::panic("Attempt to get handle for invalid well-known id: %#010x", well_known); } void Kernel::useHandle(kernel_handle_t handle) { for (int i = 0; i < KERNEL_MAX_HANDLES; i++) { if (m_handle[i] != nullptr && m_handle[i]->handle == handle) { m_handle[i]->references++; return; } } watchos::panic("Attempt to increase ref count on invalid Kernel handle"); } void Kernel::releaseHandle(kernel_handle_t handle) { for (int i = 0; i < KERNEL_MAX_HANDLES; i++) { if (m_handle[i] != nullptr && m_handle[i]->handle == handle) { m_handle[i]->references--; if (m_handle[i]->references == 0) { freeHandle(handle); } return; } } watchos::panic("Attempt to release invalid Kernel handle"); } kernel_handle_t Kernel::registerTask(Task* task, well_known_handle_t wk_handle) { for (int i = 0; i < KERNEL_MAX_TASKS; i++) { if (m_task[i] == nullptr) { m_task[i] = new KernelTask(); m_task[i]->handle = allocHandle(WATCHOS_HANDLE_TYPE_TASK, task, (kernel_handle_destructor_t)destruct_task, wk_handle); m_task[i]->task = task; useHandle(m_task[i]->handle); return m_task[i]->handle; } } watchos::panic("Exceeded KERNEL_MAX_TASKS"); } void Kernel::unregisterTask(Task* task) { for (int i = 0; i < KERNEL_MAX_TASKS; i++) { if (m_task[i] != nullptr && m_task[i]->task == task) { delete m_task[i]->task; delete m_task[i]; m_task[i] = nullptr; return; } } } void Kernel::initialize() { for (int i = 0; i < KERNEL_MAX_TASKS; i++) { if (m_task[i] != nullptr) { m_task[i]->task->initialize(); } } } void Kernel::start() { for (int i = 0; i < KERNEL_MAX_TASKS; i++) { if (m_task[i] != nullptr) { m_task[i]->task->start(); } } } void Kernel::suspend() { for (int i = 0; i < KERNEL_MAX_TASKS; i++) { if (m_task[i] != nullptr) { m_task[i]->task->suspend(); } } } void Kernel::pushEvent(well_known_handle_t source, uint16_t event, byte param1, byte param2) { Event* e = new Event(); e->source = source; e->event = event; e->param1 = param1; e->param2 = param2; kernel_handle_t handle = allocHandle(WATCHOS_HANDLE_TYPE_EVENT, e, (kernel_handle_destructor_t)destruct_event); useHandle(handle); for (int i = 0; i < KERNEL_MAX_EVENT_SUBSCRIPTIONS; i++) { if (m_event_subscription[i] != nullptr && (m_event_subscription[i]->source_mask & source) == source && (m_event_subscription[i]->event_mask & event) == event) { m_event_subscription[i]->listener->pushEvent(handle); } } // Now release our reference to it, if we've passed it to anyone they have open references. // If nothing matched, that was the last reference and we'll just clean it up right away. releaseHandle(handle); } kernel_handle_t Kernel::subscribeEvent(EventListener* listener, well_known_handle_t source_mask, uint16_t event_mask) { for (int i = 0; i < KERNEL_MAX_EVENT_SUBSCRIPTIONS; i++) { if (m_event_subscription[i] == nullptr) { m_event_subscription[i] = new KernelEventSubscription(); m_event_subscription[i]->source_mask = source_mask; m_event_subscription[i]->event_mask = event_mask; m_event_subscription[i]->listener = listener; m_event_subscription[i]->handle = allocHandle(WATCHOS_HANDLE_TYPE_EVENT_SUBSCRIPTION, m_event_subscription[i], (kernel_handle_destructor_t)destruct_event_subscription); useHandle(m_event_subscription[i]->handle); return m_event_subscription[i]->handle; } } watchos::panic("Exceeded KERNEL_MAX_EVENT_SUBSCRIPTIONS"); } void Kernel::unsubscribeEvent(KernelEventSubscription* subscription) { for (int i = 0; i < KERNEL_MAX_EVENT_SUBSCRIPTIONS; i++) { if (m_event_subscription[i] != nullptr && m_event_subscription[i] == subscription) { m_event_subscription[i] = nullptr; return; } } } void Kernel::tick() { for (int i = 0; i < KERNEL_MAX_TASKS; i++) { if (m_task[i] != nullptr) { IRunnable* runnable = dynamic_cast(m_task[i]->task); if (runnable != nullptr) { runnable->tick(); } } } }