packages feed

i2c-bus-0.1.0: cbits/foreign.c

/*
-- Copyright (c) 2026 karamellpelle@hotmail.com
-- 
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
-- 
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
-- 
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
*/

#include <errno.h>
#include <stddef.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include <unistd.h>
#include "foreign.h"

////////////////////////////////////////////////////////////////////////////////
// see `i2c_rdwr_ioctl_data` at https://www.kernel.org/doc/html/latest/i2c/dev-interface.html#full-interface-description
//
int i2c_read(int fd, uint8_t addr, uint8_t* wbuf, size_t wbuf_len, uint8_t* rbuf, size_t rbuf_len)
{
    // https://github.com/raspberrypi/linux/blob/65495647821026e14223095d1b0124aa3d502dec/include/uapi/linux/i2c.h#L74
    struct i2c_msg messages[2]; 
    
    // write
    messages[0].addr  = addr;
    messages[0].flags = 0;
    messages[0].len   = wbuf_len;
    messages[0].buf   = wbuf;
    // read
    messages[1].addr  = addr;
    messages[1].flags = I2C_M_RD;
    messages[1].len   = rbuf_len;
    messages[1].buf   = rbuf;

    struct i2c_rdwr_ioctl_data rdwr;
    
    // don't write if no data
    if ( wbuf_len == 0 )
    {
        rdwr.msgs = &messages[1];
        rdwr.nmsgs = 1;
    }
    else
    {
        rdwr.msgs = messages;
        rdwr.nmsgs = 2;
    }

    // transfer messages with repeated START (i.e. this is the kernel `i2c_transfer` function but in user API)
    int res = ioctl( fd, I2C_RDWR, &rdwr );
    
    // according to the source for the `i2ctransfer` program, ioctl returns the number of messages sent.
    // but also see comment https://github.com/raspberrypi/linux/blob/65495647821026e14223095d1b0124aa3d502dec/drivers/i2c/i2c-core-base.c#L2322
    if ( res < 0 )           return -errno;
    if ( res != rdwr.nmsgs ) return -EIO;
   
    // return number of bytes read
    return rbuf_len;
}

int i2c_read_some(int fd, uint8_t addr, uint8_t* wbuf, size_t wbuf_len, uint8_t* rbuf, size_t rbuf_len)
{
    // FIXME: maybe the i2c_msg flag "I2C_M_IGNORE_NAK: treat NACK from client as ACK" will let us
    //        treat NACK as success? 
    //        see https://www.kernel.org/doc/html/v5.14/i2c/i2c-protocol.html#modified-transactions
    // TODO: compare how the `i2ctransfer` program does this: https://www.kernel.org/pub/software/utils/i2c-tools/
    return -EPERM;
}

int i2c_write(int fd, uint8_t addr, uint8_t* wbuf, size_t wbuf_len)
{
    // https://github.com/raspberrypi/linux/blob/65495647821026e14223095d1b0124aa3d502dec/include/uapi/linux/i2c.h#L74
    struct i2c_msg messages[1]; 
    
    // write
    messages[0].addr  = addr;
    messages[0].flags = 0;
    messages[0].len   = wbuf_len;
    messages[0].buf   = wbuf;

    struct i2c_rdwr_ioctl_data rdwr;
    rdwr.msgs = messages;
    rdwr.nmsgs = 1;

    // transfer messages with repeated START (i.e. this is the `i2c_transfer` function in user API)
    int res = ioctl( fd, I2C_RDWR, &rdwr );
    
    // according to the source for the `i2ctransfer` program, ioctl returns the number of messages sent.
    // but also see comment https://github.com/raspberrypi/linux/blob/65495647821026e14223095d1b0124aa3d502dec/drivers/i2c/i2c-core-base.c#L2322
    if ( res < 0 )           return -errno;
    if ( res != rdwr.nmsgs ) return -EIO;
    
    return wbuf_len;
}

int i2c_write_some(int fd, uint8_t addr, uint8_t* wbuf, size_t wbuf_len)
{
    // FIXME: maybe the i2c_msg flag "I2C_M_IGNORE_NAK: treat NACK from client as ACK" will let us
    //        treat NACK as success? 
    //        see https://www.kernel.org/doc/html/v5.14/i2c/i2c-protocol.html#modified-transactions
    return -EPERM;
}