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)
|