Dereference a pointer

How do I dereference a pointer? The hex 01a I get back is correct, but I need it as a value. Or, am I even doing this right?

device_id = ccall((:i2c_smbus_read_byte_data, "/home/julia-user/julia-0.6.0/bin/libi2c-dev.so"), Ptr{Int}, (Int, Int), fd, MA8451_REG_WHOAMI)
Ptr{Int32} @0x0000001a

This is some of the C code from the library that I’m calling. __32 is the return type and it should just be an integer.

__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;
}

Why do you want to dereference the byte? You should just use the correct argument and return type and it should just give you an integer.

From this and your previous question (IIRC), your use of argument and return types in ccall are a bit arbitrary. Please read through the multiple tables in the doc first.

Because I’m not sure if I’m doing any of this right. I can’t find if the functions i2c_smbus_read_byte_data(), i2c_smbus_write_byte_data(), and all of the rest of the related i2c functions in the linux system somewhere. All I can find is a i2c-dev.h file (which has #defines and all of the functions [as static inline]).

So, I made that into a shared library - just taking out all of the “static inline” before each function. And, that is the library I’m trying to access fro Julia via the ccall. I’m assuming the shared library is working because I’m not getting any errors like:

ERROR: ccall: could not find function i2c_smbus_write_byte_data in library xxxx

So, I tried the ccall with the correct return parameter (per the documentation) and I kept getting a -1 return value. If I changed it to a Ptr{Int} I was getting the value I’m suppose to get but it’s an address (but it is the correct value). This is all very frustrating because the documentation isn’t very good. They have some simple examples, but not enough to really help figure things out.

I posted another question about converting C unions to Julia and C structs with unions in them and I’ve got no answers. The language is frustrating because there’s no “here’s how you do it in language X, so you would do it like this in Julia.” I am a seasoned programmer. I’ve done everything from FORTRAN on a mainframe to C/C++, Powerbuilder, C#, Java, and some python, but there are tons of books on those other languages. I even bought Mastering Julia and read it, but it doesn’t touch on any of these kind of issues.

I’m sorry I’m venting. It just seems like this kind of stuff should be easier to figure out and it seems like nobody is trying to use Julia to talk to arduinos or do gpio stuff or i2c. It’s a scientific language - it should work with devices that are used in science!

Strugling with getting C header working is understandable but I just mean you should use accurate return and argument types for ccall.

And what did you use? Note that the arguments type are also wrong, though as I mentioned before, they matters less.

You are welcome to point out which part isn’t good enough. The doc about GC safety isn’t good but I do think the argument types mapping is pretty accurate and complete.

Because that’s not possible in general.

yuichao - I just want you to know that I do appreciate the help you’ve given me on several posts. So, here’s my question (and I claim ignorance). All of the “read” routines that I’m trying access in i2c-dev return some form of the following:

#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 */
};

Here are some of the routines (they all have corresponding “write” routines):

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(int file)
{
	union i2c_smbus_data data;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data))
		return -1;
	else
		return 0x0FF & data.byte;
}

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;
}

static inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
{
	union i2c_smbus_data data;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     I2C_SMBUS_WORD_DATA,&data))
		return -1;
	else
		return 0x0FFFF & data.word;
}

/* Returns the number of read bytes */
static inline __s32 i2c_smbus_read_block_data(int file, __u8 command,
                                              __u8 *values)
{
	union i2c_smbus_data data;
	int i;
	if (i2c_smbus_access(file,I2C_SMBUS_READ,command,
	                     I2C_SMBUS_BLOCK_DATA,&data))
		return -1;
	else {
		for (i = 1; i <= data.block[0]; i++)
			values[i-1] = data.block[i];
		return data.block[0];
	}
}

/* Returns the number of read bytes */
/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
   ask for less than 32 bytes, your code will only work with kernels
   2.6.23 and later. */
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];
	}
}

I see the return type on each as __s32 (a signed 32-bit integer), but if the union can address a memory space as large as be as 34 unsigned 8-bit integers - how do I capture that as a return value from a ccall? Wouldn’t it have to be an address to a block of memory? How would you write a ccall for i2c_smbus_read_block_data - for instance?

Frank

Since you need a block of memory, you should, well, allocate that block of memory. In principle, C requires certain alignment so you should also allocate it with the max alignment you might need, in this case, the size is at least 34bytes and the alignment is 4 so you should use a Int32 tuple/array that is at least 36bytes in size (i.e. 9 elements). Either Vector{Int32}(9) or Ref{NTuple{9,Int32}}() works with Ptr{Void} as the argument type.

OK. Let me see if I have this correct.

C code

static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)

Julia code

buffer = Vector{Int32}(9)
ret = ccall((:i2c_smbus_read_block_data, "/home/julia-user/julia-0.6.0/bin/libi2c-dev.so"), Int32, (Int32, UInt8, Ptr{Void}), fd, MMA8451_REG_CTRL_REG1, buffer)        

That looks correct.

Thank you again for all of your help. I guess I kind of want to be an advocate for Julia in the maker world. There are 10s of millions of Raspberry PIs, Beaglebones, and other Linux based devices being used for all kinds of sensor readings or manipulations (all science related). Why can’t Julia be a good language choice for them if we can get GPIO, i2c, serial, and other protocols working? Just a rhetorical question.

It all seems to be working. Now, I’m creating wrapper functions for i2c-dev shared library and I want to make sure I get this function translated properly.

/* Returns the number of read bytes */
static inline __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values)

Does this translate to:

function i2c_smbus_read_block_data(file::Int32, command::UInt8, values::Any)
	return ccall((:i2c_smbus_read_block_data, @I2CLIBRARY), Int32, (Int32, UInt8, Any), file, command, value)
end

and, does this translate the same way (it has const before a parameter - not sure what to do):

static inline __s32 i2c_smbus_write_block_data(int file, __u8 command,
                                               __u8 length, const __u8 *values)

as

function i2c_smbus_write_block_data(file::Int32, command::UInt8, length::UInt8, values::Any)
	return ccall((:i2c_smbus_write_block_data, @I2CLIBRARY), Int32, (Int32, UInt8, UInt8, Any), file, command, length, value)
end

No you should never use Any when callling external libraries. Especially when the input type is arbitrary. As I show in the code above, what you should do with the pointer strongly depends on what they are, what the C code does with them and how you are using them.

OK. use Ptr{Void}?

function i2c_smbus_read_block_data(file::Int32, command::UInt8, values::Vector{UInt8})
	return ccall((:i2c_smbus_read_block_data, @I2CLIBRARY), Int32, (Int32, UInt8, Ptr{Void}), file, command, value)
end

I guess I’m not fully understanding it. Is this like void * in C? I’m assuming a __u8 *values is again going to using a Vector{UInt8} of some size. Am I following correctly?

Ptr{Void} corresponds to void* in C as clearly documented.

Any Ptr or Ref as argument type will give the correct ccall calling conventions but which one is the right one to use depends on what do you want to use with it since that determines the conversion. You should choose a type that does the correct conversion for you.

As mentioned above, you don’t have to use a Vector and especially not necessarily a Vector{UInt8}. Ptr{Void} should work with all Vector or Ref and you can also define correct convertion methods if needed.

@I2CLIBRARY

This is not necessary. You should just use a constant global variable.

Thanks, again. I’m slowly getting it all working and in place. Regarding the constant global variable, that was what I was trying to use initially. The ccall choked when I used the constant in the place of the actual string. I’ll try it again, but that is why I went the macro route.

You must use a constant global. A non-constant global won’t work.

OK. I’m using a constant now (but the macro worked, too). Now, I just need to get a couple more i2c sensor so I can give this a good testing.

Thank you again for your help!

Yes a macro will certainly work. However, macros can be expanded to whatever AST they want to so a macro usually (as they should) raise the question of what it is doing in the code. Here I know that the position it appears makes what it can return extremely limited but that’s certainly not obvious and can easily confuse reader of the code.

OK. Thanks for that bit of info.

This has been quite an exercise. I’m translating C code to Julia (for the i2c-dev) stuff and then translating python code to Julia because I have a python program that works with the MMA8451 accelerometer I’m using.

I just wanted to show you what you’ve been aiding me with. So, the little device is called a NanoPi Duo (similar to a Raspberry Pi). It’s running Ubuntu Linux. The little wired device is the MMA8451 accelerometer connected to 5V, Ground, and SDL, SDA (for i2c communications). The other picture is the Julia code running. So, it’s using a wrapper library (to go against the i2c-dev.h that I modified and made a shared library) and that is what is communicating with the accelerometer. You helped make that happen!

5 Likes