/* * 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 #include #include #include #include #include #include #include #include #include #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 \n\n" " SPI device index to use or alias\n" " SPI slave index to use or alias\n" " Number of EEPROM memory address bytes\n" " EEPROM memory page size in bytes\n" " 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; }