TIP: Click on subject to list as thread! ANSI
echo: rberrypi
to: ALL
from: DENNIS LEE BIEBER
date: 2019-11-24 18:01:00
subject: Re: One I2C bus, two prog

On Sun, 24 Nov 2019 19:43:02 +0100, "R.Wieser" 
declaimed the following:


>
>But that raises the question about what I should think about whats in the
>third link you posted (i2c-concurrent-access-on-linux-mutex), which seems to
>indicate its present - and on a low level too.
>
 See below...

>
>That is the problem I tried to explain in my "lightbulb moment" paragraph.
>Although I referred to the I2C signals themselves (the "start" and "stop"
>ones) I also assumed that those signals would coincide with the locking and
>unlocking of the I2C bus (or the other way around).
>
>In other words, I was coining up the possibility of an atomic write-read
>sequence (by not ending the write sequence with a "stop" condition, but
>sending another "start" instead).    It would solve most, if not all of the
>interference problems.
>
>The only question is if that has already been implemented, or if I need
>to/can do it myself.
>

https://www.kernel.org/doc/html/v4.14/driver-api/i2c.html
"""
Note about combined messages: Some I2C controllers can only send one
message per transfer, plus something called combined message or
write-then-read. This is (usually) a small write message followed by a read
message and barely enough to access register based devices like EEPROMs.
There is a flag to support this mode. It implies max_num_msg = 2 and does
the length checks with max_comb_*_len because combined message mode usually
has its own limitations. Because of HW implementations, some controllers
can actually do write-then-anything or other variants. To support that,
write-then-read has been broken out into smaller bits like write-first and
read-second which can be combined as needed.
"""
Emphasis:
 *** Some I2C controllers can only send one message per transfer ***

"""
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg * msgs, int num)

    execute a single or combined I2C message
"""

 So taking the two together -- it is possible that i2c_transfer()
handles each part as a separate transaction. But to determine that will
require looking at the kernel source code for the I2C on the target (R-Pi)
computer. If one is lucky, even if the messages are handled as separate
actual transactions the kernel may wrap all of them into one lock -- ie:
each API call gets wrapped in lock/unlock functions.

 That does mean you have to use that specific function if you need a
sequence of "write setup, read a {, read b, ...}", you can not use
i2c_master_send, i2c_master_recv, nor the various i2c_smbus_[read|write]
calls.

 NOTE there is also an
"""

int __i2c_transfer(struct i2c_adapter * adap, struct i2c_msg * msgs, int
num)

    unlocked flavor of i2c_transfer
"""
which goes on to state that "Adapter lock must be held when calling this
function." -- which again suggests that i2c_transfer() may internally lock.
{and so does the i2c-concurrent-access-on-linux-mutex stackflow commentary}

 Best guess, without checking source code, is that use of any read/write
API call has the risk of conflict, even if the read/write API in turn calls
i2c_transfer(), as the lock is at the single call level. ANY I2C device
that requires multiple write/read combinations is vulnerable unless one
builds the individual messages and directly invokes i2c_transfer()

 If you aren't using the low-level C API, you'll need to check the code
of the library you are using...

Python smbus library does not have a multi-message API.
https://raspberry-projects.com/pi/programming-in-python/i2c-programming-in-pyth
on/using-the-i2c-interface-2

Python smbus2 library https://smbus2.readthedocs.io/en/latest/ ,
OTOH, has
"""
 i2c_rdwr(*i2c_msgs)

    Combine a series of i2c read and write operations in a single
transaction (with repeated start bits but no stop bits in between).
"""
(but I can't figure out how to specify where the read data goes...)

"""
    @staticmethod
    def read(address, length):
        """
        Prepares an i2c read transaction.
        :param address: Slave address.
        :type: address: int
        :param length: Number of bytes to read.
        :type: length: int
        :return: New :py:class:`i2c_msg` instance for read operation.
        :rtype: :py:class:`i2c_msg`
        """
        arr = create_string_buffer(length)
        return i2c_msg(
            addr=address, flags=I2C_M_RD, len=length,
            buf=arr)
"""
 Let's see -- it creates a buffer "arr" and specifies that buffer when
creating the msg instance. My best guess is that AFTER i2c_rdwr() one has
to loop through each of the i2c_msgs (passed to i2c_rdwr()) looking for the
read message(s) (so, most likely skipping the first which sets up the
device), and extracting the buffer field (which is a CTYPES POINTER field
-- have to see how one converts from ctypes pointer to Python string).


 CircuitPython (Adafruit via blinka library) looks like they explicitly
lock (but again -- I think that lock is only relevant to a single process
that may be running multiple threads; not multiple processes).
https://learn.adafruit.com/circuitpython-basics-i2c-and-spi/i2c-devices
Using the busdevice add-on
https://circuitpython.readthedocs.io/projects/busdevice/en/latest/api.html#adaf
ruit_bus_device.i2c_device.I2CDevice
provides a write_then_readinto() function, but it is not clear if it
handles multiple reads at once (unless one read per byte of the input
buffer length).

"""
        if hasattr(self.i2c, 'writeto_then_readfrom'):
            # In linux, at least, this is a special kernel function call
            self.i2c.writeto_then_readfrom(self.device_address, out_buffer,
in_buffer,
                                           out_start=out_start,
out_end=out_end,
                                           in_start=in_start,
in_end=in_end)

        else:
            # If we don't have a special implementation, we can fake it
with two calls
            self.write(out_buffer, start=out_start, end=out_end,
stop=False)
            self.readinto(in_buffer, start=in_start, end=in_end)
"""


 And just to be annoying:
https://www.raspberrypi.org/forums/viewtopic.php?t=135928


--
 Wulfraed                 Dennis Lee Bieber         AF6VN
 wlfraed@ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/

--- SoupGate-Win32 v1.05
* Origin: Agency HUB, Dunedin - New Zealand | FidoUsenet Gateway (3:770/3)

SOURCE: echomail via QWK@docsplace.org

Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.