SPI (Serial Peripheral Interface) Framework

This note includes:

Hardware interface

This is the interface to the code that implements the hardware-specific low-level functionality of an SPI master.

Function table

The spi_funcs_t structure is a table of pointers to functions that you can provide for your hardware-specific low-level module. The high-level code calls these functions.

typedef struct {
    size_t  size;   /* size of this structure */
    void*   (*init)( void *hdl, char *options );
    void    (*fini)( void *hdl );
    int     (*drvinfo)( void *hdl, spi_drvinfo_t *info );
    int     (*devinfo)( void *hdl, uint32_t device,
                        spi_devinfo_t *info );
    int     (*setcfg)( void *hdl, uint16_t device,
                       spi_cfg_t *cfg );
    void*   (*xfer)( void *hdl, uint32_t device, uint8_t *buf,
                     int *len );
    int     (*dma_xfer)( void *hdl, uint32_t device,
                         spi_dma_paddr_t *paddr, int len );
} spi_funcs_t;

There has to be a function table entry in the low-level module, and it has to be named spi_drv_entry. High-level code looks for this symbol name to find the function table for the low-level module.

The functions are described in the sections that follow:

Low-level module handle structure

The SPIDEV structure is the handle that the low-level module has to return to the high-level code. You can extend the structure, but SPIDEV has to be at the top. This handle is also passed to the low-level driver when the high-level code calls low-level functions.

typedef struct _spidev_entry {
    iofunc_attr_t   attr;
    void        *hdl;       /* Pointer to high-level handle */
    void        *lock;      /* Pointer to lock list */
} SPIDEV;

init function

The init function initializes the master interface. The prototype for this function is:

void *init( void *hdl, char *options );

The arguments are:

hdl
The handle of the high-level code.
options
A pointer to the command-line arguments for the low-level module.

The function must return either a handle, which is the pointer to the SPIDEV of the low-level module, or NULL if an error occurred.

fini function

The fini function cleans up the low-level driver and frees any memory associated with the given handle. The prototype for this function is:

void fini( void *hdl );

The argument is the handle of the low-level module that the init function returned.

drvinfo function

The drvinfo function gets driver information from the low-level module. The prototype for this function is:

int drvinfo( void *hdl, spi_drvinfo_t *info );

The arguments are:

hdl
The handle of the low-level module that the init function returned.
info
A pointer to the driver information structure. This structure is defined as:
typedef struct {
        uint32_t    version;
        char        name[16];   /* Driver name */
        uint32_t    feature;
#define SPI_FEATURE_DMA         (1 << 31)
#define SPI_FEATURE_DMA_ALIGN   0xFF
      } spi_drvinfo_t; 
  

The function must return EOK if it successfully obtained the driver information.

devinfo function

The devinfo function obtains the information from specific devices that are connected to the SPI bus. The prototype for this function is:

int devinfo( void *hdl, uint32_t device, spi_devinfo_t *info );

The arguments are:

hdl
The handle of the low-level module that the init function returned.
device
The device ID. You can OR it with SPI_DEV_DEFAULT to select the current device; otherwise the next device is selected.
info
A pointer to the device information structure. This structure is defined as:
typedef struct {
    uint32_t    device;     /* Device ID */
    char        name[16];   /* Device description */
    spi_cfg_t   cfg;        /* Device configuration */
} spi_devinfo_t;
  

This function must return EOK, or EINVAL if the device ID is invalid.

setcfg function

The setcfg function changes the configuration of specific devices on the SPI bus. The prototype for this function is:

int setcfg( void *hdl, uint16_t device, spi_cfg_t *cfg );

The arguments are:

hdl
The handle of the low-level module that the init function returned.
device
The device ID.
cfg
A pointer to the configuration structure. This structure is defined as:
typedef struct {
    uint32_t    mode;
    uint32_t    clock_rate;
} spi_cfg_t;
  

This function must return EOK, or EINVAL if either the device ID or the configuration is invalid.

xfer function

The xfer function initiates a transmit, receive, or exchange transaction. The prototype for this function is:

void *xfer( void *hdl, uint32_t device, uint8_t *buf,
            int *len );

The arguments are:

hdl
The handle of the low-level module that the init function returned.
device
The device ID.
buf
A pointer to the data buffer for this transaction.
len
A pointer to length, in bytes, of the data for this transaction.

The function must return a pointer to the receive/exchange buffer, and store, in the location that len points to, the byte length of the data that has been transmitted, received, or exchanged by the low-level module. The high-level code checks the length to determine whether the transaction was successful.


Note: The buffer is not DMA-safe, so if the low-level module needs to use DMA, it must allocate its own DMA-safe buffer and copy the data over, if necessary.

dma_xfer function

The dma_xfer function initiates a DMA transmit, receive, or exchange transaction. The prototype for this function is:

int dma_xfer( void *hdl, uint32_t device,
              spi_dma_paddr_t *paddr, int len );

The arguments are:

hdl
The handle of the low-level module that the init function returned.
device
The device ID.
paddr
A pointer to the DMA buffer address, which is defined as:
typedef struct {
    uint64_t    rpaddr;
    uint64_t    wpaddr;
} spi_dma_paddr_t;
  

The rpaddr and wpaddr are physical addresses.

len
The length, in bytes, of the data for this DMA transaction.

This function must return the number of bytes that have been successfully transferred by DMA, or -1 if an error occurred. It's the responsibility of the application to manage the DMA buffer.

API library

The libspi-master library provides an interface to mediate access to the SPI master. The resource manager layer registers a device name (usually /dev/spi0). Applications access the SPI master by using the functions declared in <hw/spi-master.h>.

The functions include the following:

They're described in the sections that follow.

spi_open()

The spi_open() function lets the application connect to the SPI resource manager. The prototype for this function is:

int spi_open( const char *path );

The argument is:

path
The path name of the SPI device, usually /dev/spi0.

This function returns a file descriptor, or -1 if the open failed.

spi_close()

The spi_close() function disconnects the application from the SPI resource manager. The prototype for this function is:

int spi_close( int fd );

The argument is:

fd
The file descriptor that the spi_open() function returned.

spi_setcfg()

The spi_setcfg() function sets the configuration for a specific device on the SPI bus. The prototype for this function is:

int spi_setcfg( int fd, uint32_t device, spi_cfg_t *cfg );

The arguments are:

fd
The file descriptor obtained by calling spi_open().
device
The device ID. You can OR it with SPI_DEV_DEFAULT to set the device as the default for this file descriptor.
cfg
A pointer to the configuration structure. This structure is:
typedef struct {
    uint32_t    mode;
    uint32_t    clock_rate;
} spi_cfg_t;
  

The possible mode settings are defined in <hw/spi-master.h>.

This function returns EOK if the configuration is successful.

spi_getdevinfo()

The spi_getdevinfo() function gets the information for a specific device on the SPI bus. The prototype for this function is:

int spi_getdevinfo( int fd, uint32_t device,
                    spi_devinfo_t *devinfo );

The arguments are:

fd
The file descriptor returned by spi_open().
device
The device ID, or SPI_DEV_ID_NONE to select the first device. If you specify a device ID, you can OR it with SPI_DEV_DEFAULT to select the specified device; otherwise, the next device is selected.
devinfo
A pointer to the device information structure:
typedef struct {
    uint32_t    device;     /* Device ID */
    char        name[16];   /* Device description */
    spi_cfg_t   cfg;        /* Device configuration */
} spi_devinfo_t;
  

This function returns EOK if the device information is obtained successfully.

spi_getdrvinfo()

The spi_getdrvinfo() function gets the driver information for the low-level module. The prototype for this function is:

int spi_getdrvinfo( int fd, spi_drvinfo_t *drvinfo );

The arguments are:

fd
The file descriptor returned by spi_open().
devinfo
A pointer to device information structure:
typedef struct {
    uint32_t    version;
    char        name[16];   /* Driver name */
    uint32_t    feature;
} spi_drvinfo_t;
  

The function returns EOK if the driver information is obtained successfully.

spi_read()

The spi_read() function reads data from a specific device on the SPI bus. The prototype for this function is:

int spi_read( int fd, uint32_t device, void *buf, int len );

The arguments are:

fd
The file descriptor returned by spi_open().
device
The device ID with at most one of the following flags optionally ORed in:
buf
A pointer to the read buffer.
len
The length, in bytes, of the data to be read.

The function returns EOK if it successfully reads len bytes of data from the device. If an error occurred, the function returns -1 and sets errno:

EIO
The read from the device failed, or a hardware error occurred.
EINVAL
The device ID is invalid, or you're trying to unlock a device that isn't locked.
ENOMEM
Insufficient memory.
EPERM
The device is locked by another connection.

spi_write()

The spi_write() function writes data to a specific device on the SPI bus. The prototype for this function is:

int spi_write( int fd, uint32_t device, void *buf, int len );

The arguments are:

fd
The file descriptor returned by spi_open().
device
The device ID with at most one of the following flags optionally ORed in:
buf
A pointer to the write buffer.
len
The length, in bytes, of the data to be written.

The function returns EOK if it successfully wrote len bytes of data to the device. If an error occurred, the function returns -1 and sets errno:

EIO
The write to the device failed, or a hardware error occurred.
EINVAL
The device ID is invalid, or you're trying to unlock a device that isn't locked.
ENOMEM
Insufficient memory.
EPERM
The device is locked by another connection.

spi_xchange()

The spi_exchange() function exchanges data between a specific device and the SPI master. The prototype for this function is:

int spi_xchange( int fd, uint32_t device, void *wbuf,
                 void *rbuf, int len );

The arguments are:

fd
The file descriptor returned by spi_open().
device
The device ID with at most one of the following flags optionally ORed in:
wbuf
A pointer to the send buffer.
rbuf
A pointer to the receive buffer.
len
The length, in bytes, of the data to be exchanged.

The function returns EOK if len bytes of data are successfully exchanged between the device and the SPI master. If an error occurred, the function returns -1 and sets errno:

EIO
The write to the device failed, or a hardware error occurred.
EINVAL
The device ID is invalid, or you're trying to unlock a device that isn't locked.
ENOMEM
Insufficient memory.
EPERM
The device is locked by another connection.

spi_cmdread()

The spi_cmdread() function sends a command to, and then reads data from, a specific device on SPI bus. The prototype for this function is:

int spi_cmdread( int fd, uint32_t device, void *cbuf,
                int16_t clen, void *rbuf, int rlen );

The arguments are:

fd
The file descriptor returned by spi_open().
device
The device ID with at most one of the following flags optionally ORed in:
cbuf
A pointer to the command buffer.
clen
The command length, in bytes.
rbuf
A pointer to the receive buffer.
rlen
The read length, in bytes.

The function returns EOK if the command send and read operation is successful. If an error occurred, the function returns -1 and sets errno:

EIO
The write to the device failed, or a hardware error occurred.
EINVAL
The device ID is invalid, or you're trying to unlock a device that isn't locked.
ENOMEM
Insufficient memory.
EPERM
The device is locked by another connection.

Note: You can achieve the same results by calling spi_xchange().

spi_dma_xchange()

The spi_dma_xchange() function uses DMA to exchange data between the SPI master and a SPI device. The prototype for this function is:

int spi_dma_xchange( int fd, uint32_t device, void *wbuf,
                     void *rbuf, int len );

The arguments are:

fd
The file descriptor returned by spi_open().
device
The device ID with at most one of the following flags optionally ORed in:
wbuf
A pointer to the send buffer, or NULL if there's no data to send.
rbuf
A pointer to the receive buffer, or NULL if there's no data to receive.
len
The exchange length, in bytes.

The function returns EOK if DMA the exchange is successful. If an error occurred, the function returns -1 and sets errno:

EIO
The write to the device failed, or a hardware error occurred.
EINVAL
The device ID is invalid, or you're trying to unlock a device that isn't locked, or the buffer address is invalid.
ENOMEM
Insufficient memory.
EPERM
The device is locked by another connection.
ENOTSUP
DMA isn't supported.

Note: The application is responsible for allocating and managing the DMA buffer. The application can call spi_getdrvinfo() to determine if the driver supports DMA, and whether or not the DMA buffer requires alignment.