Description
As far as I can tell*, the current implementation for SPIBus
and SPIDevice
via spidev
will assert CS around any of the SPIBus
methods like read()
and write()
, because that's what spidev
does for each transfer by default.
However, the SPIDevice::transaction()
method expects CS to be asserted once at the start, remain asserted for the duration of the closure, then become deasserted afterwards. Since the closure just calls those methods on SPIBus
, CS is toggled for every transfer inside the transaction, which will break drivers.
For example, if a driver has some function that's given a buffer of data to send, and needs to prepend a command byte:
fn write_data(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.spi.transaction(|bus| {
bus.write(&[0])?;
bus.write(data)
});
}
It wants to have CS assert, a 0 byte written, the data written, and then CS de-asserted, but instead CS is asserted/deasserted around both the one-byte write of 0 and the multi-byte write for data.
Ideally, the SPIBus
methods would not touch CS at all, and only transaction()
would somehow assert CS at the start and deassert it at the end. I'm not sure if the Linux spidev interface actually allows for such a thing though, and even if it did, the way it grants you a "bus" that's actually shared with other devices means we sort of break the HAL trait assumptions anyway.
I think using spidev
's transfer_multiple()
alongside the cs_change
setting it might be possible to do the right thing here, though I'm not completely sure how... if transfers were queued up only to be executed at the end of the transaction, using flush()
inside the transaction wouldn't work, or would still cause excess CS state changes. Still, it might be better than the current situation?
* Right now I can only test this on a remote device and infer what's not working by how a chip isn't responding, so I might be totally wrong here...