1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/soft_spi/soft_spi.c
Joshua DeWeese aba1d9aaf0 drivers/soft_spi: fix device validation bug
The code did not take into account that valid bus numbers are
0...(COUNT-1).

This patch corrects this. It also makes use of the newly introduced
SOFT_SPI_NUMOF define.
2022-12-14 08:18:45 -05:00

243 lines
6.4 KiB
C

/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_soft_spi
* @{
*
* @file
* @brief Software SPI implementation
*
* @author Markus Blechschmidt <Markus.Blechschmidt@haw-hamburg.de>
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
*/
#include <stdio.h>
#include <assert.h>
#include "mutex.h"
#include "periph/gpio.h"
#include "xtimer.h"
#include "soft_spi.h"
#include "soft_spi_params.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define READ_PADDING_BYTE (0x00)
/**
* @brief Allocate one lock per SPI device
*/
static mutex_t locks[SOFT_SPI_NUMOF];
static inline bool soft_spi_bus_is_valid(soft_spi_t bus)
{
unsigned int soft_spi_num = (unsigned int) bus;
if (SOFT_SPI_NUMOF <= soft_spi_num) {
return false;
}
return true;
}
void soft_spi_init(soft_spi_t bus)
{
DEBUG("Soft SPI init\n");
assert(soft_spi_bus_is_valid(bus));
/* initialize device lock */
mutex_init(&locks[bus]);
soft_spi_init_pins(bus);
}
void soft_spi_init_pins(soft_spi_t bus)
{
DEBUG("Soft SPI soft_spi_init_pins\n");
assert(soft_spi_bus_is_valid(bus));
/* check that miso is not mosi is not clk*/
assert(!gpio_is_equal(soft_spi_config[bus].mosi_pin, soft_spi_config[bus].miso_pin));
assert(!gpio_is_equal(soft_spi_config[bus].mosi_pin, soft_spi_config[bus].clk_pin));
assert(!gpio_is_equal(soft_spi_config[bus].miso_pin, soft_spi_config[bus].clk_pin));
/* mandatory pins */
assert(gpio_is_valid(soft_spi_config[bus].mosi_pin) ||
gpio_is_valid(soft_spi_config[bus].miso_pin));
assert(gpio_is_valid(soft_spi_config[bus].clk_pin));
/* initialize clock pin */
gpio_init(soft_spi_config[bus].clk_pin, GPIO_OUT);
/* initialize optional pins */
if (gpio_is_valid(soft_spi_config[bus].mosi_pin)) {
gpio_init(soft_spi_config[bus].mosi_pin, GPIO_OUT);
gpio_clear(soft_spi_config[bus].mosi_pin);
}
if (gpio_is_valid(soft_spi_config[bus].miso_pin)) {
gpio_init(soft_spi_config[bus].miso_pin, GPIO_IN);
}
}
int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs)
{
DEBUG("Soft SPI init CS\n");
if (!soft_spi_bus_is_valid(bus)) {
DEBUG("Soft SPI bus not valid\n");
return SOFT_SPI_NODEV;
}
if (gpio_is_valid(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) {
DEBUG("Soft SPI set user CS line\n");
gpio_init(cs, GPIO_OUT);
gpio_set(cs);
}
return SOFT_SPI_OK;
}
static inline int soft_spi_mode_is_valid(soft_spi_mode_t mode)
{
if ((mode != SOFT_SPI_MODE_0) && (mode != SOFT_SPI_MODE_1) &&
(mode != SOFT_SPI_MODE_2) && (mode != SOFT_SPI_MODE_3)) {
return 0;
}
return 1;
}
void soft_spi_acquire(soft_spi_t bus, soft_spi_cs_t cs, soft_spi_mode_t mode, soft_spi_clk_t clk)
{
(void)cs;
assert(soft_spi_bus_is_valid(bus));
assert(soft_spi_mode_is_valid(mode));
/* lock bus */
mutex_lock(&locks[bus]);
soft_spi_config[bus].soft_spi_mode = mode;
switch (mode) {
case SOFT_SPI_MODE_0:
case SOFT_SPI_MODE_1:
/* CPOL=0 */
gpio_clear(soft_spi_config[bus].clk_pin);
break;
case SOFT_SPI_MODE_2:
case SOFT_SPI_MODE_3:
/* CPOL=1 */
gpio_set(soft_spi_config[bus].clk_pin);
break;
}
soft_spi_config[bus].soft_spi_clk = clk;
}
void soft_spi_release(soft_spi_t bus)
{
assert(soft_spi_bus_is_valid(bus));
mutex_unlock(&locks[bus]);
}
static inline uint8_t _transfer_one_byte(soft_spi_t bus, uint8_t out)
{
uint8_t i = 8;
if (SOFT_SPI_MODE_1 == soft_spi_config[bus].soft_spi_mode ||
SOFT_SPI_MODE_3 == soft_spi_config[bus].soft_spi_mode) {
/* CPHA = 1*/
gpio_toggle(soft_spi_config[bus].clk_pin);
}
do {
uint8_t bit = out >> 7;
gpio_write(soft_spi_config[bus].mosi_pin, bit);
xtimer_usleep(soft_spi_config[bus].soft_spi_clk);
gpio_toggle(soft_spi_config[bus].clk_pin);
out <<= 1; /*shift transfer register*/
bit = gpio_read(soft_spi_config[bus].miso_pin);
out = bit ? (out | 0x01) : (out & 0xfe); /*set or delete bit 0*/
xtimer_usleep(soft_spi_config[bus].soft_spi_clk);
--i;
if (i > 0) {
gpio_toggle(soft_spi_config[bus].clk_pin);
}
} while (i > 0);
if (SOFT_SPI_MODE_0 == soft_spi_config[bus].soft_spi_mode ||
SOFT_SPI_MODE_2 == soft_spi_config[bus].soft_spi_mode) {
/* CPHA = 0 */
xtimer_usleep(soft_spi_config[bus].soft_spi_clk);
gpio_toggle(soft_spi_config[bus].clk_pin);
}
return out;
}
uint8_t soft_spi_transfer_byte(soft_spi_t bus, soft_spi_cs_t cs, bool cont, uint8_t out)
{
DEBUG("Soft SPI soft_spi_transfer_bytes\n");
assert(soft_spi_bus_is_valid(bus));
uint8_t retval = 0;
/* activate the given chip select line */
if (gpio_is_valid(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) {
gpio_clear((gpio_t)cs);
}
retval = _transfer_one_byte(bus, out);
if (!cont) {
if (gpio_is_valid(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) {
gpio_set((gpio_t)cs);
}
}
return retval;
}
void soft_spi_transfer_bytes(soft_spi_t bus, soft_spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
DEBUG("Soft SPI soft_spi_transfer_bytes\n");
assert(soft_spi_bus_is_valid(bus));
/* make sure at least one input or one output buffer is given */
assert(out || in);
const uint8_t *outbuf = out;
uint8_t *inbuf = in;
/* activate the given chip select line */
if ((cs != GPIO_UNDEF) && (cs != SOFT_SPI_CS_UNDEF)) {
gpio_clear((gpio_t)cs);
}
if (!inbuf) {
for (size_t i = 0; i < len; i++) {
_transfer_one_byte(bus, outbuf[i]);
}
}
else if (!outbuf) {
for (size_t i = 0; i < len; i++) {
inbuf[i] = _transfer_one_byte(bus, READ_PADDING_BYTE);
}
}
else {
for (size_t i = 0; i < len; i++) {
inbuf[i] = _transfer_one_byte(bus, outbuf[i]);
}
}
if (!cont) {
if ((cs != GPIO_UNDEF) && (cs != SOFT_SPI_CS_UNDEF)) {
gpio_set((gpio_t)cs);
}
}
}