diff --git a/src/add-ons/kernel/bus_managers/Jamfile b/src/add-ons/kernel/bus_managers/Jamfile index 386b887d09..616b52e161 100644 --- a/src/add-ons/kernel/bus_managers/Jamfile +++ b/src/add-ons/kernel/bus_managers/Jamfile @@ -2,10 +2,11 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers ; SubInclude HAIKU_TOP src add-ons kernel bus_managers acpi ; SubInclude HAIKU_TOP src add-ons kernel bus_managers agp_gart ; +SubInclude HAIKU_TOP src add-ons kernel bus_managers ata ; SubInclude HAIKU_TOP src add-ons kernel bus_managers config_manager ; SubInclude HAIKU_TOP src add-ons kernel bus_managers fdt ; SubInclude HAIKU_TOP src add-ons kernel bus_managers firewire ; -SubInclude HAIKU_TOP src add-ons kernel bus_managers ata ; +SubInclude HAIKU_TOP src add-ons kernel bus_managers i2c ; SubInclude HAIKU_TOP src add-ons kernel bus_managers isa ; SubInclude HAIKU_TOP src add-ons kernel bus_managers mmc ; SubInclude HAIKU_TOP src add-ons kernel bus_managers pci ; diff --git a/src/add-ons/kernel/bus_managers/i2c/I2CBus.cpp b/src/add-ons/kernel/bus_managers/i2c/I2CBus.cpp new file mode 100644 index 0000000000..3940709ee0 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/i2c/I2CBus.cpp @@ -0,0 +1,184 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT License. + */ + + +#include "I2CPrivate.h" + + +I2CBus::I2CBus(device_node *node, uint8 id) + : + fNode(node), + fID(id), + fController(NULL), + fCookie(NULL) +{ + CALLED(); + device_node *parent = gDeviceManager->get_parent_node(node); + status_t status = gDeviceManager->get_driver(parent, + (driver_module_info **)&fController, &fCookie); + gDeviceManager->put_node(parent); + + if (status != B_OK) + return; + + fController->set_i2c_bus(fCookie, this); + +} + + +I2CBus::~I2CBus() +{ +} + + +status_t +I2CBus::InitCheck() +{ + return B_OK; +} + + +status_t +I2CBus::ExecCommand(i2c_op op, i2c_addr slaveAddress, const void *cmdBuffer, + size_t cmdLength, void* dataBuffer, size_t dataLength) +{ + CALLED(); + return fController->exec_command(fCookie, op, slaveAddress, cmdBuffer, cmdLength, + dataBuffer, dataLength); +} + + +status_t +I2CBus::RegisterDevice(i2c_addr slaveAddress, char* hid, char** cid, + acpi_handle acpiHandle) +{ + CALLED(); + + device_attr attrs[] = { + // connection + { I2C_DEVICE_SLAVE_ADDR_ITEM, B_UINT16_TYPE, { ui16: slaveAddress }}, + + // description of peripheral drivers + { B_DEVICE_BUS, B_STRING_TYPE, { string: "i2c" }}, + + { B_DEVICE_FLAGS, B_UINT32_TYPE, { ui32: B_FIND_MULTIPLE_CHILDREN }}, + + { ACPI_DEVICE_HID_ITEM, B_STRING_TYPE, { string: hid }}, + + { ACPI_DEVICE_CID_ITEM, B_STRING_TYPE, { string: cid[0] }}, + + { ACPI_DEVICE_HANDLE_ITEM, B_UINT64_TYPE, { ui64: (addr_t)acpiHandle }}, + + { NULL } + }; + + return gDeviceManager->register_node(fNode, I2C_DEVICE_MODULE_NAME, attrs, + NULL, NULL); +} + + +status_t +I2CBus::Scan() +{ + CALLED(); + + fController->scan_bus(fCookie); + return B_OK; +} + + +static status_t +i2c_init_bus(device_node *node, void **_bus) +{ + CALLED(); + uint8 pathID; + if (gDeviceManager->get_attr_uint8(node, I2C_BUS_PATH_ID_ITEM, &pathID, + false) != B_OK) { + return B_ERROR; + } + + I2CBus *bus = new(std::nothrow) I2CBus(node, pathID); + if (bus == NULL) + return B_NO_MEMORY; + + status_t result = bus->InitCheck(); + if (result != B_OK) { + ERROR("failed to set up i2c bus object\n"); + return result; + } + + *_bus = bus; + + char name[128]; + snprintf(name, sizeof(name), "bus/i2c/%d/bus_raw", pathID); + + return gDeviceManager->publish_device(node, name, I2C_BUS_RAW_MODULE_NAME); +} + + +static void +i2c_uninit_bus(void *_bus) +{ + CALLED(); + I2CBus *bus = (I2CBus *)_bus; + delete bus; +} + + +status_t +i2c_scan_bus(void *_bus) +{ + I2CBus *bus = (I2CBus *)_bus; + return bus->Scan(); +} + + +status_t +i2c_bus_exec_command(void* _bus, i2c_op op, i2c_addr slaveAddress, + const void *cmdBuffer, size_t cmdLength, void* dataBuffer, + size_t dataLength) +{ + CALLED(); + I2CBus* bus = (I2CBus*)_bus; + return bus->ExecCommand(op, slaveAddress, cmdBuffer, cmdLength, + dataBuffer, dataLength); +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + + default: + break; + } + + return B_ERROR; +} + + +i2c_bus_interface gI2CBusModule = { + { + { + I2C_BUS_MODULE_NAME, + 0, + std_ops + }, + + NULL, // supported devices + NULL, // register node + i2c_init_bus, + i2c_uninit_bus, + i2c_scan_bus, // register child devices + NULL, // rescan + }, + + i2c_bus_exec_command +}; + diff --git a/src/add-ons/kernel/bus_managers/i2c/I2CDevice.cpp b/src/add-ons/kernel/bus_managers/i2c/I2CDevice.cpp new file mode 100644 index 0000000000..ba398fdba5 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/i2c/I2CDevice.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT License. + */ + + +#include "I2CPrivate.h" + + +I2CDevice::I2CDevice(device_node *node, I2CBus* bus, i2c_addr slaveAddress) + : + fNode(node), + fBus(bus), + fSlaveAddress(slaveAddress) +{ + CALLED(); +} + + +I2CDevice::~I2CDevice() +{ +} + + +status_t +I2CDevice::InitCheck() +{ + return B_OK; +} + + +status_t +I2CDevice::ExecCommand(i2c_op op, const void *cmdBuffer, + size_t cmdLength, void* dataBuffer, size_t dataLength) +{ + CALLED(); + return fBus->ExecCommand(op, fSlaveAddress, cmdBuffer, cmdLength, + dataBuffer, dataLength); +} + + + +static status_t +i2c_init_device(device_node *node, void **_device) +{ + CALLED(); + I2CBus* bus; + + { + device_node *parent = gDeviceManager->get_parent_node(node); + gDeviceManager->get_driver(parent, NULL, (void **)&bus); + gDeviceManager->put_node(parent); + } + + uint16 slaveAddress; + if (gDeviceManager->get_attr_uint16(node, I2C_DEVICE_SLAVE_ADDR_ITEM, + &slaveAddress, false) != B_OK) { + return B_ERROR; + } + + + I2CDevice *device = new(std::nothrow) I2CDevice(node, bus, slaveAddress); + if (device == NULL) + return B_NO_MEMORY; + + status_t result = device->InitCheck(); + if (result != B_OK) { + ERROR("failed to set up i2c device object\n"); + return result; + } + + *_device = device; + + return B_OK; +} + + +static void +i2c_uninit_device(void *_device) +{ + CALLED(); + I2CDevice *device = (I2CDevice *)_device; + delete device; +} + + +static void +i2c_device_removed(void *_device) +{ + CALLED(); + //I2CDevice *device = (I2CDevice *)_device; +} + + +static status_t +i2c_exec_command(i2c_device _device, i2c_op op, const void *cmdBuffer, + size_t cmdLength, void* dataBuffer, size_t dataLength) +{ + I2CDevice *device = (I2CDevice *)_device; + return device->ExecCommand(op, cmdBuffer, cmdLength, dataBuffer, dataLength); +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + { + // Link to I2C bus. + // I2C device driver must have I2C bus loaded, but it calls its + // functions directly instead via official interface, so this + // pointer is never read. + module_info *dummy; + return get_module(I2C_BUS_MODULE_NAME, &dummy); + } + case B_MODULE_UNINIT: + return put_module(I2C_BUS_MODULE_NAME); + + default: + return B_ERROR; + } +} + + +i2c_device_interface gI2CDeviceModule = { + { + { + I2C_DEVICE_MODULE_NAME, + 0, + std_ops + }, + + NULL, // supported devices + NULL, // register node + i2c_init_device, + (void (*)(void *)) i2c_uninit_device, + NULL, // register child devices + NULL, // rescan + (void (*)(void *)) i2c_device_removed + }, + i2c_exec_command, +}; diff --git a/src/add-ons/kernel/bus_managers/i2c/I2CModule.cpp b/src/add-ons/kernel/bus_managers/i2c/I2CModule.cpp new file mode 100644 index 0000000000..f8ca7c5de4 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/i2c/I2CModule.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT License. + */ + + +#include "I2CPrivate.h" + + +device_manager_info *gDeviceManager = NULL; + + +// #pragma mark - + + +status_t +i2c_added_device(device_node *parent) +{ + CALLED(); + + int32 pathID = gDeviceManager->create_id(I2C_PATHID_GENERATOR); + if (pathID < 0) { + ERROR("cannot register i2c controller - out of path IDs\n"); + return B_ERROR; + } + + device_attr attributes[] = { + // info about device + { B_DEVICE_BUS, B_STRING_TYPE, { string: "i2c" }}, + { I2C_BUS_PATH_ID_ITEM, B_UINT8_TYPE, { ui8: (uint8)pathID }}, + { NULL } + }; + + TRACE("i2c_added_device parent %p\n", parent); + + return gDeviceManager->register_node(parent, I2C_BUS_MODULE_NAME, + attributes, NULL, NULL); +} + + +status_t +i2c_register_device(i2c_bus _bus, i2c_addr slaveAddress, char* hid, + char** cid, acpi_handle acpiHandle) +{ + CALLED(); + I2CBus* bus = (I2CBus*)_bus; + return bus->RegisterDevice(slaveAddress, hid, cid, acpiHandle); +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + case B_MODULE_UNINIT: + return B_OK; + + default: + break; + } + + return B_ERROR; +} + + +// #pragma mark - + + +i2c_for_controller_interface gI2CForControllerModule = { + { + { + I2C_FOR_CONTROLLER_MODULE_NAME, + 0, + &std_ops + }, + + NULL, // supported devices + i2c_added_device, + NULL, + NULL, + NULL + }, + + i2c_register_device, +}; + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager }, + {} +}; + + +module_info *modules[] = { + (module_info *)&gI2CForControllerModule, + (module_info *)&gI2CBusModule, + (module_info *)&gI2CDeviceModule, + (module_info *)&gI2CBusRawModule, + NULL +}; + diff --git a/src/add-ons/kernel/bus_managers/i2c/I2CPrivate.h b/src/add-ons/kernel/bus_managers/i2c/I2CPrivate.h new file mode 100644 index 0000000000..f7fec54f73 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/i2c/I2CPrivate.h @@ -0,0 +1,86 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * Distributed under the terms of the MIT License. + */ +#ifndef I2C_PRIVATE_H +#define I2C_PRIVATE_H + + +#include +#include +#include + +#include +#include +#include + + +//#define I2C_TRACE +#ifdef I2C_TRACE +# define TRACE(x...) dprintf("\33[33mi2c:\33[0m " x) +#else +# define TRACE(x...) +#endif +#define TRACE_ALWAYS(x...) dprintf("\33[33mi2c:\33[0m " x) +#define ERROR(x...) TRACE_ALWAYS(x) +#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +#define I2C_SIM_MODULE_NAME "bus_managers/i2c/sim/driver_v1" + +#define I2C_PATHID_GENERATOR "i2c/path_id" +#define I2C_BUS_RAW_MODULE_NAME "bus_managers/i2c/bus/raw/device_v1" + + +class I2CBus; +class I2CDevice; + +extern device_manager_info *gDeviceManager; +extern struct device_module_info gI2CBusRawModule; +extern i2c_bus_interface gI2CBusModule; +extern i2c_device_interface gI2CDeviceModule; + +class I2CDevice { +public: + I2CDevice(device_node* node, I2CBus* bus, + i2c_addr slaveAddress); + ~I2CDevice(); + + status_t InitCheck(); + + status_t ExecCommand(i2c_op op, const void *cmdBuffer, + size_t cmdLength, void* dataBuffer, + size_t dataLength); + +private: + device_node* fNode; + I2CBus* fBus; + i2c_addr fSlaveAddress; +}; + + +class I2CBus { +public: + I2CBus(device_node *node, uint8 id); + ~I2CBus(); + + status_t InitCheck(); + + status_t ExecCommand(i2c_op op, i2c_addr slaveAddress, + const void *cmdBuffer, size_t cmdLength, + void* dataBuffer, size_t dataLength); + status_t Scan(); + status_t RegisterDevice(i2c_addr slaveAddress, + char* hid, char** cid, + acpi_handle acpiHandle); + +private: + device_node* fNode; + uint8 fID; + i2c_sim_interface* fController; + void* fCookie; +}; + + + +#endif // I2C_PRIVATE_H diff --git a/src/add-ons/kernel/bus_managers/i2c/Jamfile b/src/add-ons/kernel/bus_managers/i2c/Jamfile new file mode 100644 index 0000000000..65983c926c --- /dev/null +++ b/src/add-ons/kernel/bus_managers/i2c/Jamfile @@ -0,0 +1,11 @@ +SubDir HAIKU_TOP src add-ons kernel bus_managers i2c ; + +UsePrivateHeaders i2c ; +UsePrivateKernelHeaders ; + +KernelAddon i2c : + I2CBus.cpp + I2CDevice.cpp + I2CModule.cpp + bus_raw.cpp + ; diff --git a/src/add-ons/kernel/bus_managers/i2c/bus_raw.cpp b/src/add-ons/kernel/bus_managers/i2c/bus_raw.cpp new file mode 100644 index 0000000000..86e68d2a31 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/i2c/bus_raw.cpp @@ -0,0 +1,154 @@ +/* + * Copyright 2020, Jérôme Duval. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "I2CPrivate.h" + + +static status_t +i2c_bus_raw_init(void* driverCookie, void **_cookie) +{ + CALLED(); + I2CBus *bus = (I2CBus*)driverCookie; + TRACE("i2c_bus_raw_init bus %p\n", bus); + + *_cookie = bus; + return B_OK; +} + + +static void +i2c_bus_raw_uninit(void *bus) +{ +} + + +static status_t +i2c_bus_raw_open(void *bus, const char *path, int openMode, + void **handle_cookie) +{ + CALLED(); + *handle_cookie = bus; + TRACE("i2c_bus_raw_open bus %p\n", bus); + + return B_OK; +} + + +static status_t +i2c_bus_raw_close(void *cookie) +{ + return B_OK; +} + + +static status_t +i2c_bus_raw_free(void *cookie) +{ + return B_OK; +} + + +static status_t +i2c_bus_raw_control(void *_cookie, uint32 op, void *data, size_t length) +{ + CALLED(); + I2CBus *bus = (I2CBus*)_cookie; + + TRACE("i2c_bus_raw_control bus %p\n", bus); + + + switch (op) { + case I2CEXEC: + { + i2c_ioctl_exec exec; + uint8 cmdBuffer[32]; + uint8 buffer[32]; + const void* userCmdBuffer = NULL; + void* userBuffer = NULL; + if (user_memcpy(&exec, data, sizeof(i2c_ioctl_exec)) != B_OK) + return B_BAD_ADDRESS; + if (exec.cmdBuffer != NULL) { + if (user_memcpy(cmdBuffer, exec.cmdBuffer, exec.cmdLength) + != B_OK) { + return B_BAD_ADDRESS; + } + userCmdBuffer = exec.cmdBuffer; + exec.cmdBuffer = cmdBuffer; + } + if (exec.buffer != NULL) { + if (user_memcpy(buffer, exec.buffer, exec.bufferLength) + != B_OK) { + return B_BAD_ADDRESS; + } + userBuffer = exec.buffer; + exec.buffer = buffer; + } + + status_t status = bus->ExecCommand(exec.op, exec.addr, + &exec.cmdBuffer, exec.cmdLength, exec.buffer, + exec.bufferLength); + if (status != B_OK) + return status; + + exec.cmdBuffer = userCmdBuffer; + if (exec.buffer != NULL) { + if (user_memcpy(userBuffer, exec.buffer, exec.bufferLength) + != B_OK) { + return B_BAD_ADDRESS; + } + exec.buffer = userBuffer; + } + + if (user_memcpy(data, &exec, sizeof(i2c_ioctl_exec)) != B_OK) + return B_BAD_ADDRESS; + + return B_OK; + } + } + + return B_ERROR; +} + + +static status_t +i2c_bus_raw_read(void *cookie, off_t position, void *data, + size_t *numBytes) +{ + *numBytes = 0; + return B_ERROR; +} + + +static status_t +i2c_bus_raw_write(void *cookie, off_t position, + const void *data, size_t *numBytes) +{ + *numBytes = 0; + return B_ERROR; +} + + +struct device_module_info gI2CBusRawModule = { + { + I2C_BUS_RAW_MODULE_NAME, + 0, + NULL + }, + + i2c_bus_raw_init, + i2c_bus_raw_uninit, + NULL, // removed + + i2c_bus_raw_open, + i2c_bus_raw_close, + i2c_bus_raw_free, + i2c_bus_raw_read, + i2c_bus_raw_write, + NULL, // io + i2c_bus_raw_control, + NULL, // select + NULL // deselect +};