OK. I have started working on translating spidev.h and spidev.c to Julia. I hope @yuyichao can tell me if I’m heading down the right path on this (I’m trying to somewhat follow what you advises for i2c-dev).
Here’s the C code for the main structure used:
/**
* struct spi_ioc_transfer - describes a single SPI transfer
* @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
* If no data is provided, zeroes are shifted out.
* @rx_buf: Holds pointer to userspace buffer for receive data, or null.
* @len: Length of tx and rx buffers, in bytes.
* @speed_hz: Temporary override of the device's bitrate.
* @bits_per_word: Temporary override of the device's wordsize.
* @delay_usecs: If nonzero, how long to delay after the last bit transfer
* before optionally deselecting the device before the next transfer.
* @cs_change: True to deselect device before starting the next transfer.
*
* This structure is mapped directly to the kernel spi_transfer structure;
* the fields have the same meanings, except of course that the pointers
* are in a different address space (and may be of different sizes in some
* cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
* Zero-initialize the structure, including currently unused fields, to
* accommodate potential future updates.
*
* SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
* Pass it an array of related transfers, they'll execute together.
* Each transfer may be half duplex (either direction) or full duplex.
*
* struct spi_ioc_transfer mesg[4];
* ...
* status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
*
* So for example one transfer might send a nine bit command (right aligned
* in a 16-bit word), the next could read a block of 8-bit data before
* terminating that command by temporarily deselecting the chip; the next
* could send a different nine bit command (re-selecting the chip), and the
* last transfer might write some register values.
*/
struct spi_ioc_transfer {
__u64 tx_buf;
__u64 rx_buf;
__u32 len;
__u32 speed_hz;
__u16 delay_usecs;
__u8 bits_per_word;
__u8 cs_change;
__u8 tx_nbits;
__u8 rx_nbits;
__u16 pad;
/* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
*
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
};
I see the largest item as 64-bit - so I created the following (again trying to follow what was done with i2c-dev). I also created some helper functions to get the values out like they would have been used in the C structure.
struct spi_ioc_transfer
block::NTuple{4, UInt64}
end
#****************************************************************
# The following: get_tx_buf, get_rx_buf, get_len, get_speed,
# get_delay_usecs, get_bits_per_word, get_cs_change,
# get_tx_nbits, get_rx_nbits are helper functions to get the
# proper values out of the block::spi_ioc_transfer structure.
#****************************************************************
function get_tx_buf(block::spi_ioc_transfer)
return block[1] #1) 64-bits
end
function get_rx_buf(block::spi_ioc_transfer)
return block[2] #2) 64-bits
end
function get_len(block::spi_ioc_transfer)
return block[3] & 0xFFFFFFFF00000000 #3) 32-bits
end
function set_len(len::UInt32)
num = UInt64(len)
return num << 32 #needs to be upper
end
function get_speed(block::spi_ioc_transfer)
return (block[3] << 32) >> 32 #4) 32-bits
end
function get_delay_usecs(block::spi_ioc_transfer)
return block[4] >> 48 #5) 16-bits
end
function get_bits_per_word(block::spi_ioc_transfer)
return (block[4] << 16) >> 56 #6) 8-bits
end
function get_cs_change(block::spi_ioc_transfer)
return (block[4] << 24) >> 56 #7) 8-bits
end
function get_tx_nbits(block::spi_ioc_transfer)
return (block[4] << 32) >> 56 #8) 8-bits
end
function get_rx_nbits(block::spi_ioc_transfer)
return (block[4] << 40) >> 56 #9) 8-bits
end
#****************************************************************
and, here is how it is used in C:
int spi_xfer(int fd, uint8_t *tx_buffer, uint8_t tx_len, uint8_t *rx_buffer, uint8_t rx_len){
struct spi_ioc_transfer spi_message[2];
memset(spi_message, 0, sizeof(spi_message));
spi_message[0].tx_buf = (unsigned long)tx_buffer;
spi_message[0].len = tx_len;
spi_message[1].rx_buf = (unsigned long)rx_buffer;
spi_message[1].len = rx_len;
return ioctl(fd, SPI_IOC_MESSAGE(2), spi_message);
}
And, how I’m trying to use it in Julia. I’m not sure about how to pass an array of structures (or in this case NTuples - if I’m doing this right):
function spi_xfer(fd::Int32, tx_buffer::UInt8, tx_len::UInt8, rx_buffer::UInt8, rx_len::UInt8)
tx = spi_ioc_transfer(tx_buffer,0, set_len(tx_len),0)
rx = spi_ioc_transfer(0,rx_buffer,set_len(rx_len),0)
spi_message = NTuple{2, spi_ioc_transfer}((rx, tx))
ret = ccall((:ioctl, "libc"), Int, (Cint, Cint, Ref{spi_ioc_transfer}), fd, SPI_IOC_MESSAGE(2), spi_message[1])
return ret
end
The link to the spidev.h file has the same contents as what’s on my debian system.
http://elixir.free-electrons.com/linux/latest/source/include/uapi/linux/spi/spidev.h