Help converting C code (i2c-dev)


#1

I’m trying to convert the i2c-dev.h file into Julia. I am having problems converting a C union to Julia, a couple of structs, and how to pass a struct to a ccall(). I’ll show the C code first then the Julia code.

The C code:

/*
 * I2C Message - used for pure i2c transaction, also from /dev interface
 */
struct i2c_msg {
	__u16 addr;	/* slave address			*/
	unsigned short flags;
#define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
#define I2C_M_RD	0x01
#define I2C_M_NOSTART	0x4000
#define I2C_M_REV_DIR_ADDR	0x2000
#define I2C_M_IGNORE_NAK	0x1000
#define I2C_M_NO_RD_ACK		0x0800
	short len;		/* msg length				*/
	char *buf;		/* pointer to msg data			*/
};

/* To determine what functionality is present */

#define I2C_FUNC_I2C			0x00000001
#define I2C_FUNC_10BIT_ADDR		0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
#define I2C_FUNC_SMBUS_PEC		0x00000008
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK		0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */

#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
                             I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
                                   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

/* Old name, for compatibility */
#define I2C_FUNC_SMBUS_HWPEC_CALC	I2C_FUNC_SMBUS_PEC

/*
 * Data for SMBus Messages
 */
#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */
#define I2C_SMBUS_I2C_BLOCK_MAX	32	/* Not specified but we use same structure */
union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
	                                            /* and one more for PEC */
};

/* smbus_access read or write markers */
#define I2C_SMBUS_READ	1
#define I2C_SMBUS_WRITE	0

/* SMBus transaction types (size parameter in the above functions)
   Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
#define I2C_SMBUS_QUICK		    0
#define I2C_SMBUS_BYTE		    1
#define I2C_SMBUS_BYTE_DATA	    2
#define I2C_SMBUS_WORD_DATA	    3
#define I2C_SMBUS_PROC_CALL	    4
#define I2C_SMBUS_BLOCK_DATA	    5
#define I2C_SMBUS_I2C_BLOCK_BROKEN  6
#define I2C_SMBUS_BLOCK_PROC_CALL   7		/* SMBus 2.0 */
#define I2C_SMBUS_I2C_BLOCK_DATA    8


/* /dev/i2c-X ioctl commands.  The ioctl's parameter is always an
 * unsigned long, except for:
 *	- I2C_FUNCS, takes pointer to an unsigned long
 *	- I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
 *	- I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
 */
#define I2C_RETRIES	0x0701	/* number of times a device address should
				   be polled when not acknowledging */
#define I2C_TIMEOUT	0x0702	/* set timeout in units of 10 ms */

/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
 * are NOT supported! (due to code brokenness)
 */
#define I2C_SLAVE	0x0703	/* Use this slave address */
#define I2C_SLAVE_FORCE	0x0706	/* Use this slave address, even if it
				   is already in use by a driver! */
#define I2C_TENBIT	0x0704	/* 0 for 7 bit addrs, != 0 for 10 bit */

#define I2C_FUNCS	0x0705	/* Get the adapter functionality mask */

#define I2C_RDWR	0x0707	/* Combined R/W transfer (one STOP only) */

#define I2C_PEC		0x0708	/* != 0 to use PEC with SMBus */
#define I2C_SMBUS	0x0720	/* SMBus transfer */


/* This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data {
	__u8 read_write;
	__u8 command;
	__u32 size;
	union i2c_smbus_data *data;
};

/* This is the structure as used in the I2C_RDWR ioctl call */
struct i2c_rdwr_ioctl_data {
	struct i2c_msg *msgs;	/* pointers to i2c_msgs */
	__u32 nmsgs;			/* number of i2c_msgs */
};

#define  I2C_RDRW_IOCTL_MAX_MSGS	42


static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
                                     int size, union i2c_smbus_data *data)
{
	struct i2c_smbus_ioctl_data args;

	args.read_write = read_write;
	args.command = command;
	args.size = size;
	args.data = data;
	return ioctl(file,I2C_SMBUS,&args);
}

The Julia code:

#
# I2C Message - used for pure i2c transaction, also from /dev interface
#
const	I2C_M_TEN			=	0x10	# we have a ten bit chip address	
const	I2C_M_RD			=	0x01
const	I2C_M_NOSTART		=	0x4000
const	I2C_M_REV_DIR_ADDR	=	0x2000
const	I2C_M_IGNORE_NAK	=	0x1000
const	I2C_M_NO_RD_ACK		=	0x0800

struct i2c_msg 
	addr::UInt32	# slave address
	flags::Cushort
	len::Cshort		# msg length
	buf::Ptr{UInt8}	#pointer to msg data
end

# To determine what functionality is present 

const I2C_FUNC_I2C						=	0x00000001
const I2C_FUNC_10BIT_ADDR				=	0x00000002
const I2C_FUNC_PROTOCOL_MANGLING		=	0x00000004 # I2C_M_{REV_DIR_ADDR,NOSTART,..} 
const I2C_FUNC_SMBUS_PEC				=	0x00000008
const I2C_FUNC_SMBUS_BLOCK_PROC_CALL	=	0x00008000 # SMBus 2.0
const I2C_FUNC_SMBUS_QUICK				=	0x00010000
const I2C_FUNC_SMBUS_READ_BYTE			=	0x00020000
const I2C_FUNC_SMBUS_WRITE_BYTE			=	0x00040000
const I2C_FUNC_SMBUS_READ_BYTE_DATA		=	0x00080000
const I2C_FUNC_SMBUS_WRITE_BYTE_DATA	=	0x00100000
const I2C_FUNC_SMBUS_READ_WORD_DATA		=	0x00200000
const I2C_FUNC_SMBUS_WRITE_WORD_DATA	=	0x00400000
const I2C_FUNC_SMBUS_PROC_CALL			=	0x00800000
const I2C_FUNC_SMBUS_READ_BLOCK_DATA	=	0x01000000
const I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 	=	0x02000000
const I2C_FUNC_SMBUS_READ_I2C_BLOCK		=	0x04000000 # I2C-like block xfer  
const I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	=	0x08000000 # w/ 1-byte reg. addr. 

const I2C_FUNC_SMBUS_BYTE 				=	(I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE)
const I2C_FUNC_SMBUS_BYTE_DATA 			=	(I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
const I2C_FUNC_SMBUS_WORD_DATA 			=	(I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA)
const I2C_FUNC_SMBUS_BLOCK_DATA 		=	(I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
const I2C_FUNC_SMBUS_I2C_BLOCK 			=	(I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

# Old name, for compatibility 
const I2C_FUNC_SMBUS_HWPEC_CALC			=	I2C_FUNC_SMBUS_PEC

#
# Data for SMBus Messages
#
const I2C_SMBUS_BLOCK_MAX				=	32	# As specified in SMBus standard 
const I2C_SMBUS_I2C_BLOCK_MAX			=	32	# Not specified but we use same structure 

I don’t know how to translate this union to Julia.

union i2c_smbus_data 
	byte::UInt8
	word::Cushort
	block::Vector{UInt8}(I2C_SMBUS_BLOCK_MAX + 2) 	# block[0] is used for length
													# and one more for PEC 
end
# smbus_access read or write markers 
const I2C_SMBUS_READ					=	1
const I2C_SMBUS_WRITE					=	0

# SMBus transaction types (size parameter in the above functions)
#  Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! 

const I2C_SMBUS_QUICK		    		=	0
const I2C_SMBUS_BYTE		    		=	1
const I2C_SMBUS_BYTE_DATA	    		=	2
const I2C_SMBUS_WORD_DATA	    		=	3
const I2C_SMBUS_PROC_CALL	    		=	4
const I2C_SMBUS_BLOCK_DATA	    		=	5
const I2C_SMBUS_I2C_BLOCK_BROKEN  		=	6
const I2C_SMBUS_BLOCK_PROC_CALL   		=	7		# SMBus 2.0
const I2C_SMBUS_I2C_BLOCK_DATA    		=	8


# /dev/i2c-X ioctl commands.  The ioctl's parameter is always an
# * unsigned long, except for:
# *	- I2C_FUNCS, takes pointer to an unsigned long
# *	- I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
# *	- I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
#

const I2C_RETRIES						=	0x0701	# number of times a device address should
													# be polled when not acknowledging 
const I2C_TIMEOUT						=	0x0702	# set timeout in units of 10 ms 

# NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
# * are NOT supported! (due to code brokenness)
#

const I2C_SLAVE							=	0x0703	# Use this slave address
const I2C_SLAVE_FORCE					=	0x0706	# Use this slave address, even if it
													# is already in use by a driver! 
const I2C_TENBIT						=	0x0704	# 0 for 7 bit addrs, != 0 for 10 bit 

const I2C_FUNCS							=	0x0705	# Get the adapter functionality mask 

const I2C_RDWR							=	0x0707	# Combined R/W transfer (one STOP only) 

const I2C_PEC							=	0x0708	# != 0 to use PEC with SMBus 
const I2C_SMBUS							=	0x0720	# SMBus transfer 

I don’t know how to translate this union within a struct to Julia.

# This is the structure as used in the I2C_SMBUS ioctl call 
struct i2c_smbus_ioctl_data 
	read_write::UInt8
	command::UInt8
	size::UInt32
	union i2c_smbus_data *data
end

How do you make a pointer to a struct within a struct?

# This is the structure as used in the I2C_RDWR ioctl call 
struct i2c_rdwr_ioctl_data 
	struct i2c_msg *msgs	# pointers to i2c_msgs 
	nmsgs::UInt32			# number of i2c_msgs 
end
const  I2C_RDRW_IOCTL_MAX_MSGS				=	42

How do you pass a pointer to a union to a function? Then, how do you define a struct parameter for a ccall and then pass that struct?

function i2c_smbus_access(file::Int, read_write::Cuchar, command::UInt8,
                                     size::Int, union i2c_smbus_data *data)
	args = i2c_smbus_ioctl_data()

	args.read_write = read_write
	args.command = command
	args.size = size
	args.data = data
	
	ret = ccall(:ioctl, "libc"), Int, (Int, Int, 
	return ioctl(file,I2C_SMBUS,&args);
end

#2

[edit: the below code is all wrong, as yuyichao pointed out. I think he answered your question in the other thread, so I’ll refrain from correcting my post and just keep it for my eternal shame. Thanks!]

For your union, you could try something like:

type i2c_smbus_data 
    Block::Vector{UInt8}
    function i2c_smbus_data()
        d = new()
        d.Block = Vector{UInt8}(I2C_SMBUS_BLOCK_MAX + 2) 
        return d
    end
end

extract_byte(d::i2c_smbus_data) = d.Block[1]

extract_word(d::i2c_smbus_data) = 
    unsafe_load(convert(Ptr{Cushort},pointer(d.Block)),1)

The pointer-load-stuff is because we don’t know the endianness of UInt16/Cushort on our machine. I don’t know whether there is a more elegant way (but constructions like the above tend to compile down to a single load instruction).

The next one is simpler, since you don’t have a union inside a struct; you just have a normal pointer field:

# This is the structure as used in the I2C_SMBUS ioctl call 
type i2c_smbus_ioctl_data 
	read_write::UInt8
	command::UInt8
	size::UInt32
	data::Ptr{i2c_smbus_data}
end

And the same should hold in the next one:

# This is the structure as used in the I2C_RDWR ioctl call 
type i2c_rdwr_ioctl_data 
	msgs::Ptr{i2c_msg}	# pointers to i2c_msgs 
	nmsgs::UInt32			# number of i2c_msgs 
end

How do you pass a pointer to a union to a function? Then, how do you define a struct parameter for a ccall and then pass that struct?

If your C function expects pointers, you just pass pointers. If your C function expects structs/unions by value, then you need to make your types immutable-bitstype (which you mostly did by declaring them as struct and filling them with primitives and pointers only), and can pass them.

The exception is i2c_smbus_data, which is not bitstype because it contains a reference to a heap-allocated Vector.

If you need to pass i2c_smbus_data by value (seriously? who for gods sake would design such an API?), I would define it instead as a tuple.

Unions are just syntactic sugar for “please reinterpret this blob of memory in the following way” and “allocate a blob of memory of max(sizeof(f) for f in fields)”; julia does not provide this syntactic sugar by default, but you can emulate it by hand.

Warning: the above code is untested and may well be wrong


#3

Wrong alignment. Ref Dereference a pointer


#4

@yuyichao in the other thread you said:

I posted another question about converting C unions to Julia and C structs with unions in them and I’ve got no answers.

Because that’s not possible in general.

A C union is just a struct with the alignment of the largest member (plus some syntax sugar).

Using a Julia struct with the largest union member as its only field should give the correct alignment. This question has been answered with such a suggestion many times, and the suggestion is documented. It’s annoying/dangerous if there are conditional members, but assuming correct mirroring of what the preprocessor did, it should work fine.

But I’m sure you know all of this … am I missing something?


#5

No, it has at least the size of the largest member and the alignment of the most aligned member, not the largest member. So that doc is wrong.


#6

Maybe, if there is an answer to the union issue - this could be added to the documentation under Callling C and Fortran. Unions are commonly used in C and it would probably benefit many if it were in the documentation with a couple of examples.

On Sat, Oct 7, 2017 at 8:19 AM, Yichao Yujulialang@discoursemail.com wrote: ihnorton:

A C union is just a struct with the alignment of the largest member (plus some syntax sugar).

No, it has at least the size of the largest member and the alignment of the most aligned member, not the largest member. So that doc is wrong.

Visit Topic or reply to this email to respond.

To unsubscribe from these emails, click here.


#7

Alright, thanks for the correction.

I think the doc is basically accurate in that it mentions the potential need for padding as a parenthetical (Jameson wrote the paragraph).


#8

OK. I have the whole i2c-dev.c converted to Julia. I am having problems with how to define the ccall (again). I’ve been to the Calling C and Fortran documentation and there are no examples of how to pass a struct. Can you help again? I’m sorry to be so ignorant on this stuff.

Here’s the C Code:

#define I2C_SMBUS_BLOCK_MAX	32	/* As specified in SMBus standard */
#define I2C_SMBUS_I2C_BLOCK_MAX	32	/* Not specified but we use same structure */
union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
	                                            /* and one more for PEC */
};

/* This is the structure as used in the I2C_SMBUS ioctl call */
struct i2c_smbus_ioctl_data {
	__u8 read_write;
	__u8 command;
	__u32 size;
	union i2c_smbus_data *data;
};

static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
                                     int size, union i2c_smbus_data *data)
{
	struct i2c_smbus_ioctl_data args;

	args.read_write = read_write;
	args.command = command;
	args.size = size;
	args.data = data;
	return ioctl(file,I2C_SMBUS,&args);
}

Here’s the Julia code. I figured i2c_smbus_data can just become a data::Vector{UInt8} and I’ll put/get the values appropriately (something like data[1] becomes byte, data[1] and data[2] become a word [with some bit shifting], and data[1:n] becomes block).

# This is the structure as used in the I2C_SMBUS ioctl call 
mutable struct i2c_smbus_ioctl_data 
	read_write::UInt8
	command::UInt8
	size::UInt32
	data::Vector{UInt8}
end

function i2c_smbus_access(file::Int32, read_write::UInt8, command::UInt8,
                                     size::Int32, data::Vector{UInt8})
	args = i2c_smbus_ioctl_data(read_write, command, size, data)

	#args.read_write = read_write
	#args.command = command
	#args.size = size
	#args.data = data
	
	ret::Int32 = ccall((:ioctl, "libc"), Int32, (Int32, UInt16, i2c_smbus_ioctl_data), file, I2C_SMBUS, args)
	return ret
end

#9

I’m pretty sure this is at least the third time I talked about this in this and previous thread but no you can’t, the alignment is wrong. You have to use Vector{UInt16} or NTuple{N,UInt16}.


#10

And this is wrong. There’s no equivalent of Vector in C. You have to store a pointer to a struct here. You can do that with.

mutable struct i2c_smbus_data
    block::NTuple{(I2C_SMBUS_BLOCK_MAX + 1) ÷ 2 + 1,UInt16}
end
mutable struct i2c_smbus_ioctl_data
	read_write::UInt8
	command::UInt8
	size::UInt32
	data::i2c_smbus_data
end

Note that the C code must NOT mutate the data field of i2c_smbus_ioctl_data. It can mutate the memory data points to but it can’t change what data points to.


#11

Alternatively, if this struct will not be passed around, you can directly store a pointer there (and use @gc_preserve to make sure the object owning that pionter is alive during the ccall). You still need to make sure that the data has the right alignment though.


#12

Man, I don’t know how you figure this stuff out. A good blog, article, or whatever - would be so helpful on figuring this stuff. Thanks, again, for your help, and I’m sorry I’m not getting it when you’ve explained it before.


#13

OK. I just don’t know what I’m doing wrong. (Please don’t get mad. I went back and read your previous posts to me. I’ve read the Calling C and Fortran section. I just don’t know what I’m doing wrong.)

All of the functions in i2c-dev call on the function - i2c_smbus_access(). Below is an example call.

const I2C_SLAVE = 0x0703
const ACCEL_ADDR	=	0x1D
const MA8451_REG_WHOAMI = 0x0D

#This works - I get a proper file descriptor
fd = i2c_init("/dev/i2c-0", ACCEL_ADDR, I2C_SLAVE)

#This doesn't work. See the code below.
device_id = i2c_smbus_read_byte_data(fd, MA8451_REG_WHOAMI)

If I can get i2c_smbus_access() - I’m pretty sure the rest will fall into place. The pieces are:

file = fd
read_write = I2C_SMBUS_READ)
command = MA8451_REG_WHOAMI
size = I2C_SMBUS_BYTE_DATA.

So,

args = i2c_smbus_ioctl_data(read_write, command, size, data)

ccall((:ioctl, "libc"), Int32, (Int32, UInt16, i2c_smbus_ioctl_data), file, I2C_SMBUS, args)

Here’s what the C code is doing.

static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
                                     int size, union i2c_smbus_data *data)
{
struct i2c_smbus_ioctl_data args;

	args.read_write = read_write;
	args.command = command;
	args.size = size;
	args.data = data;
	return ioctl(file,I2C_SMBUS,&args);
}

static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
{
	union i2c_smbus_data data;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     I2C_SMBUS_BYTE_DATA,&data))
		return -1;
	else
		return 0x0FF & data.byte;
}

The Julia code:

const 	I2C_SMBUS_BLOCK_MAX				=	32	# As specified in SMBus standard 
const 	I2C_SMBUS_I2C_BLOCK_MAX			=	32	# Not specified but we use same structure 
mutable struct i2c_smbus_data
    block::NTuple{(I2C_SMBUS_BLOCK_MAX + 1) ÷ 2 + 1,UInt16}
    #block::Vector{Int32}
end


# smbus_access read or write markers 
const 	I2C_SMBUS_READ					=	1
const 	I2C_SMBUS_WRITE		                        =      0

const 	I2C_SMBUS_BYTE		    		=	1
const 	I2C_SMBUS_BYTE_DATA	    		=	2

mutable struct i2c_smbus_ioctl_data
	read_write::UInt8
	command::UInt8
	size::UInt32
	data::i2c_smbus_data
end

function i2c_smbus_access(file::Int32, read_write::UInt8, command::UInt8,
                                     size::Int32, data::i2c_smbus_data)
	args = i2c_smbus_ioctl_data(read_write, command, size, data)

	#args.read_write = read_write
	#args.command = command
	#args.size = size
	#args.data = data
	
	ret = ccall((:ioctl, "libc"), Int32, (Int32, UInt16, i2c_smbus_ioctl_data), file, I2C_SMBUS, args)
	return ret
end

function i2c_smbus_read_byte_data(file::Int32, command::UInt8)
	data = i2c_smbus_data([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])
		
	ret = i2c_smbus_access(file, UInt8(I2C_SMBUS_READ), command, I2C_SMBUS_BYTE_DATA, data)
	if (ret < 0)
		return ret
	else
		return 0x0FF & data.block[1]
	end
end

And, every other routine uses i2c_smbus_access() like the one above.


#14

what do you mean by “not working”?


#15

I get a -1 on the ccall. I should get a decimal 26.


#16

This is wrong.

  1. Please read the C function declaration and declare the argument types accordingly. As I said before, the argument type for the first two arguments doesn’t matter too much in this case but you should still use the right one, (both Cint).
  2. And as I said before, the vararg arguments needs to be declared in the argument so the third argument type must have a ...
  3. You are passing struct by value, pass by ref (pointer) instead.

The correct call signature should be (Cint, Cint, Ref{i2c_smbus_ioctl_data}...)


#17

I should add that I have an mma8451 accelerometer attached to my linux device. So, I know what values I should get back based on the wrapper I wrote for the i2c-dev shared library I created (which all works). I’m trying to create a Julia only version (with no shared library - only going against libs directly).


#18

Ok. I’ll try it (will be in the morning). I’m away from my computer, now. Thank you, again.

On Wed, Oct 11, 2017 at 6:29 PM, Yichao Yujulialang@discoursemail.com wrote: Frank_Applin:

ret = ccall((:ioctl, “libc”), Int32, (Int32, UInt16, i2c_smbus_ioctl_data), file, I2C_SMBUS, args)

This is wrong.

  • Please read the C function declaration and declare the argument types accordingly. As I said before, the argument type for the first two arguments doesn’t matter too much in this case but you should still use the right one, (both Cint).
  • And as I said before, the vararg arguments needs to be declared in the argument so the third argument type must have a …
  • You are passing struct by value, pass by ref (pointer) instead.

The correct call signature should be (Cint, Cint, Ref{i2c_smbus_ioctl_data}…)

Visit Topic or reply to this email to respond.

To unsubscribe from these emails, click here.


#19

OK. I made real progress thanks to all of your help. The byte and word routines seem to be working fine. Now, I need to get the block data back.

Here’s the C code (so, the buffer [values] is passed as a pointer):

static inline __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command,
                                                  __u8 length, __u8 *values)
{
	union i2c_smbus_data data;
	int i;

	if (length > 32)
		length = 32;
	data.block[0] = length;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
	                      I2C_SMBUS_I2C_BLOCK_DATA,&data))
		return -1;
	else {
		for (i = 1; i <= data.block[0]; i++)
			values[i-1] = data.block[i];
		return data.block[0];
	}
}

In Julia - I had to create a new NTuple (because I believe NTuple are immutable). I am getting the correct values in data.block, but now I need to get them back through the function call):

function i2c_smbus_read_i2c_block_data(file::Int32, command::UInt8, length::UInt8, values::i2c_smbus_data)

	if (length > 32)
		length = 32
	end
	data = i2c_smbus_data((length,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))

	
	ret = i2c_smbus_access(file, UInt8(I2C_SMBUS_READ), command,
	                     length == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
	                      I2C_SMBUS_I2C_BLOCK_DATA, data)
	if (ret < 0)
		return ret
	else 
		tmpArray = collect(data.block)
		print("length: ")
		println(length)
		for i = 1:length
			print("data.block[i]: ")
			println(data.block[i])
			tmpArray[i] = data.block[i]
		end
		values = NTuple{(I2C_SMBUS_BLOCK_MAX + 1) ÷ 2 + 1, UInt16}(tmpArray)

		return length
	end
end

and, this is the actual call:

buffer = i2c_smbus_data((0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))

read_bytes = i2c_smbus_read_i2c_block_data(fd, MMA8451_REG_OUT_X_MSB, 0x6, buffer) 

print("read_bytes: ")
println(read_bytes)  
	
for n = 1:read_bytes    
	println("raw: " * string(buffer.block[n]))
end

So, when I use my print statements - I see:

length: 6
data.block[i]: 2054
data.block[i]: 63860
data.block[i]: 7836
data.block[i]: 8
data.block[i]: 0
data.block[i]: 0
returning
read_bytes: 6
raw: 0
raw: 0
raw: 0
raw: 0
raw: 0
raw: 0


So, how do I get my newly created values back out as a function parameter?


#20

You need to mutate values instead of assigning to it. There isn’t a very good way to do that right now in general but for this case you can just do.

Base.@gc_preserve data ccall(:memcpy, Ptr{Void}, (Ref{i2c_smbus_data}, Ptr{Void}, Csize_t), values, pointer_from_objref(data) + 1, data.block[0] % UInt8)

You can leave out the Base.@gc_preserve data pre-0.7 but doing so can crash on 0.7+. Note that this relies on the little endianness of the layout. So does the initialization of data.