Search code examples
raspberry-picommon-lispi2csbclioctl

Trying to select an i2c address throws ioctl failed: invalid argument


I am building a program in Common Lisp, which communicates via i2c to a pca9685 using sysfs on a raspberry pi 3B+.

When initializing the device, I need to select the pca by using (ioctl fd +i2c-slave+ addr) where +i2c-slave+ = xf0703 (command for selecting slave) and addr = 0x40 (for pca9685)

However from the call I get an error:

IOCTL 1795 failed: Invalid argument
[Condition of type SIMPLE-ERROR]
0: ((SETF I2C::IOCTL) 64 10 1795)
1: (I2C:I2C-START #S(I2C:I2C-DEV :FILENAME "/dev/i2c-1" :DEV 1 :ADDR 64))

I am using the ioctl implementation from cl-spidev.

Since I am on sbcl, the relevant function is

(defun (setf ioctl) (arg fd cmd)
  (sb-alien:with-alien ((value sb-alien:int))
    (setf value arg)
    (multiple-value-bind (wonp error)
        (sb-unix:unix-ioctl (stream-fd fd)
                            (if (< cmd (expt 2 31)) cmd (- cmd (expt 2 32)))
                            (sb-alien:alien-sap (sb-alien:addr value)))
      (unless wonp
        (error "IOCTL ~a failed: ~a" cmd (sb-impl::strerror error))))
    arg))

and it is called like this

(setf (ioctl fd cl-i2c-lli:+i2c-slave+) addr)

My current guess is that the ioctl function is passing a pointer, while it should be a plain long (according to the docs), but still am unable to wrap my head around it completely. Am I missing something obvious?

The Adafruit python library, and some example C implementations work in the same way.

Notes

  • Also tried with I2C_SLAVE_FORCE, and got the same result.
  • How to pass the integer to the foreign call as a value? Is it possible?
  • i2cdetect confirms that there is a device on #x40, successfully read some registers with i2cget.

Solution

  • Your supposition is correct, this wrapper function that you are using calls ioctl with a pointer to your value instead of the value.

    Have you tried simply (sb-unix:unix-ioctl fd cl-i2c-lli:+i2c-slave+ addr)?