Files
rtems/cpukit/dev/i2c/i2c-dev.c
T
2026-02-05 10:46:11 -07:00

297 lines
6.4 KiB
C

/**
* @file
*
* @brief Inter-Integrated Circuit (I2C) Bus Implementation
*
* @ingroup I2CDevice
*/
/*
* Copyright (C) 2014 embedded brains GmbH & Co. KG
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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;
}