mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-08-30 17:19:26 +08:00
297 lines
6.4 KiB
C
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;
|
|
}
|