thispi4me
Posts: 16
Joined: Mon Jan 20, 2020 12:23 am

ioctl

Tue Feb 04, 2020 2:54 am

Okay thanks to help from this forum and (LdB’s) reply I am starting to understand how to control I2C devices. Coming from a microcontroller background I love all that Linux adds to reducing my coding while continuing to be frustrated with it’s learning curve at the same time. I'm confused about the ioctl function. I get the point it is a path to the kernel from user space but how do I control it? Google tells me there should be a /sys/ioctl.h file that shows options but this isn't found on my pi. Where is it than? Below is a small code fragment that works to control a I2C slave but if I change the address in code to a incorrect value it fails (exits) one of two ways: Silently or printing ": Remote I/O error", never my error message. Where do I find more information about ioctl?

Code: Select all

int main() {
  int fd;
  int ads_address = 0x11;
  uint8_t buf[10];
  int16_t val;

  // open device
  if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) {
	printf("Error: Couldn't open device! %d\n", fd);
	return 1;
  }
  printf("Device open\n");
  // connect to  i2c slave
  if (ioctl(fd, I2C_SLAVE, ads_address) < 0) {
	printf("Error: Couldn't find device on address!\n");
	return 1;
	

LdB
Posts: 1518
Joined: Wed Dec 07, 2016 2:29 pm

Re: ioctl

Tue Feb 04, 2020 7:58 am

Did you turn on the I2C and check the device shows on i2c-0 or i2c-1

basically did you do all this section on linux first (Checking For Connected Devices)
https://raspberry-projects.com/pi/progr ... -interface

If it doesn't show on linux your C code won't work because it uses linux.

User avatar
jojopi
Posts: 3190
Joined: Tue Oct 11, 2011 8:38 pm

Re: ioctl

Tue Feb 04, 2020 9:41 am

thispi4me wrote:
Tue Feb 04, 2020 2:54 am
Google tells me there should be a /sys/ioctl.h file that shows options but this isn't found on my pi.
It is not /sys/ioctl.h, it is <sys/ioctl.h> as in #include <sys/ioctl.h>. The angle brackets tell the compiler to look in the normal places, such as /usr/include/. The header file is not particularly useful to the programmer. You would want man ioctl et al.
Silently or printing ": Remote I/O error", never my error message.
The code you listed (and the shell you ran it from) cannot possibly print "Remote I/O error". You are not running the code you think you are running.

By the way, there is no point printing the file descriptor if open() fails. It will be -1. What you do want to print is the error message based on the value of errno:

Code: Select all

  if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) {
	perror("Error: Couldn't open device!");
	return 1;
  }
Maybe you did already know that, which would help to explain how you get ": Remote I/O error" messages appended to your own. Or again maybe you are not running the code you think you are.

I cannot comment on i2c usage. You have not even indicated what type of chip you are talking to.

LdB
Posts: 1518
Joined: Wed Dec 07, 2016 2:29 pm

Re: ioctl

Tue Feb 04, 2020 1:43 pm

jojopi wrote: The code you listed (and the shell you ran it from) cannot possibly print "Remote I/O error". You are not running the code you think you are
Incorrect the kernel prints that error when you try to IOControl a none existent device ... his code is being executed and that is the error message the kernel spits to the console which is by default STDERR output. You are just adding confusion in and actually with that error I know his code is working correctly.

If he does a i2cdetect like he was supposed to when he followed the instructions it will look like this
i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
That is my machine because I have no I2c device and pretty sure that is what his will look like.

So basically it is likely the device isn't connected properly and while it looks like that if he tries to IOControl he will get that exact message because that is what the kernel will spit out via STDERR.

So thispi4me until the device shows in the i2cdetect please don't waste time with C code there is nothing you can do with the C code to fix that ... you have a HARDWARE ISSUE. So probably start by telling us device and what you have connected Pi Model and pins as well please.

User avatar
jojopi
Posts: 3190
Joined: Tue Oct 11, 2011 8:38 pm

Re: ioctl

Tue Feb 04, 2020 3:56 pm

LdB wrote:
Tue Feb 04, 2020 1:43 pm
Incorrect the kernel prints that error when you try to IOControl a none existent device ... his code is being executed and that is the error message the kernel spits to the console which is by default STDERR output.
No, that is not how Linux works at all. The kernel never writes to stderr except when userspace does write(2, …), etc.

If a system call fails, Linux returns a negative error number such as -EREMOTEIO. The C library copies the positive version of the error code into the current thread's errno variable and returns -1, because that is the calling convention.

No error message will ever be printed unless the user code does something to consult errno and look up the translated message according to the current locale. The code in the OP never does that, so it cannot print "Remote I/O error".

Since we never see the intended message "Error: Couldn't find device on address!" either, we know that the ioctl() is in fact succeeding. That makes sense, because this particular request cannot fail with EREMOTEIO; it can only fail with EBUSY or EINVAL.

It is a different or subsequent, unposted piece of code that is actually failing, and does have a call to perror(), but with an empty message; hence the leading colon-space on the error. We need to call perror() after every failed system call, and to include a sufficient message to make it clear where we are.

Of course you are quite correct about the meaning of the Remote I/O error, but you had already addressed that.

thispi4me
Posts: 16
Joined: Mon Jan 20, 2020 12:23 am

Re: ioctl

Tue Feb 04, 2020 4:24 pm

i2cdetect does show the slave address. The program works as expected but I purposely put in the wrong address for testing. That is when I get: ( : Remote I/O error)

[email protected]:/usr $ ./testc1
passed open
passed ioctl
Write to register error

: Remote I/O error

I was only trying to further understand the code.

Code: Select all

#include <stdio.h>
#include <sys/types.h> // open
#include <sys/stat.h>  // open
#include <fcntl.h>     // open
#include <unistd.h>    // read/write usleep
#include <stdlib.h>    // exit
#include <inttypes.h>  // uint8_t, etc
#include <linux/i2c-dev.h> // I2C bus definitions
////////////////////////////////////////////////
// main
int main() {
  int fd;
  int ads_address = 0x18;
  uint8_t buf[10];
  int16_t val;

  // open device on /dev/i2c-1 
  if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) 
  {
	printf("Error: Couldn't open device! %d\n", fd);
	return 1;
  }
  printf("passed open\n");

  if (ioctl(fd, I2C_SLAVE, ads_address) < 0)  // ads1115  
  {
	printf("Error: Couldn't find device on address!\n");
	return 1;
  }
  printf("passed ioctl\n");
   
  
  ///////////////////////////////
  // set config register and start conversion
  // AIN0 and GND, 4.096v, 128s/s
  buf[0] = 1;    // config register is 1
  buf[1] = 0xc3;
  buf[2] = 0x85;
  if (write(fd, buf, 3) != 3) 
  {
	perror("Write to register error\n");
	exit(-1);
  }
  //////////////////////////////
  // wait for conversion complete
  do {
	if (read(fd, buf, 2) != 2) 
        {
	  perror("Read conversion error\n");
	  exit(-1);
	}
  } while (!(buf[0] & 0x80)); //Bit 15 goes hi when done.
  //////////////////////////////
  // read conversion register
  buf[0] = 0;   // conversion register is 0
  if (write(fd, buf, 1) != 1) 
  {
	perror("Write register select error");
	exit(-1);
  }
  if (read(fd, buf, 2) != 2) {
	perror("Read conversion error");
	exit(-1);
  }
  //////////////////////////////
  // convert output and display results
  val = (int16_t)buf[0]*256 + (uint16_t)buf[1];
  printf("Conversion %02x%02x %d %f\n",buf[0],buf[1],val,(float)val*4.096/32768);
//*/
  close(fd);

  return 0;
    
I ordered a copy of
"The Linux Programming Interface: A Linux and UNIX System Programming Handbook "

I hope this clears up many of my questions.
Thanks for the replies!

thispi4me
Posts: 16
Joined: Mon Jan 20, 2020 12:23 am

Re: ioctl

Tue Feb 04, 2020 4:40 pm

My first posted question was really two fold.
First I didn't understand the :remote I/O error (Thanks guys for clearing that up.)
Secondly where is the ioctl.h file? I have completely searched my PI's memory with no luck.

User avatar
jojopi
Posts: 3190
Joined: Tue Oct 11, 2011 8:38 pm

Re: ioctl

Tue Feb 04, 2020 4:57 pm

thispi4me wrote:
Tue Feb 04, 2020 4:24 pm
Write to register error
: Remote I/O error
You need to remove the trailing newlines from your perror messages, so that the error descriptions go on the same line.

The I2C_SLAVE ioctl does not actually communicate with the remote chip. It just tells the kernel what address to use for subsequent reads and writes. So it is quite normal that it accepts an address with no chip present, and the code only crashes when you write.

However, the ioctl can fail with EINVAL if you pass an address that is out of range, or EBUSY if the chip at that address is under control of a kernel driver ("UU" in i2cdetect).

The full path of the header file is /usr/include/arm-linux-gnueabihf/sys/ioctl.h, and it includes a few others. But there is really nothing very interesting in them.

LdB
Posts: 1518
Joined: Wed Dec 07, 2016 2:29 pm

Re: ioctl

Tue Feb 04, 2020 5:18 pm

If you want to actually check for the device before you ever write you can see what i2cdetect does in the source code
https://kernel.googlesource.com/pub/scm ... 2cdetect.c


So this is what it does to detect a device

Code: Select all

/* Set slave address */
if (ioctl(file, I2C_SLAVE, i+j) < 0) {
	if (errno == EBUSY) {
		printf("UU ");
		continue;
	} else {
		fprintf(stderr, "Error: Could not set "
		"address to 0x%02x: %s\n", i+j,
			strerror(errno));
		return -1;
	}
}
So it tries to set the slave address if it fails it checks the "errno" and if it's EBUSY then there is no device, anything else means there is something there but it failed.

So you can basically do that if you want to safety up your code.

As JoJoPi said the C interface file is just a shim onto the /dev/i2c device
If you want to really see what is happening with the I2C you need to look at the Raspbian driver
https://github.com/raspberrypi/linux/tr ... rivers/i2c

This is probably the detail you are interested in and it describes what is in /dev/i2c
https://github.com/raspberrypi/linux/bl ... -interface

Final comment is she is a very slow device so if you are doing anything serious you will want to put it in a thread with a semaphore lock if you are going to call it from multiple other threads.

thispi4me
Posts: 16
Joined: Mon Jan 20, 2020 12:23 am

Re: ioctl

Tue Feb 04, 2020 6:17 pm

SWEET!

Thanks Guys that helps a lot!!!

Return to “C/C++”