i2c: bus manager

Change-Id: Ic2a7d3a4949a06a6e5f35dbb0b43d8239f92a370
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2457
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
Reviewed-by: Rene Gollent <rene@gollent.com>
This commit is contained in:
Jérôme Duval 2020-04-04 12:08:21 +02:00
parent b9d640760e
commit f6f8cb83e4
7 changed files with 682 additions and 1 deletions

View File

@ -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 ;

View File

@ -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
};

View File

@ -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,
};

View File

@ -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
};

View File

@ -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 <new>
#include <stdio.h>
#include <string.h>
#include <lock.h>
#include <util/AutoLock.h>
#include <i2c.h>
//#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

View File

@ -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
;

View File

@ -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
};