#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include "include/ngkh_i2c.h"
// Macro
#define I2C_MAX_LENGTH_DEVPATH 32U
#define I2C_MAX_NUM_BUSES 3U
// Type
typedef struct {
int fd;
char devicePath[I2C_MAX_LENGTH_DEVPATH];
} ngkh_i2cBus_t;
typedef struct {
ngkh_i2cBus_t bus[I2C_MAX_NUM_BUSES];
size_t numBuses;
} ngkh_i2cBusCluster_t;
// Variable
static ngkh_i2cBusCluster_t ngkh_i2cBusCluster = {0};
// Prototypes
static bool ngkh_i2cSetSlaveAddressOnBus(const ngkh_i2cBus_t *i2cBus, uint8_t devAddr);
static int ngkh_i2cGetBusNumFromPath(const char *i2cBusDeviceFilePath);
static int ngkh_i2cAddBus(const char *i2cBusDeviceFilePath);
// Functions
void ngkh_i2cInitialize(void)
{
ngkh_i2cBus_t *i2cBus = NULL;
for (size_t i = 0; i < I2C_MAX_NUM_BUSES; i++) {
i2cBus = ngkh_i2cBusCluster.bus + i;
i2cBus->fd = 0;
i2cBus->devicePath[0] = '\n';
}
ngkh_i2cBusCluster.numBuses = 0;
}
ngkh_i2cDev_t *ngkh_i2cDevAdd(const char *i2cBusDeviceFilePath, uint8_t devAddr)
{
ngkh_i2cDev_t *ret = NULL;
int busNum = ngkh_i2cGetBusNumFromPath(i2cBusDeviceFilePath);
if (busNum < 0) {
if ((busNum = ngkh_i2cAddBus(i2cBusDeviceFilePath)) < 0) {
return ret;
}
}
ret = (ngkh_i2cDev_t*)malloc(sizeof (ngkh_i2cDev_t));
ret->busNum = busNum;
ret->devAddr = devAddr;
return ret;
}
void ngkh_i2cWrite(const ngkh_i2cDev_t *i2cDev, uint8_t regAddr, uint8_t data)
{
const ngkh_i2cBus_t *i2cBus = ngkh_i2cBusCluster.bus + i2cDev->busNum;
uint8_t sendData[] = {regAddr, data};
ngkh_i2cSetSlaveAddressOnBus(i2cBus, i2cDev->devAddr);
if (write(i2cBus->fd, sendData, 2) != 2) {
fprintf(stderr, "Error: Fail to write data to i2c slave\n");
return;
}
}
uint8_t ngkh_i2cRead(const ngkh_i2cDev_t *i2cDev, uint8_t regAddr)
{
const ngkh_i2cBus_t *i2cBus = ngkh_i2cBusCluster.bus + i2cDev->busNum;
uint8_t readData = 0;
ngkh_i2cSetSlaveAddressOnBus(i2cBus, i2cDev->devAddr);
if (write(i2cBus->fd, ®Addr, 1) != 1) {
fprintf(stderr, "Error: Fail to set address on i2c bus.\n");
return readData;
}
if (read(i2cBus->fd, &readData, 1) != 1) {
fprintf(stderr, "Error: Fail to read data from i2c slave.\n");
return readData;
}
return readData;
}
void ngkh_i2cDevRemove(ngkh_i2cDev_t *i2cDev)
{
free(i2cDev);
}
void ngkh_i2cDeinitialize(void)
{
ngkh_i2cBus_t *i2cBus = NULL;
for (size_t i = 0; i < ngkh_i2cBusCluster.numBuses; i++) {
i2cBus = ngkh_i2cBusCluster.bus + i;
if (close(i2cBus->fd) < 0) {
fprintf(stderr, "Error: Fail to close i2c bus\n");
}
}
}
// File-only functions
static bool ngkh_i2cSetSlaveAddressOnBus(const ngkh_i2cBus_t *i2cBus, uint8_t devAddr)
{
bool ret = true;
int statusIoctl = ioctl(i2cBus->fd, I2C_SLAVE, (unsigned long)devAddr);
if (statusIoctl < 0) {
fprintf(stderr, "Error: Unable to get bus access to talk to slave\n");
ret = false;
}
return ret;
}
static int ngkh_i2cGetBusNumFromPath(const char *i2cBusDeviceFilePath)
{
int busNum = -1;
const ngkh_i2cBus_t *i2cBus = NULL;
for (size_t i = 0; i < ngkh_i2cBusCluster.numBuses; i++) {
i2cBus = ngkh_i2cBusCluster.bus + i;
if (strcmp(i2cBusDeviceFilePath, i2cBus->devicePath) == 0) {
busNum = i;
break;
}
}
return busNum;
}
static int ngkh_i2cAddBus(const char *i2cBusDeviceFilePath)
{
ngkh_i2cBus_t *i2cBus = ngkh_i2cBusCluster.bus + ngkh_i2cBusCluster.numBuses;
int fd = 0;
int busNum = -1;
if ((fd = open(i2cBusDeviceFilePath, O_RDWR)) < 0) {
fprintf(stderr, "Error: Fail to open I2C device.\n");
return busNum;
}
i2cBus->fd = fd;
strcpy(i2cBus->devicePath, i2cBusDeviceFilePath);
busNum = ngkh_i2cBusCluster.numBuses++;
return busNum;
}