root/src/add-ons/kernel/bus_managers/i2c/I2CDevice.cpp
/*
 * 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);
}


status_t
I2CDevice::AcquireBus()
{
        CALLED();
        return fBus->AcquireBus();
}


void
I2CDevice::ReleaseBus()
{
        CALLED();
        fBus->ReleaseBus();
}


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
i2c_acquire_bus(i2c_device _device)
{
        I2CDevice *device = (I2CDevice *)_device;
        return device->AcquireBus();
}


static void
i2c_release_bus(i2c_device _device)
{
        I2CDevice *device = (I2CDevice *)_device;
        return device->ReleaseBus();
}


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,
        i2c_acquire_bus,
        i2c_release_bus,
};