435 lines
11 KiB
C
435 lines
11 KiB
C
/*
|
|
* Copyright 2017, Digi International Inc.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
#include <libdigiapix/spi.h>
|
|
|
|
#define DEFAULT_SPI_ALIAS "DEFAULT_SPI"
|
|
#define DEFAULT_SPI_ADDRESS_SIZE 1
|
|
#define DEFAULT_SPI_PAGE_SIZE 16
|
|
#define DEFAULT_SPI_PAGE_INDEX 0
|
|
|
|
#define ARG_SPI_DEVICE 0
|
|
#define ARG_SPI_SLAVE 1
|
|
|
|
#define BUFF_SIZE 256
|
|
|
|
#define WREN 0x06
|
|
#define WRDI 0x04
|
|
#define WRITE 0x02
|
|
#define READ 0x03
|
|
#define RDSR 0x05
|
|
|
|
#define CLK_MODE SPI_CLK_MODE_0
|
|
#define CHIP_SELECT SPI_CS_ACTIVE_LOW
|
|
#define BIT_ORDER SPI_BO_MSB_FIRST
|
|
#define MAX_BUS_SPEED 1000000 /* 1MHz */
|
|
#define BITS_PER_WORD SPI_BPW_8
|
|
|
|
#define OPERATION_BYTES 1
|
|
|
|
static spi_t *spi_dev;
|
|
static unsigned int page_size, address_bytes = 0;
|
|
static uint8_t *tx_buffer;
|
|
static uint8_t *rx_buffer;
|
|
|
|
/*
|
|
* usage_and_exit() - Show usage information and exit with 'exitval' return
|
|
* value
|
|
*
|
|
* @name: Application name.
|
|
* @exitval: The exit code.
|
|
*/
|
|
static void usage_and_exit(char *name, int exitval)
|
|
{
|
|
fprintf(stdout,
|
|
"Example application using libdigiapix SPI support\n"
|
|
"\n"
|
|
"Usage: %s <spi-dev> <spi-ss> <address-size> <page-size> <page-index>\n\n"
|
|
"<spi-dev> SPI device index to use or alias\n"
|
|
"<spi-ss> SPI slave index to use or alias\n"
|
|
"<address-size> Number of EEPROM memory address bytes\n"
|
|
"<page-size> EEPROM memory page size in bytes\n"
|
|
"<page-index> EEPROM memory page index to use\n"
|
|
"\n"
|
|
"Aliases for SPI can be configured in the library config file\n"
|
|
"\n", name);
|
|
|
|
exit(exitval);
|
|
}
|
|
|
|
/*
|
|
* cleanup() - Frees all the allocated memory before exiting
|
|
*/
|
|
static void cleanup(void)
|
|
{
|
|
/* Free spi */
|
|
ldx_spi_free(spi_dev);
|
|
|
|
/* Free buffers */
|
|
free(tx_buffer);
|
|
free(rx_buffer);
|
|
}
|
|
|
|
/*
|
|
* sigaction_handler() - Handler to execute after receiving a signal
|
|
*
|
|
* @signum: Received signal.
|
|
*/
|
|
static void sigaction_handler(int signum)
|
|
{
|
|
/* 'atexit' executes the cleanup function */
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* register_signals() - Registers program signals
|
|
*/
|
|
static void register_signals(void)
|
|
{
|
|
struct sigaction action;
|
|
|
|
action.sa_handler = sigaction_handler;
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
|
|
sigaction(SIGHUP, &action, NULL);
|
|
sigaction(SIGINT, &action, NULL);
|
|
sigaction(SIGTERM, &action, NULL);
|
|
}
|
|
|
|
/*
|
|
* parse_argument() - Parses the given string argument and returns the
|
|
* corresponding integer value
|
|
*
|
|
* @argv: Argument to parse in string format.
|
|
* @arg_type: Type of the argument to parse.
|
|
*
|
|
* Return: The parsed integer argument, -1 on error.
|
|
*/
|
|
static int parse_argument(char *argv, int arg_type)
|
|
{
|
|
char *endptr;
|
|
long value;
|
|
|
|
errno = 0;
|
|
value = strtol(argv, &endptr, 10);
|
|
|
|
if ((errno == ERANGE && (value == LONG_MAX || value == LONG_MIN))
|
|
|| (errno != 0 && value == 0))
|
|
return -1;
|
|
|
|
if (endptr == argv) {
|
|
switch (arg_type) {
|
|
case ARG_SPI_DEVICE:
|
|
return ldx_spi_get_device(endptr);
|
|
case ARG_SPI_SLAVE:
|
|
return ldx_spi_get_slave(endptr);
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* enable_write() - Sets the SPI write enable bit
|
|
*
|
|
* Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
|
|
*/
|
|
static int enable_write(void)
|
|
{
|
|
uint8_t write_data[1] = {0};
|
|
|
|
printf("[INFO] Setting write enable bit...\n");
|
|
write_data[0] = WREN;
|
|
|
|
return ldx_spi_write(spi_dev, write_data, sizeof(write_data));
|
|
}
|
|
|
|
/*
|
|
* read_status_register() - Reads the SPI status register
|
|
*
|
|
* @status: Variable to store the read status.
|
|
*
|
|
* Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
|
|
*/
|
|
static int read_status_register(uint8_t *status)
|
|
{
|
|
uint8_t write_data[2] = {0};
|
|
uint8_t read_data[2] = {0};
|
|
|
|
printf("[INFO] Reading status register...\n");
|
|
write_data[0] = RDSR;
|
|
if (ldx_spi_transfer(spi_dev, write_data, read_data, 2) != EXIT_SUCCESS) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
printf("[INFO] SPI Status Register is 0x%02x\n", read_data[1]);
|
|
*status = read_data[1];
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* write_page() - Writes an EEPROM page with the given data
|
|
*
|
|
* @page_index: index of the EEPROM page to write.
|
|
* @data: the data to write.
|
|
*
|
|
* Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
|
|
*/
|
|
static int write_page(int page_index, uint8_t* data)
|
|
{
|
|
uint8_t *write_data;
|
|
uint16_t page_address = page_size * page_index;
|
|
uint8_t status = 0;
|
|
int i, ret = 0;
|
|
|
|
/* Create the page write buffer */
|
|
write_data = (uint8_t *)calloc(page_size + OPERATION_BYTES + address_bytes,
|
|
sizeof(uint8_t));
|
|
if (write_data == NULL) {
|
|
printf("Unable to allocate memory to write the page.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
printf("[INFO] Writing %d bytes to page %d at address 0x%x...\n", page_size,
|
|
page_index, page_address);
|
|
write_data[0] = WRITE; // Operation.
|
|
for (i = 0; i < address_bytes; i++) {
|
|
write_data[i + OPERATION_BYTES] = (page_address >> (8 * (address_bytes -
|
|
i - 1)));
|
|
}
|
|
|
|
/* Fill the data array. */
|
|
for (i = 0; i < page_size; i++) {
|
|
write_data[(i + OPERATION_BYTES + address_bytes)] = data[i];
|
|
}
|
|
|
|
/* Perform the write operation. */
|
|
if (ldx_spi_write(spi_dev, write_data, page_size + OPERATION_BYTES +
|
|
address_bytes) != EXIT_SUCCESS) {
|
|
free(write_data);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Wait for the operation to complete. */
|
|
do {
|
|
/* Read the status register to check the WIP (write-in-progress) bit. */
|
|
ret = read_status_register(&status);
|
|
if (ret != EXIT_SUCCESS) {
|
|
free(write_data);
|
|
return EXIT_FAILURE;
|
|
}
|
|
/* Check the WIP (write-in-progress) status bit. */
|
|
if (status & 0x01) {
|
|
printf("[INFO] Write in progress...\n");
|
|
usleep(1);
|
|
} else {
|
|
printf("[INFO] Write finished!\n");
|
|
}
|
|
} while (status & 0x1);
|
|
|
|
free(write_data);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* read_page() - Reads an EEPROM page
|
|
*
|
|
* @page_index: index of the EEPROM page to read.
|
|
* @data: buffer to store the read data in.
|
|
*
|
|
* Return: EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
|
|
*/
|
|
static int read_page(int page_index, uint8_t* data)
|
|
{
|
|
uint8_t *write_data;
|
|
uint8_t *read_data;
|
|
uint16_t page_address;
|
|
int i = 0;
|
|
|
|
page_address = page_size * page_index;
|
|
|
|
/* Create the buffers */
|
|
write_data = (uint8_t *)calloc(page_size + OPERATION_BYTES + address_bytes,
|
|
sizeof(uint8_t));
|
|
read_data = (uint8_t *)calloc(page_size + OPERATION_BYTES + address_bytes,
|
|
sizeof(uint8_t));
|
|
if (write_data == NULL || read_data == NULL) {
|
|
printf("Unable to allocate memory to read the page.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
printf("[INFO] Reading page %d at address 0x%x...\n", page_index,
|
|
page_address);
|
|
write_data[0] = READ; // Operation.
|
|
for (i = 0; i < address_bytes; i++) {
|
|
write_data[i + OPERATION_BYTES] = (page_address >> (8 * (address_bytes -
|
|
i - 1)));
|
|
}
|
|
|
|
/* Perform the read operation with a transfer */
|
|
if (ldx_spi_transfer(spi_dev, write_data, read_data, page_size +
|
|
OPERATION_BYTES + address_bytes) != EXIT_SUCCESS) {
|
|
free(write_data);
|
|
free(read_data);
|
|
return EXIT_FAILURE;
|
|
}
|
|
for (i = 0; i < page_size; i++) {
|
|
data[i] = read_data[(i + OPERATION_BYTES + address_bytes)];
|
|
}
|
|
|
|
free(write_data);
|
|
free(read_data);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int spi_device = 0, spi_slave = 0, page_index = 0, i = 0;
|
|
spi_transfer_cfg_t transfer_mode = {0};
|
|
char *name = basename(argv[0]);
|
|
|
|
/* Check input parameters */
|
|
if (argc == 1) {
|
|
/* Use default values */
|
|
spi_device = ldx_spi_get_device(DEFAULT_SPI_ALIAS);
|
|
spi_slave = ldx_spi_get_slave(DEFAULT_SPI_ALIAS);
|
|
address_bytes = DEFAULT_SPI_ADDRESS_SIZE;
|
|
page_size = DEFAULT_SPI_PAGE_SIZE;
|
|
page_index = DEFAULT_SPI_PAGE_INDEX;
|
|
} else if (argc == 6) {
|
|
/* Parse command line arguments */
|
|
spi_device = parse_argument(argv[1], ARG_SPI_DEVICE);
|
|
spi_slave = parse_argument(argv[2], ARG_SPI_SLAVE);
|
|
address_bytes = atoi(argv[3]);
|
|
page_size = atoi(argv[4]);
|
|
page_index = atoi(argv[5]);
|
|
} else {
|
|
usage_and_exit(name, EXIT_FAILURE);
|
|
}
|
|
|
|
if (spi_device < 0 || spi_slave < 0) {
|
|
printf("Unable to parse SPI device/slave arguments\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (address_bytes <= 0) {
|
|
printf("Address bytes must be greater than 0\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (page_size <= 0) {
|
|
printf("Page size must be greater than 0\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (page_index < 0) {
|
|
printf("Page index must be greater or equal than 0\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Register signals and exit cleanup function */
|
|
atexit(cleanup);
|
|
register_signals();
|
|
|
|
/* Request SPI */
|
|
spi_dev = ldx_spi_request((unsigned int)spi_device, (unsigned int)spi_slave);
|
|
if (!spi_dev) {
|
|
printf("Failed to initialize SPI\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Configure the transfer mode */
|
|
transfer_mode.clk_mode = CLK_MODE;
|
|
transfer_mode.chip_select = CHIP_SELECT;
|
|
transfer_mode.bit_order = BIT_ORDER;
|
|
if (ldx_spi_set_transfer_mode(spi_dev, &transfer_mode) != EXIT_SUCCESS) {
|
|
printf("Failed to configure SPI transfer mode\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Configure the bits-per-word */
|
|
if (ldx_spi_set_bits_per_word(spi_dev, BITS_PER_WORD) != EXIT_SUCCESS) {
|
|
printf("Failed to configure SPI bits-per-word\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Configure the max bus speed */
|
|
if (ldx_spi_set_speed(spi_dev, MAX_BUS_SPEED) != EXIT_SUCCESS) {
|
|
printf("Failed to configure SPI max bus speed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Set the write-enable bit. */
|
|
if (enable_write() != EXIT_SUCCESS) {
|
|
printf("Failed to set the write-enable bit\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Initialize the write and read buffers */
|
|
tx_buffer = (uint8_t *)calloc(page_size, sizeof(uint8_t));
|
|
rx_buffer = (uint8_t *)calloc(page_size, sizeof(uint8_t));
|
|
if (tx_buffer == NULL || rx_buffer == NULL) {
|
|
printf("Failed to initialize read/write buffers\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Fill the data to write with random bytes. */
|
|
srand(time(NULL));
|
|
for (i = 0; i < (page_size); i++) {
|
|
tx_buffer[i] = rand() % 255;
|
|
}
|
|
|
|
/* Write the page. */
|
|
if (write_page(page_index, tx_buffer) != EXIT_SUCCESS) {
|
|
printf("Write page failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Read the page. */
|
|
if (read_page(page_index, rx_buffer) != EXIT_SUCCESS) {
|
|
printf("Read page failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Validate the read data. */
|
|
printf("[INFO] Validating read data...\n");
|
|
for (i = 0; i < page_size; i++) {
|
|
printf(" Byte %d: Write 0x%02x - Read 0x%02x", i,
|
|
tx_buffer[i], rx_buffer[i]);
|
|
if (tx_buffer[i] == rx_buffer[i]) {
|
|
printf(" - Correct\n");
|
|
} else {
|
|
printf(" - Incorrect\n");
|
|
}
|
|
}
|
|
|
|
/* 'atexit' executes the cleanup function */
|
|
return EXIT_SUCCESS;
|
|
}
|