Newer
Older
libngkh_i2c / ngkh_i2c.c
#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, &regAddr, 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;
}