Open tykling opened 3 years ago
Hi @tykling. Pardon my lack of experience with FreeBSD.
Let's leave SMBus support out of the discussion for now and concentrate on the pure i2c capabilities. The smbus2 lib as well as the example you linked to communicate in similar ways over i2c:
ioctl
.ioctl
are the same:
class i2c_msg
in smbus2 is identical to struct iic_msg
.class i2c_rdwr_ioctl_data
in smbus is identical to struct iic_rdwr_data
.That indicated to me that it should be possible to smbus2's i2c_rdwr
right out of the box.... You know, the examples shown in the I2C section in the readme. Have you tested those?
Hello :) Thank you for the input, it is much appreciated!
I've been trying to make this work. My i2c device is on address 48 and working well:
[root@pizero1 ~]# i2c -sv
dev: /dev/iic0, addr: 0x0, r/w: r, offset: 0x00, width: 8, count: 1
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic0: 48 60
[root@pizero1 ~]# i2c -a 48 -d r -c 2 -m tr
0c 19
[root@pizero1 ~]#
I am using the first i2c example from the README:
[root@pizero1 ~/smbus2]# cat i2ctest.py
from smbus2 import SMBus, i2c_msg
with SMBus(1) as bus:
msg = i2c_msg.read(48, 2)
bus.i2c_rdwr(msg)
[root@pizero1 ~/smbus2]#
With no changes to smbus2 I get the following error - which is understandable since it is using a Linux ioctl:
(smbus2_venv) [root@pizero1 ~/smbus2]# python i2ctest.py
Traceback (most recent call last):
File "i2ctest.py", line 5, in <module>
bus.i2c_rdwr(msg)
File "/root/smbus2/smbus2/smbus2.py", line 663, in i2c_rdwr
ioctl(self.fd, I2C_RDWR, ioctl_data)
OSError: [Errno 25] Inappropriate ioctl for device
(smbus2_venv) [root@pizero1 ~/smbus2]#
It took me a while to resolve the correct ioctl macro I2CRDWR on FreeBSD - finally found the integer with help from a friend and this little program:
[root@pizero1 ~]# cat resolvemacro.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef unsigned char u_char;
#include <dev/iicbus/iic.h>
int main(int argc, char *argv[])
{
printf("0x%lx\n", I2CRDWR);
return EXIT_SUCCESS;
}
[root@pizero1 ~]# cc -Wall -O2 resolvemacro.c
[root@pizero1 ~]# ./a.out
0x80086906
[root@pizero1 ~]#
After putting the value 0x80086906 in this line: https://github.com/kplindegaard/smbus2/blob/master/smbus2/smbus2.py#L33 the error changes, so I think the ioctl is correct:
(smbus2_venv) [root@pizero1 ~/smbus2]# python i2ctest.py
Traceback (most recent call last):
File "i2ctest.py", line 5, in <module>
bus.i2c_rdwr(msg)
File "/root/smbus2/smbus2/smbus2.py", line 663, in i2c_rdwr
ioctl(self.fd, I2C_RDWR, ioctl_data)
OSError: [Errno 5] Input/output error
(smbus2_venv) [root@pizero1 ~/smbus2]#
Something is still wrong though.
This is how far I got in the weekend. I will keep at it, but if you have any ideas I am all ears :)
Thanks! :+1:
OK, playing a bit more with it, I appear to have discovered that it does work, but on some unexpected addresses. Changing my test script to loop over 0-1024 and reading from each turns up my sensor which has i2c address 0x48, but it responds on 144 and 145, and again on 400 and 401, and again on 656 and 657 and so on.
The sensor which has i2c address 0x60 responds on 192 and 193, and again on 448 and 449, and so on.
(smbus2_venv) [root@pizero1 ~/smbus2]# cat i2ctest.py
from smbus2 import SMBus, i2c_msg
with SMBus("/dev/iic0") as bus:
for i in range(0,1024):
msg = i2c_msg.read(i, 2)
try:
bus.i2c_rdwr(msg)
print(f"response from {i}")
for value in msg:
print(value)
except:
#print("fail")
pass
(smbus2_venv) [root@pizero1 ~/smbus2]#
(smbus2_venv) [root@pizero1 ~/smbus2]# python i2ctest.py
response from 144
9
198
response from 145
9
198
response from 192
0
0
response from 193
0
0
response from 400
9
198
response from 401
9
198
response from 448
0
0
response from 449
0
0
response from 656
9
198
response from 657
9
198
response from 704
0
0
response from 705
0
0
response from 912
9
199
response from 913
9
199
response from 960
0
0
response from 961
0
0
(smbus2_venv) [root@pizero1 ~/smbus2]#
Great, finally some progress \o/ :+1: Can you spot why:
Thanks again!
Is this somehow related to the read/write bit being "counted"?
Yes, you're not supposed to pass raw addresses anywhere, the kernel always works with (addr << 1) | rw
. See iic(4).
It's a bit confusing that you mixed hex and dec in the comment ;) but yeah, 0x60 << 1 == 192
. With your code how it is, it essentially takes (addr << 1) | rw
from the user instead of taking addr
.
BTW, SMBus is a completely separate API on completely separate devices, see smb(4). The iicsmb
driver can do SMBus on top of I2C controllers, but note that there is no correlation guaranteed between /dev/iicN
and /dev/smbN
device numbers. So the Linux thing of supporting both I2C and SMBus API on the same /dev/i2c-N
device won't work :/
Hello :) I haven't forgotten or given up on this. I took over my new house on february 15th and haven't had much time since. I will get back to it asap!
@tykling I hope you are enjoying the new house :) I am wondering if you have had a chance to get back to this as I am interested as well.
As I was missing this as well and had some spare time, I've been so bold and created a PR implementing support for FreeBSD. Tested on a Raspberry PI 3B. Have fun!
Thank you @0xc0decafe! #81
Hello :)
I would love to see FreeBSD support in smbus2 so Python i2c stuff would be easier in FreeBSD.
Before I do too much I wanted to ask if you are interested in accepting a PR for FreeBSD support (if I get that far)?
I've dug up https://github.com/freebsd/freebsd-src/blob/main/sys/dev/iicbus/iic.h which is used by https://github.com/freebsd/freebsd-src/blob/098dbd7ff7f3da9dda03802cdb2d8755f816eada/usr.sbin/i2c/i2c.c - the latter works well for communicating with i2c devices from the console. So I think the basics are in place.
I am not sure how to continue from here though, any pointers would be much appreciated! My ultimate goal is getting FreeBSD support in the Qwiic_py package for SparkFuns QWIIC ecosystem of sensors.
Thank you! :+1: