On Nanopi NEO (Allwinner H3), accessing I2C EEPROM with 16-bit addressing width panics the OS. This seems working on RPI. Same issue experienced on: FreeBSD 12.2 Release FreeBSD 13.0 Release FreeBSD 13.0 Stable root@nanopi-neo:~ # uname -a FreeBSD nanopi-neo 14.0-CURRENT FreeBSD 14.0-CURRENT #1 main-n249946-824bbb9a408-dirty: Fri Oct 8 05:34:21 PST 2021 root@media.klyren.net:/zdata0/work/obj/zdata0/src-14-CURRENT/arm.armv7/sys/GENERIC arm root@nanopi-neo:~ # dmesg |grep ii iichb0: <Allwinner Integrated I2C Bus Controller> mem 0x1c2ac00-0x1c2afff irq 40 on simplebus0 iicbus0: <OFW I2C bus> on iichb0 miibus0: <MII bus> on awg0 ukphy0: <Generic IEEE 802.3u media interface> PHY 0 on miibus0 ukphy1: <Generic IEEE 802.3u media interface> PHY 1 on miibus0 iic0: <I2C generic I/O> on iicbus0 root@nanopi-neo:~ # i2c -s -f /dev/iic0 57 68 root@nanopi-neo:~ # i2c -a 0x57 -f /dev/iic0 -d r -o 1 -w 16 -c 1 -m tr panic: Assertion strlen(description) < MAX_W_NAME failed at /zdata0/src-14-CURRENT/sys/kern/subr_witness.c:1914 ðñòóôõö÷øùúûüýþÿtime = 1633646823 KDB: stack backtrace: db_trace_self() at db_trace_self pc = 0xc05b2e24 lr = 0xc007b07c (db_trace_self_wrapper+0x30) sp = 0xd89e8928 fp = 0xd89e8a40 db_trace_self_wrapper() at db_trace_self_wrapper+0x30 pc = 0xc007b07c lr = 0xc02dddbc (vpanic+0x17c) sp = 0xd89e8a48 fp = 0xd89e8a68 r4 = 0x00000100 r5 = 0x00000000 r6 = 0xc0708d0c r7 = 0xc08e8aa8 vpanic() at vpanic+0x17c pc = 0xc02dddbc lr = 0xc02ddb60 (doadump) sp = 0xd89e8a70 fp = 0xd89e8a74 r4 = 0xc10a0878 r5 = 0xc10a0830 r6 = 0xc08be7bc r7 = 0xc079bd0e r8 = 0x0000019e r9 = 0x0000019d r10 = 0xc0ad95f0 doadump() at doadump pc = 0xc02ddb60 lr = 0xc034f3b4 (enroll+0x540) sp = 0xd89e8a7c fp = 0xd89e8aa8 r4 = 0xc0ad95f0 r5 = 0xd89e8a74 r6 = 0xc02ddb60 r10 = 0xd89e8a7c enroll() at enroll+0x540 pc = 0xc034f3b4 lr = 0xc034f878 (witness_init+0xb0) sp = 0xd89e8ab0 fp = 0xd89e8ab8 r4 = 0xd2caffa0 r5 = 0x00000000 r6 = 0xd8f2b754 r7 = 0xc00b1074 r8 = 0xd8cc17c0 r9 = 0xc0889ed0 r10 = 0xd2e0e040 witness_init() at witness_init+0xb0 pc = 0xc034f878 lr = 0xc02e7b04 (sx_init_flags+0x88) sp = 0xd89e8ac0 fp = 0xd89e8ad0 r4 = 0xd2caffa0 r10 = 0xd2e0e040 sx_init_flags() at sx_init_flags+0x88 pc = 0xc02e7b04 lr = 0xc00b10b8 (iicopen+0x44) sp = 0xd89e8ad8 fp = 0xd89e8ae0 r4 = 0xd2caffa0 r5 = 0xd2a4ce00 iicopen() at iicopen+0x44 pc = 0xc00b10b8 lr = 0xc01d8784 (devfs_open+0x12c) sp = 0xd89e8ae8 fp = 0xd89e8b20 r4 = 0xd2a4ce00 r5 = 0x00000000 devfs_open() at devfs_open+0x12c pc = 0xc01d8784 lr = 0xc06a5324 (VOP_OPEN_APV+0x50) sp = 0xd89e8b28 fp = 0xd89e8b40 r4 = 0xd89e8b50 r5 = 0xc08a9fd4 r6 = 0xc09138c5 r7 = 0x00000003 r8 = 0xd8f71e00 r9 = 0xd89e8b50 r10 = 0xd8cc17c0 VOP_OPEN_APV() at VOP_OPEN_APV+0x50 pc = 0xc06a5324 lr = 0xc03e0dec (vn_open_vnode+0x184) sp = 0xd89e8b48 fp = 0xd89e8ba8 r4 = 0xd8f2b754 r5 = 0x00000000 r6 = 0xd2e0e040 r10 = 0xd8cc17c0 vn_open_vnode() at vn_open_vnode+0x184 pc = 0xc03e0dec lr = 0xc03e0828 (vn_open_cred+0x578) sp = 0xd89e8bb0 fp = 0xd89e8cc0 r4 = 0x00000001 r5 = 0xd89e8cf8 r6 = 0x00000003 r7 = 0xd89e8d58 r8 = 0xd89e8dc0 r9 = 0x00000000 r10 = 0x00000000 vn_open_cred() at vn_open_cred+0x578 pc = 0xc03e0828 lr = 0xc03e02a8 (vn_open+0x24) sp = 0xd89e8cc8 fp = 0xd89e8cd0 r4 = 0xd8cc17c0 r5 = 0xd89e8cf8 r6 = 0x00000000 r7 = 0xd89e8cf8 r8 = 0xffffff9c r9 = 0x00000012 r10 = 0xbfbfee4b vn_open() at vn_open+0x24 pc = 0xc03e02a8 lr = 0xc03d6f30 (kern_openat+0x258) sp = 0xd89e8cd8 fp = 0xd89e8db8 kern_openat() at kern_openat+0x258 pc = 0xc03d6f30 lr = 0xc03d71b8 (sys_openat+0x2c) sp = 0xd89e8dc0 fp = 0xd89e8dc8 r4 = 0xd8cc17c0 r5 = 0x00000001 r6 = 0xc08b42bc r7 = 0x00000000 r8 = 0x00000000 r9 = 0xd8cc1a68 r10 = 0xd8cc0530 sys_openat() at sys_openat+0x2c pc = 0xc03d71b8 lr = 0xc05d5128 (swi_handler+0x15c) sp = 0xd89e8dd0 fp = 0xd89e8e40 swi_handler() at swi_handler+0x15c pc = 0xc05d5128 lr = 0xc05b574c (swi_exit) sp = 0xd89e8e48 fp = 0xbfbfe780 r4 = 0x201f8a44 r5 = 0x00044190 r6 = 0xbfbfec98 r7 = 0x000001f3 r8 = 0x00000001 r9 = 0x00010b92 r10 = 0x00000000 swi_exit() at swi_exit pc = 0xc05b574c lr = 0xc05b574c (swi_exit) sp = 0xd89e8e48 fp = 0xbfbfe780 KDB: enter: panic [ thread pid 985 tid 100114 ] Stopped at kdb_enter+0x58: ldrb r15, [r15, r15, ror r15]! db> ps pid ppid pgrp uid state wmesg wchan cmd 985 972 985 0 R+ CPU 1 i2c 972 971 972 0 S+ pause 0xd8cc0928 csh 971 1 971 0 Ss+ wait 0xd2dacc40 login 952 1 952 0 Ss select 0xd2c954a4 sshd 925 1 925 0 Ss nanslp 0xc0ac3d8e cron 914 1 914 123 Ss (threaded) ntpd 100117 S select 0xd2ccb224 ntpd 100122 S usem 0xd2dc5e80 ntpd 870 1 870 0 Ss select 0xd2cb2664 syslogd 682 1 682 0 Ss select 0xd2cb2524 devd 284 1 284 0 Ss select 0xd2cb2624 wpa_supplicant 23 0 0 0 DL mmcsd d 0xd2ccce00 [mmcsd0: mmc/sd card] 22 0 0 0 DL - 0xc0b31af0 [soaiod4] 21 0 0 0 DL - 0xc0b31af0 [soaiod3] 20 0 0 0 DL - 0xc0b31af0 [soaiod2] 19 0 0 0 DL - 0xc0b31af0 [soaiod1] 18 0 0 0 DL syncer 0xc0b32960 [syncer] 17 0 0 0 DL vlruwt 0xd173d388 [vnlru] 16 0 0 0 DL (threaded) [bufdaemon] 100084 D qsleep 0xc0b31fdc [bufdaemon] 100087 D - 0xc0915700 [bufspacedaemon-0] 100104 D sdflush 0xd2d98c84 [/ worker] 15 0 0 0 DL psleep 0xc0b36930 [vmdaemon] 9 0 0 0 DL (threaded) [pagedaemon] 100082 D psleep 0xc0b36290 [dom0] 100092 D launds 0xc0b3629c [laundry: dom0] 100093 D umarcl 0xc056eeb4 [uma] 8 0 0 0 DL - 0xc0939e18 [rand_harvestq] 14 0 0 0 DL (threaded) [usb] 100046 D - 0xd280400c [usbus0] 100047 D - 0xd280403c [usbus0] 100048 D - 0xd280406c [usbus0] 100049 D - 0xd280409c [usbus0] 100050 D - 0xd28040cc [usbus0] 100052 D - 0xd17a6c84 [usbus1] 100053 D - 0xd17a6cb4 [usbus1] 100054 D - 0xd17a6ce4 [usbus1] 100055 D - 0xd17a6d14 [usbus1] 100056 D - 0xd17a6d44 [usbus1] 100058 D - 0xd280daac [usbus2] 100059 D - 0xd280dadc [usbus2] 100060 D - 0xd280db0c [usbus2] 100061 D - 0xd280db3c [usbus2] 100062 D - 0xd280db6c [usbus2] 100064 D - 0xd2838c84 [usbus3] 100065 D - 0xd2838cb4 [usbus3] 100066 D - 0xd2838ce4 [usbus3] 100067 D - 0xd2838d14 [usbus3] 100068 D - 0xd2838d44 [usbus3] 100070 D - 0xd284aaac [usbus4] 100071 D - 0xd284aadc [usbus4] 100072 D - 0xd284ab0c [usbus4] 100073 D - 0xd284ab3c [usbus4] 100074 D - 0xd284ab6c [usbus4] 7 0 0 0 DL (threaded) [cam] 100039 D - 0xc09361c0 [doneq0] 100040 D - 0xc0936140 [async] 100080 D - 0xc093606c [scanner] 6 0 0 0 DL crypto_ 0xc3bbfdac [crypto returns 3] 5 0 0 0 DL crypto_ 0xc3bbfd7c [crypto returns 2] 4 0 0 0 DL crypto_ 0xc3bbfd4c [crypto returns 1] 3 0 0 0 DL crypto_ 0xc3bbfd1c [crypto returns 0] 2 0 0 0 DL crypto_ 0xc0b352ec [crypto] 13 0 0 0 DL seqstat 0xd0495dcc [sequencer 00] 12 0 0 0 DL (threaded) [geom] 100026 D - 0xc0ab2d38 [g_event] 100027 D - 0xc0ab2d3c [g_up] 100028 D - 0xc0ab2d40 [g_down] 11 0 0 0 WL (threaded) [intr] 100010 I [swi6: Giant taskq] 100016 I [swi5: fast taskq] 100018 I [swi6: task queue] 100019 I [swi1: netisr 0] 100020 I [swi4: clock (0)] 100021 I [swi4: clock (1)] 100022 I [swi4: clock (2)] 100023 I [swi4: clock (3)] 100024 I [swi3: vm] 100041 I [gic0,s11: gpio0] 100042 I [gic0,s45: gpio1] 100043 I [gic0,s50: a31dmac0] 100044 I [gic0,s60: aw_mmc0] 100045 I [gic0,s71: musbotg0] 100051 I [gic0,s72: ehci0] 100057 I [gic0,s73: ohci0] 100063 I [gic0,s78: ehci1] 100069 I [gic0,s79: ohci1] 100075 I [gic0,s82: awg0] 100076 I [swi0: uart] 100077 I [gic0,s31:-_thermal0] 100081 I [gic0,s6: iichb0] 10 0 0 0 RL (threaded) [idle] 100002 Run CPU 0 [idle: cpu0] 100003 CanRun [idle: cpu1] 100004 Run CPU 2 [idle: cpu2] 100005 Run CPU 3 [idle: cpu3] 1 0 1 0 SLs wait 0xc29fdc40 [init] 0 0 0 0 DLs (threaded) [kernel] 100000 D swapin 0xc0ab31c0 [swapper] 100006 D - 0xc3bc0400 [softirq_0] 100007 D - 0xc3bc0300 [softirq_1] 100008 D - 0xc3bc0200 [softirq_2] 100009 D - 0xc3bc0100 [softirq_3] 100011 D - 0xd0453e00 [in6m_free taskq] 100012 D - 0xd0453d00 [thread taskq] 100013 D - 0xd0453c00 [aiod_kick taskq] 100014 D - 0xd0453b00 [deferred_unmount ta] 100015 D - 0xd0453a00 [inm_free taskq] 100017 D - 0xd0453800 [kqueue_ctx taskq] 100025 D - 0xd0453600 [firmware taskq] 100030 D - 0xd0453500 [crypto_0] 100031 D - 0xd0453500 [crypto_1] 100032 D - 0xd0453500 [crypto_2] 100033 D - 0xd0453500 [crypto_3] 100079 D - 0xd173fa00 [CAM taskq] 100095 D - 0xd2cdce00 [rtwn0 net80211 task] db>
^Triage: Set Version to earliest confirmed (12.2-R)
4 byte constant string "iic" did not pass length check for 64 byte buffer. That is strange. You definitely have some sort of memory corruption. Through local source modification, out of buffer write or hardware failure.
Created attachment 228624 [details] twsi.c driver
Comment on attachment 228624 [details] twsi.c driver Seems the driver does not handle 16bit wide addressing properly. I made some modification. Looks to be working. root@nanopi-neo:~/prog/I2C # ./i2c -s -f /dev/iic0 Scanning I2C devices on /dev/iic0: 57 68 root@nanopi-neo:~/prog/I2C # ./i2c -a 0x57 -f /dev/iic0 -d r -o 0 -w 16 -c 16 -m tr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f root@nanopi-neo:~/prog/I2C # echo -n "test" | ./i2c -a 0x57 -f /dev/iic0 -d w -o 0x6 -w 16 -c 4 -m tr root@nanopi-neo:~/prog/I2C # ./i2c -a 0x57 -f /dev/iic0 -d r -o 0 -w 16 -c 16 -m tr 00 01 02 03 04 05 74 65 73 74 0a 0b 0c 0d 0e 0f root@nanopi-neo:~/prog/I2C # date;./ds1307 -s;./ds1307 -r Tue Oct 12 19:45:35 PST 2021 19:45:35 12/10/2021 root@nanopi-neo:~/prog/I2C # ./i2c -a 0x68 -f /dev/iic0 -d r -o 0 -w 8 -c 1 -m tr 15 root@nanopi-neo:~/prog/I2C # ./i2c -a 0x68 -f /dev/iic0 -d r -o 0 -w 8 -c 1 -m tr 16
on i2c(8), i also adjusted the parsing of offset. Seems LSB is being sent first. I need MSB first. off.off16 = (((uint16_t)i2c_opt.off << 8) & 0xff00) | (((uint16_t)i2c_opt.off >> 8) & 0x00ff);
Could upload it as a diff against base version rather than entire content
(In reply to Vladimir Kondratyev from comment #6) Yeah! Sorry, this is my first. :) Will do next time. Thank you!
Created attachment 228827 [details] Added some improvements. root@nanopi-neo:~/i2c # i2c -s /dev/iic0 Scanning I2C devices on /dev/iic0: 40 57 68 [8bit single write] root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d w -o 0 -w 8 -c 1 -m tr < 00.hex root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d r -o 0 -w 8 -c 1 -m tr 00 [8bit single write] root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d w -o 0 -w 8 -c 1 -m tr < 20.hex root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d r -o 0 -w 8 -c 1 -m tr 20 [8bit multiple write] Clear Registers from offset 5 to 1f [27 bytes] root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d w -o 5 -w 8 -c 27 -m tr < null.txt [8bit multiple Read] Read Registers from offset 0 [48 bytes] root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d r -o 0 -w 8 -c 48 -m tr 20 13 44 4c 46 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 10 00 00 00 10 00 00 00 10 00 00 00 10 00 00 [8bit multiple write] Fill Register from offset 5 to 1f [27 bytes] root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d w -o 5 -w 8 -c 27 -m tr < random.txt root@nanopi-neo:~/i2c # i2c -a 40 -f /dev/iic0 -d r -o 0 -w 8 -c 48 -m tr 20 13 44 4c 46 7e 77 14 67 01 58 1c 2b 06 0e 17 69 10 1e 1f 2e 00 c6 16 0c 04 a2 04 3c 06 1e 11 ff 10 00 00 00 10 00 00 00 10 00 00 00 10 00 00 [16bit write] Clear eeprom data from offset 0 to 1F [32 bytes] root@nanopi-neo:~/i2c # i2c -a 57 -f /dev/iic0 -d w -o 0 -w 16 -c 32 -m tr < null.txt [16bit Read] Read eeprom data from offset 0 [48 bytes] root@nanopi-neo:~/i2c # i2c -a 57 -f /dev/iic0 -d r -o 0 -w 16 -c 48 -m tr 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f8 0c 00 00 02 04 00 05 34 00 20 00 08 00 28 00 [16bit write] Fill eeprom from offset 0, 32bytes root@nanopi-neo:~/i2c # i2c -a 57 -f /dev/iic0 -d w -o 0 -w 16 -c 32 -m tr < random.txt root@nanopi-neo:~/i2c # i2c -a 57 -f /dev/iic0 -d r -o 0 -w 16 -c 48 -m tr 7e 77 b4 67 e1 58 1c 2b a6 0e d7 69 50 1e 9f 2e a0 c6 76 0c 84 a2 a4 3c 66 1e 51 b4 10 f1 d8 60 f8 0c 00 00 02 04 00 05 34 00 20 00 08 00 28 00
FWIW, I've been also working on twsi a little bit. I used a bunch of I2C peripherals and had to write new drivers for a couple of devices. I found that twsi was either too limiting or buggy with some valid protocol sequences needed for those peripherals. My work can be found here: https://github.com/avg-I/freebsd/commit/1c6777dc1d2ee5ba9e8ff887e9517c58340ce457 https://github.com/avg-I/freebsd/commit/35f319546b594a6094a86bcb18e415d4de2643e2 https://github.com/avg-I/freebsd/commit/5f2d7e7c13f53bfe51644fd0e1757623ca0d7990 https://github.com/avg-I/freebsd/commit/4fc6103770cf48b12c6de275b8c200df0080210a https://github.com/avg-I/freebsd/commit/0075e711e23cc20d7a4d31cf97648c20df8c9ef3 https://github.com/avg-I/freebsd/commit/937a86158404fe84d8135110e01dd60b1e6df81b
(In reply to Andriy Gapon from comment #9) Hi Andriy, Been searching a for a patch on twsi.c but couldn't find before. Thank you for sharing your works! I tested some of your commits, below are the results: 1c6777dc1d2ee5ba9e8ff887e9517c58340ce457 - 8bit single byte write not working - eeprom write not working 35f319546b594a6094a86bcb18e415d4de2643e2 -eeprom (16bit addressing) single byte read/write working -eeprom (16bit addressing) continues read/write working -8bit single byte read/write working -8bit continues read/write working 937a86158404fe84d8135110e01dd60b1e6df81b -can't write eeprom
(In reply to Andriy Gapon from comment #9) Hi Andriy, Could you handle this PR? I do not have any twsi(4) hardware or driver internals knowledge.
(In reply to Vladimir Kondratyev from comment #11) Yes, I am trying as much as my spare time allows.
(In reply to alydiomc from comment #10) Did you try each of those commits individually (in isolation) ? Or did you apply them sequentially (accumulated the changes) ? Or something else? Each commit has its own purpose as described in their commit messages.
(In reply to Andriy Gapon from comment #13) Hi Andriy, I downloaded the "raw" file individually and then recompiled the kernel. Regards, Alyd
(In reply to alydiomc from comment #14) So, you did all of them one by one in isolation from each other?
(In reply to Andriy Gapon from comment #15) Hi Andriy, Yes, I tested them separately but only the three commits below: 1c6777dc1d2ee5ba9e8ff887e9517c58340ce457 35f319546b594a6094a86bcb18e415d4de2643e2 937a86158404fe84d8135110e01dd60b1e6df81b Steps: 1. download raw file. 2. copied to ${SRCROOT}/sys/dev/iicbus/twsi/twsi.c 3. rebuild 4. test
A commit in branch main references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=04622a7f21157827fe75276a4520a52d3a571a86 commit 04622a7f21157827fe75276a4520a52d3a571a86 Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2021-11-26 08:07:27 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-11-26 14:17:24 +0000 twsi: move handling of TWSI_CONTROL_ACK into the state machine Previously the code set TWSI_CONTROL_ACK in twsi_transfer() based on whether the first message had a length of one. That was done regardless of whether the message was a read or write and what kind of messages followed it. Now the bit is set or cleared while handling TWSI_STATUS_ADDR_R_ACK state transition based on the current (read) message. The old code did not correctly work in a scenario where a single byte was read from an EEPROM device with two byte addressing. For example: i2c -m tr -a 0x50 -d r -w 16 -o 0 -c 1 -v The reason is that the first message (a write) has two bytes, so TWSI_CONTROL_ACK was set and never cleared. Since the controller did not send NACK the EEPROM sent more data resulting in a buffer overrun. While working on TWSI_STATUS_ADDR_R_ACK I also added support for the zero-length read access and then I did the same for zero-length write access. While rare, those types of I2C transactions are completely valid and are used by some devices. PR: 258994 MFC after: 3 weeks sys/dev/iicbus/twsi/twsi.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-)
A commit in branch main references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=ff1e8581806f70e54fecddf8a6a23488dc7b968a commit ff1e8581806f70e54fecddf8a6a23488dc7b968a Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2021-11-26 09:48:21 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-11-26 14:20:27 +0000 twsi: support more message combinations in transfers Most prominently, add support for a transfer where a write with no-stop flag is followed by a write with no-start flag. Logically, it's a single larger write, but consumers may want to split it like that because one part can be a register ID and the other part can be data to be written to (or starting at) that register. Such a transfer can be created by i2c tool and iic(4) driver, e.g., for an EEPROM write at specific offset: i2c -m tr -a 0x50 -d w -w 16 -o 0 -c 8 -v < /dev/random This should be fixed by new code that handles the end of data transfer for both reads and writes. It handles two existing conditions and one new. Namely: - the last message has been completed -- end of transfer; - a message has been completed and the next one requires the start condition; - a message has been completed and the next one should be sent without the start condition. In the last case we simply switch to the next message and start sending its data. Reads without the start condition are not supported yet, though. That's because we NACK the last byte of the previous message, so the device stops sending data. To fix this we will need to add a look-ahead at the next message when handling the penultimate byte of the current one. This change also fixed a bug where msg_idx was not incremented after a read message. Apparently, typically a read message is a last message in a transfer, so the bug did not cause much trouble. PR: 258994 MFC after: 3 weeks sys/dev/iicbus/twsi/twsi.c | 85 +++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 32 deletions(-)
A commit in branch stable/13 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=c7bf34cfe52c09abf813c784a60d7d9677afb603 commit c7bf34cfe52c09abf813c784a60d7d9677afb603 Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2021-11-26 08:07:27 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-12-17 07:30:41 +0000 twsi: move handling of TWSI_CONTROL_ACK into the state machine Previously the code set TWSI_CONTROL_ACK in twsi_transfer() based on whether the first message had a length of one. That was done regardless of whether the message was a read or write and what kind of messages followed it. Now the bit is set or cleared while handling TWSI_STATUS_ADDR_R_ACK state transition based on the current (read) message. The old code did not correctly work in a scenario where a single byte was read from an EEPROM device with two byte addressing. For example: i2c -m tr -a 0x50 -d r -w 16 -o 0 -c 1 -v The reason is that the first message (a write) has two bytes, so TWSI_CONTROL_ACK was set and never cleared. Since the controller did not send NACK the EEPROM sent more data resulting in a buffer overrun. While working on TWSI_STATUS_ADDR_R_ACK I also added support for the zero-length read access and then I did the same for zero-length write access. While rare, those types of I2C transactions are completely valid and are used by some devices. PR: 258994 (cherry picked from commit 04622a7f21157827fe75276a4520a52d3a571a86) sys/dev/iicbus/twsi/twsi.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-)
A commit in branch stable/13 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=6820d578bb1fb8d0a8e781a7f3d59311a35d453f commit 6820d578bb1fb8d0a8e781a7f3d59311a35d453f Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2021-11-26 09:48:21 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-12-17 07:30:58 +0000 twsi: support more message combinations in transfers Most prominently, add support for a transfer where a write with no-stop flag is followed by a write with no-start flag. Logically, it's a single larger write, but consumers may want to split it like that because one part can be a register ID and the other part can be data to be written to (or starting at) that register. Such a transfer can be created by i2c tool and iic(4) driver, e.g., for an EEPROM write at specific offset: i2c -m tr -a 0x50 -d w -w 16 -o 0 -c 8 -v < /dev/random This should be fixed by new code that handles the end of data transfer for both reads and writes. It handles two existing conditions and one new. Namely: - the last message has been completed -- end of transfer; - a message has been completed and the next one requires the start condition; - a message has been completed and the next one should be sent without the start condition. In the last case we simply switch to the next message and start sending its data. Reads without the start condition are not supported yet, though. That's because we NACK the last byte of the previous message, so the device stops sending data. To fix this we will need to add a look-ahead at the next message when handling the penultimate byte of the current one. This change also fixed a bug where msg_idx was not incremented after a read message. Apparently, typically a read message is a last message in a transfer, so the bug did not cause much trouble. PR: 258994 (cherry picked from commit ff1e8581806f70e54fecddf8a6a23488dc7b968a) sys/dev/iicbus/twsi/twsi.c | 85 +++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 32 deletions(-)
A commit in branch stable/12 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=074248b948b6fab3ec3a84e2573201fe82190cf3 commit 074248b948b6fab3ec3a84e2573201fe82190cf3 Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2021-11-26 08:07:27 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-12-17 07:31:21 +0000 twsi: move handling of TWSI_CONTROL_ACK into the state machine Previously the code set TWSI_CONTROL_ACK in twsi_transfer() based on whether the first message had a length of one. That was done regardless of whether the message was a read or write and what kind of messages followed it. Now the bit is set or cleared while handling TWSI_STATUS_ADDR_R_ACK state transition based on the current (read) message. The old code did not correctly work in a scenario where a single byte was read from an EEPROM device with two byte addressing. For example: i2c -m tr -a 0x50 -d r -w 16 -o 0 -c 1 -v The reason is that the first message (a write) has two bytes, so TWSI_CONTROL_ACK was set and never cleared. Since the controller did not send NACK the EEPROM sent more data resulting in a buffer overrun. While working on TWSI_STATUS_ADDR_R_ACK I also added support for the zero-length read access and then I did the same for zero-length write access. While rare, those types of I2C transactions are completely valid and are used by some devices. PR: 258994 (cherry picked from commit 04622a7f21157827fe75276a4520a52d3a571a86) sys/dev/iicbus/twsi/twsi.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-)
A commit in branch stable/12 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=e8dbe7bdcf5d9dfbe40b326001b7751e3d42c635 commit e8dbe7bdcf5d9dfbe40b326001b7751e3d42c635 Author: Andriy Gapon <avg@FreeBSD.org> AuthorDate: 2021-11-26 09:48:21 +0000 Commit: Andriy Gapon <avg@FreeBSD.org> CommitDate: 2021-12-17 07:31:39 +0000 twsi: support more message combinations in transfers Most prominently, add support for a transfer where a write with no-stop flag is followed by a write with no-start flag. Logically, it's a single larger write, but consumers may want to split it like that because one part can be a register ID and the other part can be data to be written to (or starting at) that register. Such a transfer can be created by i2c tool and iic(4) driver, e.g., for an EEPROM write at specific offset: i2c -m tr -a 0x50 -d w -w 16 -o 0 -c 8 -v < /dev/random This should be fixed by new code that handles the end of data transfer for both reads and writes. It handles two existing conditions and one new. Namely: - the last message has been completed -- end of transfer; - a message has been completed and the next one requires the start condition; - a message has been completed and the next one should be sent without the start condition. In the last case we simply switch to the next message and start sending its data. Reads without the start condition are not supported yet, though. That's because we NACK the last byte of the previous message, so the device stops sending data. To fix this we will need to add a look-ahead at the next message when handling the penultimate byte of the current one. This change also fixed a bug where msg_idx was not incremented after a read message. Apparently, typically a read message is a last message in a transfer, so the bug did not cause much trouble. PR: 258994 (cherry picked from commit ff1e8581806f70e54fecddf8a6a23488dc7b968a) sys/dev/iicbus/twsi/twsi.c | 85 +++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 32 deletions(-)
Should be fixed in the stable branches now. Please re-test.