/* * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. * Copyright 2008-2011 Michael Lotz * Distributed under the terms of the MIT license. */ //! Driver for I2C Human Interface Devices. #include #include #include #include "DeviceList.h" #include "Driver.h" #include "HIDDevice.h" #include "ProtocolHandler.h" #include #include #include #include #include struct hid_driver_cookie { device_node* node; i2c_device_interface* i2c; i2c_device i2c_cookie; uint32 descriptorAddress; HIDDevice* hidDevice; }; struct device_cookie { ProtocolHandler* handler; uint32 cookie; hid_driver_cookie* driver_cookie; }; #define I2C_HID_DRIVER_NAME "drivers/input/i2c_hid/driver_v1" #define I2C_HID_DEVICE_NAME "drivers/input/i2c_hid/device_v1" /* Base Namespace devices are published to */ #define I2C_HID_BASENAME "input/i2c_hid/%d" // name of pnp generator of path ids #define I2C_HID_PATHID_GENERATOR "i2c_hid/path_id" #define ACPI_NAME_HID_DEVICE "PNP0C50" static device_manager_info *sDeviceManager; static acpi_module_info* gACPI; DeviceList *gDeviceList = NULL; static mutex sDriverLock; static acpi_object_type* acpi_evaluate_dsm(acpi_handle handle, const uint8 *guid, uint64 revision, uint64 function) { acpi_data buffer; buffer.pointer = NULL; buffer.length = ACPI_ALLOCATE_BUFFER; acpi_object_type array[4]; acpi_objects acpi_objects; acpi_objects.count = 4; acpi_objects.pointer = array; array[0].object_type = ACPI_TYPE_BUFFER; array[0].buffer.buffer = (void*)guid; array[0].buffer.length = 16; array[1].object_type = ACPI_TYPE_INTEGER; array[1].integer.integer = revision; array[2].object_type = ACPI_TYPE_INTEGER; array[2].integer.integer = function; array[3].object_type = ACPI_TYPE_PACKAGE; array[3].package.objects = NULL; array[3].package.count = 0; if (gACPI->evaluate_method(handle, "_DSM", &acpi_objects, &buffer) == B_OK) return (acpi_object_type*)buffer.pointer; return NULL; } // #pragma mark - notify hooks /* status_t i2c_hid_device_removed(void *cookie) { mutex_lock(&sDriverLock); int32 parentCookie = (int32)(addr_t)cookie; TRACE("device_removed(%" B_PRId32 ")\n", parentCookie); for (int32 i = 0; i < gDeviceList->CountDevices(); i++) { ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->DeviceAt(i); if (!handler) continue; HIDDevice *device = handler->Device(); if (device->ParentCookie() != parentCookie) continue; // remove all the handlers for (uint32 i = 0;; i++) { handler = device->ProtocolHandlerAt(i); if (handler == NULL) break; gDeviceList->RemoveDevice(NULL, handler); } // this handler's device belongs to the one removed if (device->IsOpen()) { // the device and it's handlers will be deleted in the free hook device->Removed(); } else delete device; break; } mutex_unlock(&sDriverLock); return B_OK; }*/ // #pragma mark - driver hooks static status_t i2c_hid_init_device(void *driverCookie, void **cookie) { *cookie = driverCookie; return B_OK; } static void i2c_hid_uninit_device(void *_cookie) { } static status_t i2c_hid_open(void *initCookie, const char *path, int flags, void **_cookie) { TRACE("open(%s, %" B_PRIu32 ", %p)\n", path, flags, _cookie); device_cookie *cookie = new(std::nothrow) device_cookie(); if (cookie == NULL) return B_NO_MEMORY; cookie->driver_cookie = (hid_driver_cookie*)initCookie; MutexLocker locker(sDriverLock); ProtocolHandler *handler = (ProtocolHandler *)gDeviceList->FindDevice(path); TRACE(" path %s: handler %p\n", path, handler); cookie->handler = handler; cookie->cookie = 0; status_t result = handler == NULL ? B_ENTRY_NOT_FOUND : B_OK; if (result == B_OK) result = handler->Open(flags, &cookie->cookie); if (result != B_OK) { delete cookie; return result; } *_cookie = cookie; return B_OK; } static status_t i2c_hid_read(void *_cookie, off_t position, void *buffer, size_t *numBytes) { device_cookie *cookie = (device_cookie *)_cookie; TRACE("read(%p, %" B_PRIu64 ", %p, %p (%" B_PRIuSIZE ")\n", cookie, position, buffer, numBytes, numBytes != NULL ? *numBytes : 0); return cookie->handler->Read(&cookie->cookie, position, buffer, numBytes); } static status_t i2c_hid_write(void *_cookie, off_t position, const void *buffer, size_t *numBytes) { device_cookie *cookie = (device_cookie *)_cookie; TRACE("write(%p, %" B_PRIu64 ", %p, %p (%" B_PRIuSIZE ")\n", cookie, position, buffer, numBytes, numBytes != NULL ? *numBytes : 0); return cookie->handler->Write(&cookie->cookie, position, buffer, numBytes); } static status_t i2c_hid_control(void *_cookie, uint32 op, void *buffer, size_t length) { device_cookie *cookie = (device_cookie *)_cookie; TRACE("control(%p, %" B_PRIu32 ", %p, %" B_PRIuSIZE ")\n", cookie, op, buffer, length); return cookie->handler->Control(&cookie->cookie, op, buffer, length); } static status_t i2c_hid_close(void *_cookie) { device_cookie *cookie = (device_cookie *)_cookie; TRACE("close(%p)\n", cookie); return cookie->handler->Close(&cookie->cookie); } static status_t i2c_hid_free(void *_cookie) { device_cookie *cookie = (device_cookie *)_cookie; TRACE("free(%p)\n", cookie); mutex_lock(&sDriverLock); HIDDevice *device = cookie->handler->Device(); if (device->IsOpen()) { // another handler of this device is still open so we can't free it } else if (device->IsRemoved()) { // the parent device is removed already and none of its handlers are // open anymore so we can free it here delete device; } mutex_unlock(&sDriverLock); delete cookie; return B_OK; } // #pragma mark - driver module API static float i2c_hid_support(device_node *parent) { CALLED(); // make sure parent is really the I2C bus manager const char *bus; if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) return -1; if (strcmp(bus, "i2c")) return 0.0; TRACE("i2c_hid_support found an i2c device %p\n", parent); // check whether it's an HID device uint64 handlePointer; if (sDeviceManager->get_attr_uint64(parent, ACPI_DEVICE_HANDLE_ITEM, &handlePointer, false) != B_OK) { TRACE("i2c_hid_support found an i2c device without acpi handle\n"); return B_ERROR; } const char *name; if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name, false) == B_OK && strcmp(name, ACPI_NAME_HID_DEVICE) == 0) { TRACE("i2c_hid_support found an hid i2c device\n"); return 0.6; } if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_CID_ITEM, &name, false) == B_OK && strcmp(name, ACPI_NAME_HID_DEVICE) == 0) { TRACE("i2c_hid_support found a compatible hid i2c device\n"); return 0.6; } uint16 slaveAddress; if (sDeviceManager->get_attr_uint16(parent, I2C_DEVICE_SLAVE_ADDR_ITEM, &slaveAddress, false) != B_OK) { TRACE("i2c_hid_support found a non hid without addr i2c device\n"); return B_ERROR; } TRACE("i2c_hid_support found a non hid i2c device\n"); return 0.0; } static status_t i2c_hid_register_device(device_node *node) { CALLED(); acpi_handle handle; if (sDeviceManager->get_attr_uint64(node, ACPI_DEVICE_HANDLE_ITEM, (uint64*)&handle, false) != B_OK) { return B_DEVICE_NOT_FOUND; } static uint8_t acpiHidGuid[] = { 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45, 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE }; acpi_object_type* object = acpi_evaluate_dsm(handle, acpiHidGuid, 1, 1); if (object == NULL) return B_DEVICE_NOT_FOUND; if (object->object_type != ACPI_TYPE_INTEGER) { free(object); return B_DEVICE_NOT_FOUND; } uint32 descriptorAddress = object->integer.integer; free(object); device_attr attrs[] = { { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "I2C HID Device" }}, { "descriptorAddress", B_UINT32_TYPE, { .ui32 = descriptorAddress }}, { NULL } }; return sDeviceManager->register_node(node, I2C_HID_DRIVER_NAME, attrs, NULL, NULL); } static status_t i2c_hid_init_driver(device_node *node, void **driverCookie) { CALLED(); uint32 descriptorAddress; if (sDeviceManager->get_attr_uint32(node, "descriptorAddress", &descriptorAddress, false) != B_OK) { return B_DEVICE_NOT_FOUND; } hid_driver_cookie *device = (hid_driver_cookie *)calloc(1, sizeof(hid_driver_cookie)); if (device == NULL) return B_NO_MEMORY; *driverCookie = device; device->node = node; device->descriptorAddress = descriptorAddress; device_node *parent; parent = sDeviceManager->get_parent_node(node); sDeviceManager->get_driver(parent, (driver_module_info **)&device->i2c, (void **)&device->i2c_cookie); sDeviceManager->put_node(parent); mutex_lock(&sDriverLock); HIDDevice *hidDevice = new(std::nothrow) HIDDevice(descriptorAddress, device->i2c, device->i2c_cookie); if (hidDevice != NULL && hidDevice->InitCheck() == B_OK) { device->hidDevice = hidDevice; } else delete hidDevice; mutex_unlock(&sDriverLock); return device->hidDevice != NULL ? B_OK : B_IO_ERROR; } static void i2c_hid_uninit_driver(void *driverCookie) { CALLED(); hid_driver_cookie *device = (hid_driver_cookie*)driverCookie; free(device); } static status_t i2c_hid_register_child_devices(void *cookie) { CALLED(); hid_driver_cookie *device = (hid_driver_cookie*)cookie; HIDDevice* hidDevice = device->hidDevice; if (hidDevice == NULL) return B_OK; for (uint32 i = 0;; i++) { ProtocolHandler *handler = hidDevice->ProtocolHandlerAt(i); if (handler == NULL) break; // As devices can be un- and replugged at will, we cannot // simply rely on a device count. If there is just one // keyboard, this does not mean that it uses the 0 name. // There might have been two keyboards and the one using 0 // might have been unplugged. So we just generate names // until we find one that is not currently in use. int32 index = 0; char pathBuffer[128]; const char *basePath = handler->BasePath(); while (true) { sprintf(pathBuffer, "%s%" B_PRId32, basePath, index++); if (gDeviceList->FindDevice(pathBuffer) == NULL) { // this name is still free, use it handler->SetPublishPath(strdup(pathBuffer)); break; } } gDeviceList->AddDevice(handler->PublishPath(), handler); sDeviceManager->publish_device(device->node, pathBuffer, I2C_HID_DEVICE_NAME); } /* int pathID = sDeviceManager->create_id(I2C_HID_PATHID_GENERATOR); if (pathID < 0) { ERROR("register_child_devices: couldn't create a path_id\n"); return B_ERROR; }*/ return B_OK; } static status_t std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: gDeviceList = new(std::nothrow) DeviceList(); if (gDeviceList == NULL) { return B_NO_MEMORY; } mutex_init(&sDriverLock, "i2c hid driver lock"); return B_OK; case B_MODULE_UNINIT: delete gDeviceList; gDeviceList = NULL; mutex_destroy(&sDriverLock); return B_OK; default: break; } return B_ERROR; } // #pragma mark - driver_module_info i2c_hid_driver_module = { { I2C_HID_DRIVER_NAME, 0, &std_ops }, i2c_hid_support, i2c_hid_register_device, i2c_hid_init_driver, i2c_hid_uninit_driver, i2c_hid_register_child_devices, NULL, // rescan NULL, // removed }; struct device_module_info i2c_hid_device_module = { { I2C_HID_DEVICE_NAME, 0, NULL }, i2c_hid_init_device, i2c_hid_uninit_device, NULL, i2c_hid_open, i2c_hid_close, i2c_hid_free, i2c_hid_read, i2c_hid_write, NULL, i2c_hid_control, NULL, NULL }; module_dependency module_dependencies[] = { { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, { B_ACPI_MODULE_NAME, (module_info**)&gACPI }, {} }; module_info *modules[] = { (module_info *)&i2c_hid_driver_module, (module_info *)&i2c_hid_device_module, NULL };