forked from Imagelibrary/rtems
287 lines
5.1 KiB
C
287 lines
5.1 KiB
C
/**
|
|
* @file
|
|
*
|
|
* @brief Inter-Integrated Circuit (I2C) Bus Implementation
|
|
*
|
|
* @ingroup I2CDevice
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2014 embedded brains GmbH & Co. KG
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.org/license/LICENSE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <dev/i2c/i2c.h>
|
|
|
|
#include <rtems/imfs.h>
|
|
#include <rtems/score/assert.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
static ssize_t i2c_dev_read(
|
|
rtems_libio_t *iop,
|
|
void *buffer,
|
|
size_t count
|
|
)
|
|
{
|
|
i2c_dev *dev = IMFS_generic_get_context_by_iop(iop);
|
|
ssize_t n;
|
|
|
|
n = (*dev->read)(dev, buffer, count, iop->offset);
|
|
if (n >= 0) {
|
|
iop->offset += n;
|
|
|
|
return n;
|
|
} else {
|
|
rtems_set_errno_and_return_minus_one(-n);
|
|
}
|
|
}
|
|
|
|
static ssize_t i2c_dev_write(
|
|
rtems_libio_t *iop,
|
|
const void *buffer,
|
|
size_t count
|
|
)
|
|
{
|
|
i2c_dev *dev = IMFS_generic_get_context_by_iop(iop);
|
|
ssize_t n;
|
|
|
|
n = (*dev->write)(dev, buffer, count, iop->offset);
|
|
if (n >= 0) {
|
|
iop->offset += n;
|
|
|
|
return n;
|
|
} else {
|
|
rtems_set_errno_and_return_minus_one(-n);
|
|
}
|
|
}
|
|
|
|
static int i2c_dev_ioctl(
|
|
rtems_libio_t *iop,
|
|
ioctl_command_t command,
|
|
void *arg
|
|
)
|
|
{
|
|
i2c_dev *dev = IMFS_generic_get_context_by_iop(iop);
|
|
int err;
|
|
|
|
err = (*dev->ioctl)(dev, command, arg);
|
|
|
|
if (err == 0) {
|
|
return 0;
|
|
} else {
|
|
rtems_set_errno_and_return_minus_one(-err);
|
|
}
|
|
}
|
|
|
|
static int i2c_dev_fstat(
|
|
const rtems_filesystem_location_info_t *loc,
|
|
struct stat *buf
|
|
)
|
|
{
|
|
i2c_dev *dev = IMFS_generic_get_context_by_location(loc);
|
|
|
|
buf->st_size = (*dev->get_size)(dev);
|
|
buf->st_blksize = (*dev->get_block_size)(dev);
|
|
|
|
return IMFS_stat(loc, buf);
|
|
}
|
|
|
|
static const rtems_filesystem_file_handlers_r i2c_dev_handler = {
|
|
.open_h = rtems_filesystem_default_open,
|
|
.close_h = rtems_filesystem_default_close,
|
|
.read_h = i2c_dev_read,
|
|
.write_h = i2c_dev_write,
|
|
.ioctl_h = i2c_dev_ioctl,
|
|
.lseek_h = rtems_filesystem_default_lseek_file,
|
|
.fstat_h = i2c_dev_fstat,
|
|
.ftruncate_h = rtems_filesystem_default_ftruncate,
|
|
.fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
|
|
.fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
|
|
.fcntl_h = rtems_filesystem_default_fcntl,
|
|
.kqfilter_h = rtems_filesystem_default_kqfilter,
|
|
.mmap_h = rtems_filesystem_default_mmap,
|
|
.poll_h = rtems_filesystem_default_poll,
|
|
.readv_h = rtems_filesystem_default_readv,
|
|
.writev_h = rtems_filesystem_default_writev
|
|
};
|
|
|
|
static void i2c_dev_node_destroy(IMFS_jnode_t *node)
|
|
{
|
|
i2c_dev *dev;
|
|
|
|
dev = IMFS_generic_get_context_by_node(node);
|
|
(*dev->destroy)(dev);
|
|
|
|
IMFS_node_destroy_default(node);
|
|
}
|
|
|
|
static const IMFS_node_control i2c_dev_node_control = IMFS_GENERIC_INITIALIZER(
|
|
&i2c_dev_handler,
|
|
IMFS_node_initialize_generic,
|
|
i2c_dev_node_destroy
|
|
);
|
|
|
|
int i2c_dev_register(
|
|
i2c_dev *dev,
|
|
const char *dev_path
|
|
)
|
|
{
|
|
int rv;
|
|
|
|
rv = IMFS_make_generic_node(
|
|
dev_path,
|
|
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
|
&i2c_dev_node_control,
|
|
dev
|
|
);
|
|
if (rv != 0) {
|
|
(*dev->destroy)(dev);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static ssize_t i2c_dev_read_default(
|
|
i2c_dev *dev,
|
|
void *buf,
|
|
size_t n,
|
|
off_t offset
|
|
)
|
|
{
|
|
(void) dev;
|
|
(void) buf;
|
|
(void) n;
|
|
(void) offset;
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static ssize_t i2c_dev_write_default(
|
|
i2c_dev *dev,
|
|
const void *buf,
|
|
size_t n,
|
|
off_t offset
|
|
)
|
|
{
|
|
(void) dev;
|
|
(void) buf;
|
|
(void) n;
|
|
(void) offset;
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int i2c_dev_ioctl_default(
|
|
i2c_dev *dev,
|
|
ioctl_command_t command,
|
|
void *arg
|
|
)
|
|
{
|
|
(void) dev;
|
|
(void) command;
|
|
(void) arg;
|
|
|
|
return -ENOTTY;
|
|
}
|
|
|
|
static off_t i2c_dev_get_size_default(i2c_dev *dev)
|
|
{
|
|
(void) dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static blksize_t i2c_dev_get_block_size_default(i2c_dev *dev)
|
|
{
|
|
(void) dev;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int i2c_dev_do_init(
|
|
i2c_dev *dev,
|
|
const char *bus_path,
|
|
uint16_t address,
|
|
void (*destroy)(i2c_dev *dev)
|
|
)
|
|
{
|
|
int rv;
|
|
|
|
dev->bus_fd = open(bus_path, O_RDWR);
|
|
if (dev->bus_fd < 0) {
|
|
(*destroy)(dev);
|
|
|
|
return -1;
|
|
}
|
|
|
|
rv = ioctl(dev->bus_fd, I2C_BUS_GET_CONTROL, &dev->bus);
|
|
if (rv != 0) {
|
|
(*destroy)(dev);
|
|
|
|
return -1;
|
|
}
|
|
|
|
dev->read = i2c_dev_read_default;
|
|
dev->write = i2c_dev_write_default;
|
|
dev->ioctl = i2c_dev_ioctl_default;
|
|
dev->get_size = i2c_dev_get_size_default;
|
|
dev->get_block_size = i2c_dev_get_block_size_default;
|
|
dev->destroy = destroy;
|
|
dev->address = address;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void i2c_dev_destroy(i2c_dev *dev)
|
|
{
|
|
int rv;
|
|
|
|
rv = close(dev->bus_fd);
|
|
_Assert(dev->bus_fd < 0 || rv == 0);
|
|
(void) rv;
|
|
}
|
|
|
|
void i2c_dev_destroy_and_free(i2c_dev *dev)
|
|
{
|
|
i2c_dev_destroy(dev);
|
|
free(dev);
|
|
}
|
|
|
|
int i2c_dev_init(i2c_dev *dev, const char *bus_path, uint16_t address)
|
|
{
|
|
return i2c_dev_do_init(dev, bus_path, address, i2c_dev_destroy);
|
|
}
|
|
|
|
i2c_dev *i2c_dev_alloc_and_init(
|
|
size_t size,
|
|
const char *bus_path,
|
|
uint16_t address
|
|
)
|
|
{
|
|
i2c_dev *dev = NULL;
|
|
|
|
if (size >= sizeof(*dev)) {
|
|
dev = calloc(1, size);
|
|
if (dev != NULL) {
|
|
int rv;
|
|
|
|
rv = i2c_dev_do_init(dev, bus_path, address, i2c_dev_destroy_and_free);
|
|
if (rv != 0) {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dev;
|
|
}
|