diff options
Diffstat (limited to 'common/recipes-utils/bitbang/files/src')
-rw-r--r-- | common/recipes-utils/bitbang/files/src/Makefile | 29 | ||||
-rw-r--r-- | common/recipes-utils/bitbang/files/src/bitbang.c | 236 | ||||
-rw-r--r-- | common/recipes-utils/bitbang/files/src/bitbang.h | 66 | ||||
-rw-r--r-- | common/recipes-utils/bitbang/files/src/mdio_bb.c | 335 | ||||
-rw-r--r-- | common/recipes-utils/bitbang/files/src/spi_bb.c | 317 |
5 files changed, 983 insertions, 0 deletions
diff --git a/common/recipes-utils/bitbang/files/src/Makefile b/common/recipes-utils/bitbang/files/src/Makefile new file mode 100644 index 0000000..24e4220 --- /dev/null +++ b/common/recipes-utils/bitbang/files/src/Makefile @@ -0,0 +1,29 @@ +# Copyright 2014-present Facebook. All Rights Reserved. +# +# This program file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA + +all: spi-bb mdio-bb + +spi-bb: spi_bb.o bitbang.o + $(CC) -o $@ $^ $(LDFLAGS) -lgpio + +mdio-bb: mdio_bb.o bitbang.o + $(CC) -o $@ $^ $(LDFLAGS) -lgpio + +.PHONY: clean + +clean: + rm -rf *.o spi-bb mdio-bb diff --git a/common/recipes-utils/bitbang/files/src/bitbang.c b/common/recipes-utils/bitbang/files/src/bitbang.c new file mode 100644 index 0000000..1318f42 --- /dev/null +++ b/common/recipes-utils/bitbang/files/src/bitbang.c @@ -0,0 +1,236 @@ +/* + * Copyright 2014-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +//#define DEBUG +//#define VERBOSE + +#include "bitbang.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <openbmc/log.h> + +#define NANOSEC_IN_SEC (1000 * 1000 * 1000) + +#define BITBANG_FREQ_MAX (500 * 1000 * 1000) /* 500M Hz */ +#define BITBANG_FREQ_DEFAULT (1 * 1000 * 1000) /* 1M Hz */ + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +struct bitbang_handle { + bitbang_init_st bbh_init; + uint32_t bbh_half_clk; /* ns per clock cycle */ +}; + +void bitbang_init_default(bitbang_init_st *init) +{ + memset(init, sizeof(*init), 0); + init->bbi_clk_start = BITBANG_PIN_HIGH; + init->bbi_data_out = BITBANG_CLK_EDGE_FALLING; + init->bbi_data_in = BITBANG_CLK_EDGE_RISING; + init->bbi_freq = BITBANG_FREQ_DEFAULT; +} + +bitbang_handle_st* bitbang_open(const bitbang_init_st *init) +{ + bitbang_handle_st *hdl; + + if (!init || !init->bbi_pin_f + || !init->bbi_freq || init->bbi_freq > BITBANG_FREQ_MAX) { + LOG_ERR(EINVAL, "Invalid init structure"); + return NULL; + } + + hdl = calloc(1, sizeof(*hdl)); + if (!hdl) { + return NULL; + } + + hdl->bbh_init = *init; + hdl->bbh_half_clk = NANOSEC_IN_SEC / init->bbi_freq / 2; + + LOG_DBG("Bitbang open with initial %s, data out at %s, data in at %s, " + "freq at %uHz, half clk %uns", + (init->bbi_clk_start == BITBANG_PIN_LOW) ? "LOW" : "HIGH", + (init->bbi_data_out == BITBANG_CLK_EDGE_RISING) + ? "RISING" : "FALLING", + (init->bbi_data_in == BITBANG_CLK_EDGE_RISING) + ? "RISING" : "FALLING", + init->bbi_freq, hdl->bbh_half_clk); + + return hdl; +} + +void bitbang_close(bitbang_handle_st *hdl) +{ + free(hdl); +} + +/* + * The threshold (ns) to use spin instead of nanosleep(). + * Before adding the high resolution timer support, either spin or nanosleep() + * will not bring the process wakeup within 10ms. It turns out the system time + * update is also controlled by HZ (100). + * After I added the high resolution timer support, the spin works as the + * system time is updated more frequently. However, nanosleep() solution is + * still noticable slower comparing with spin. There could be some kernel + * scheduling tweak missing. Did not get time on that yet. + * For now, use 10ms as the threshold to determine if spin or nanosleep() + * is used. + */ +#define BITBANG_SPIN_THRESHOLD (10 * 1000 * 1000) + +static int sleep_ns(uint32_t clk) +{ + struct timespec req, rem; + int rc = 0; + if (clk <= BITBANG_SPIN_THRESHOLD) { + struct timespec orig; + rc = clock_gettime(CLOCK_MONOTONIC, &req); + orig = req; + while (!rc && clk) { + uint32_t tmp; + rc = clock_gettime(CLOCK_MONOTONIC, &rem); + tmp = (rem.tv_sec - req.tv_sec) * NANOSEC_IN_SEC; + if (rem.tv_nsec >= req.tv_nsec) { + tmp += rem.tv_nsec - req.tv_nsec; + } else { + tmp -= req.tv_nsec - rem.tv_nsec; + } + if (tmp >= clk) { + break; + } + clk -= tmp; + req = rem; + } + } else { + req.tv_sec = 0; + req.tv_nsec = clk; + while ((rc = nanosleep(&req, &rem)) == -1 && errno == EINTR) { + req = rem; + } + } + if (rc == -1) { + rc = errno; + LOG_ERR(rc, "Failed to sleep %u nanoseconds", clk); + } + return rc; +} + +int bitbang_io(const bitbang_handle_st *hdl, bitbang_io_st *io) +{ + int rc = 0; + uint32_t clk = hdl->bbh_half_clk; + const struct { + bitbang_pin_value_en value; + bitbang_clk_edge_en edge; + } clks[] = { + {BITBANG_PIN_HIGH, BITBANG_CLK_EDGE_FALLING}, + {BITBANG_PIN_LOW, BITBANG_CLK_EDGE_RISING}, + }; + int clk_idx; + int n_clk = 0; + int n_bits = 0; + const uint8_t *dout = io->bbio_dout; + uint8_t *din = io->bbio_din; + int bit_pos = 7; + bitbang_pin_func pin_f = hdl->bbh_init.bbi_pin_f; + void *context = hdl->bbh_init.bbi_context; + + if ((io->bbio_in_bits == 0 && io->bbio_din) + || (io->bbio_in_bits > 0 && !io->bbio_din)) { + rc = EINVAL; + LOG_ERR(rc, "Incorrect in bits and in buffer"); + goto out; + } + + if ((io->bbio_out_bits == 0 && io->bbio_dout) + || (io->bbio_out_bits > 0 && !io->bbio_dout)) { + rc = EINVAL; + LOG_ERR(rc, "Incorrect out bits and out buffer"); + goto out; + } + + if (io->bbio_in_bits == 0 && io->bbio_out_bits == 0) { + rc = EINVAL; + LOG_ERR(rc, "Both in and out bits are 0"); + goto out; + } + + if (hdl->bbh_init.bbi_clk_start == BITBANG_PIN_HIGH) { + clk_idx = 0; + } else { + clk_idx = 1; + } + + /* set the CLK pin start position */ + pin_f(BITBANG_CLK_PIN, clks[clk_idx].value, context); + + /* clear the first byte of din */ + if (din && io->bbio_in_bits) { + memset(din, 0, (io->bbio_in_bits + 7) / 8); + } + + do { + if ((rc = sleep_ns(clk))) { + goto out; + } + + /* output first */ + if (hdl->bbh_init.bbi_data_out == clks[clk_idx].edge) { + if (dout && n_bits < io->bbio_out_bits) { + pin_f(BITBANG_DATA_OUT, (*dout >> bit_pos) & 0x1, context); + } + } + + /* then, input */ + if (hdl->bbh_init.bbi_data_in == clks[clk_idx].edge) { + if (din && n_bits < io->bbio_in_bits) { + *din |= (pin_f(BITBANG_DATA_IN, 0, context) & 0x1) << bit_pos; + } + } + + if (++n_clk % 2 == 0) { + /* one bit for every 2 half clks */ + n_bits ++; + if (bit_pos == 0) { + if (dout) { + dout++; + } + if (din) { + din++; + } + bit_pos = 7; + } else { + bit_pos --; + } + } + clk_idx = 1 - clk_idx; + pin_f(BITBANG_CLK_PIN, clks[clk_idx].value, context); + } while (n_bits < MAX(io->bbio_in_bits, io->bbio_out_bits)); + + out: + + return -rc; +} diff --git a/common/recipes-utils/bitbang/files/src/bitbang.h b/common/recipes-utils/bitbang/files/src/bitbang.h new file mode 100644 index 0000000..0f21a49 --- /dev/null +++ b/common/recipes-utils/bitbang/files/src/bitbang.h @@ -0,0 +1,66 @@ +/* + * Copyright 2014-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef BITBANG_H +#define BITBANG_H + +#include <stdint.h> + +typedef enum { + BITBANG_CLK_PIN, + BITBANG_DATA_IN, + BITBANG_DATA_OUT, +} bitbang_pin_type_en; + +typedef enum { + BITBANG_PIN_LOW = 0, + BITBANG_PIN_HIGH = 1, +} bitbang_pin_value_en; + +typedef enum { + BITBANG_CLK_EDGE_RISING, + BITBANG_CLK_EDGE_FALLING, +} bitbang_clk_edge_en; + +typedef bitbang_pin_value_en (* bitbang_pin_func)( + bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context); + +typedef struct { + bitbang_pin_value_en bbi_clk_start; + bitbang_clk_edge_en bbi_data_out; + bitbang_clk_edge_en bbi_data_in; + uint32_t bbi_freq; + bitbang_pin_func bbi_pin_f; + void *bbi_context; +} bitbang_init_st; + +typedef struct bitbang_handle bitbang_handle_st; + +void bitbang_init_default(bitbang_init_st *init); +bitbang_handle_st* bitbang_open(const bitbang_init_st *init); +void bitbang_close(bitbang_handle_st *hdl); + +typedef struct { + uint32_t bbio_in_bits; + uint32_t bbio_out_bits; + uint8_t *bbio_dout; + uint8_t *bbio_din; +} bitbang_io_st; + +int bitbang_io(const bitbang_handle_st *hdl, bitbang_io_st *io); + +#endif diff --git a/common/recipes-utils/bitbang/files/src/mdio_bb.c b/common/recipes-utils/bitbang/files/src/mdio_bb.c new file mode 100644 index 0000000..9137e17 --- /dev/null +++ b/common/recipes-utils/bitbang/files/src/mdio_bb.c @@ -0,0 +1,335 @@ +/* + * Copyright 2014-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +//#define DEBUG +//#define VERBOSE + +#include <stdlib.h> +#include <unistd.h> + +#include <openbmc/log.h> +#include <openbmc/gpio.h> + +#include "bitbang.h" + +typedef struct { + gpio_st m_mdc; + gpio_st m_mdio; +} mdio_context_st; + +/* + * 32b preamble, 2b start of frame, 2b operation code, + * 5b phy addr, 5b register addr, 2b turnaround, 16b data + */ +#define N_BITS (32 + 2 + 2 + 5 + 5 + 2 + 16) +#define N_BYTES (N_BITS + 7 / 8) + +#define START_OF_FRAME 0x1 +#define OP_READ 0x2 +#define OP_WRITE 0x1 +#define TURNAROUND 0x2 /* TA for write, for read, phy sends out TA */ + +void usage() +{ + fprintf(stderr, + "Usage:\n" + "mdio-bb: -c <GPIO for MDC> [-C <HIGH|low>]\n" + " -d <GPIO for MDIO> [-O <rising|FALLING>]\n" + " [-I <RISING|falling>] [-p] [-b]\n" + " <read|write> <phy address> <register address>\n" + " [value to write]\n"); +} + +bitbang_pin_value_en mdio_pin_f( + bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context) +{ + mdio_context_st *ctx = (mdio_context_st *)context; + gpio_st *gpio; + bitbang_pin_value_en res; + + switch (pin) { + case BITBANG_CLK_PIN: + gpio = &ctx->m_mdc; + break; + case BITBANG_DATA_IN: + case BITBANG_DATA_OUT: + gpio = &ctx->m_mdio; + break; + } + if (pin == BITBANG_DATA_IN) { + res = gpio_read(gpio) ? BITBANG_PIN_HIGH : BITBANG_PIN_LOW; + } else { + res = value; + gpio_write(gpio, ((res == BITBANG_PIN_HIGH) + ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW)); + } + return res; +} + +int main(int argc, char* const argv[]) +{ + int opt; + int mdc = -1, mdio = -1; + bitbang_pin_value_en mdc_start = BITBANG_PIN_HIGH; + bitbang_clk_edge_en out_edge = BITBANG_CLK_EDGE_FALLING; + bitbang_clk_edge_en in_edge = BITBANG_CLK_EDGE_RISING; + int is_write; + uint32_t phy_addr; + uint32_t reg_addr; + uint32_t data; /* data to write/read*/ + uint8_t buf[N_BYTES]; + uint8_t *buf_p; + mdio_context_st ctx; + bitbang_init_st init; + bitbang_handle_st *hdl = NULL; + bitbang_io_st io; + int n_bits; + int i; + int rc = 0; + int preamble = 0; + int binary = 0; + + while ((opt = getopt(argc, argv, "bc:C:d:D:p")) != -1) { + switch (opt) { + case 'b': + binary = 1; + break; + case 'c': + mdc = atoi(optarg); + break; + case 'C': + if (!strcasecmp(optarg, "high")) { + mdc_start = BITBANG_PIN_HIGH; + } else if (!strcasecmp(optarg, "low")) { + mdc_start = BITBANG_PIN_LOW; + } else { + usage(); + exit(-1); + } + break; + case 'd': + mdio = atoi(optarg); + break; + case 'I': + if (!strcasecmp(optarg, "rising")) { + in_edge = BITBANG_CLK_EDGE_RISING; + } if (!strcasecmp(optarg, "falling")) { + in_edge = BITBANG_CLK_EDGE_FALLING; + } else { + usage(); + exit(-1); + } + break; + case 'O': + if (!strcasecmp(optarg, "rising")) { + out_edge = BITBANG_CLK_EDGE_RISING; + } if (!strcasecmp(optarg, "falling")) { + out_edge = BITBANG_CLK_EDGE_FALLING; + } else { + usage(); + exit(-1); + } + break; + case 'p': + preamble = 1; + break; + default: + usage(); + exit(-1); + } + } + + if (mdc < 0 || mdio < 0) { + usage(); + exit(-1); + } + + if (optind + 2 >= argc) { + usage(); + exit(-1); + } + + /* read or write */ + if (!strcasecmp(argv[optind], "read")) { + is_write = 0; + } else if (!strcasecmp(argv[optind], "write")) { + is_write = 1; + } else { + usage(); + exit(-1); + } + + /* phy address, 5 bits only, so must be <= 0x1f */ + phy_addr = strtoul(argv[optind + 1], NULL, 0); + if (phy_addr > 0x1f) { + usage(); + exit(-1); + } + + /* register address, 5 bits only, so must be <= 0x1f */ + reg_addr = strtoul(argv[optind + 2], NULL, 0); + if (reg_addr > 0x1f) { + usage(); + exit(-1); + } + + /* data */ + if (is_write) { + if ((!binary && (optind + 4 != argc)) || + (binary && (optind + 3 != argc))) { + usage(); + exit(-1); + } + if (binary) { + uint16_t temp = 0; + if (fread(&temp, sizeof(temp), 1, stdin) != 1) { + usage(); + exit(-1); + } + data = htons(temp); + } else { + data = strtoul(argv[optind + 3], NULL, 0); + } + if (data > 0xFFFF) { + usage(); + exit(-1); + } + } else { + if ((!binary && (optind + 3 != argc)) || + (binary && (optind + 2 != argc))) { + usage(); + exit(-1); + } + } + + /* open all gpio */ + memset(&ctx, sizeof(ctx), 0); + gpio_init_default(&ctx.m_mdc); + gpio_init_default(&ctx.m_mdio); + if (gpio_open(&ctx.m_mdc, mdc) || gpio_open(&ctx.m_mdio, mdio)) { + goto out; + } + + if (gpio_change_direction(&ctx.m_mdc, GPIO_DIRECTION_OUT) + || gpio_change_direction(&ctx.m_mdio, GPIO_DIRECTION_OUT)) { + goto out; + } + + bitbang_init_default(&init); + init.bbi_clk_start = mdc_start; + init.bbi_data_out = out_edge; + init.bbi_data_in = in_edge; + init.bbi_freq = 1000 * 1000; /* 1M Hz */ + init.bbi_pin_f = mdio_pin_f; + init.bbi_context = &ctx; + hdl = bitbang_open(&init); + if (!hdl) { + goto out; + } + + if (is_write) { + buf[0] = (data >> 8) & 0xFF; + buf[1] = data & 0xFF; + io.bbio_out_bits = 16; + io.bbio_dout = buf; + io.bbio_in_bits = 0; + io.bbio_din = NULL; + } else { + io.bbio_in_bits = 16; + io.bbio_din = buf; + io.bbio_out_bits = 0; + io.bbio_dout = NULL; + } + + /* preamble, 32b */ + buf_p = buf; + n_bits = 0; + if (preamble) { + /* 32 bit of 1 for preamble */ + for (i = 0; i < 4; i++) { + *buf_p++ = 0xFF; + } + n_bits += 32; + } + + /* + * MDIO transaction header is: + * 2b START, 2b OPER CODE, 5b PHY ADDR, 5b register addr, 2b TURNROUND + */ + *buf_p++ = (START_OF_FRAME << 6) | (((is_write) ? OP_WRITE : OP_READ) << 4) + | ((phy_addr >> 1) & 0xF); + *buf_p++ = ((phy_addr & 0x1) << 7) | ((reg_addr & 0x1F) << 2) | TURNAROUND; + if (is_write) { + *buf_p++ = (data >> 8) & 0xFF; + *buf_p++ = data & 0xFF; + /* total # of bits is transaction header + 2 bytes to write */ + n_bits += 2 + 2 + 5 + 5 + 2 + 16; + } else { + /* for read, master does not send TR, so, n_bits should not include TR */ + n_bits += 2 + 2 + 5 + 5; + } + + memset(&io, sizeof(io), 0); + io.bbio_out_bits = n_bits; + io.bbio_dout = buf; + io.bbio_in_bits = 0; + io.bbio_din = NULL; + + rc = bitbang_io(hdl, &io); + if (rc != 0) { + goto out; + } + + /* for read, need to do another io for (2b TR + 16b data) reading */ + if (!is_write) { + /* first, change the MDIO to input */ + gpio_change_direction(&ctx.m_mdio, GPIO_DIRECTION_IN); + /* then, run the clock for read */ + memset(&io, sizeof(io), 0); + io.bbio_out_bits = 0; + io.bbio_dout = NULL;; + io.bbio_in_bits = 18; + io.bbio_din = buf; + + rc = bitbang_io(hdl, &io); + if (rc != 0) { + goto out; + } + + data = ((buf[0] << 2) | (buf[1] >> 6)) & 0xFF; + data <<= 8; + data |= ((buf[1] << 2) | (buf[2] >> 6)) & 0xFF; + } + + if (binary) { + if (!is_write) { + uint16_t temp = ntohs(data); + fwrite(&temp, sizeof(temp), 1, stdout); + } + } else { + printf("%s: 0x%02x\n", (is_write) ? "Wrote" : "Read", data); + } + + out: + if (hdl) { + bitbang_close(hdl); + } + gpio_close(&ctx.m_mdc); + gpio_close(&ctx.m_mdio); + + return 0; +} diff --git a/common/recipes-utils/bitbang/files/src/spi_bb.c b/common/recipes-utils/bitbang/files/src/spi_bb.c new file mode 100644 index 0000000..2125877 --- /dev/null +++ b/common/recipes-utils/bitbang/files/src/spi_bb.c @@ -0,0 +1,317 @@ +/* + * Copyright 2014-present Facebook. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +//#define DEBUG +//#define VERBOSE + +#include <stdlib.h> +#include <unistd.h> + +#include <openbmc/gpio.h> +#include <openbmc/log.h> + +#include "bitbang.h" + +void usage() +{ + fprintf(stderr, + "Usage:\n" + "spi-bb: -s <GPIO for CS> [-S <HIGH|low>]\n" + " -c <GPIO for CLK> [-C <HIGH|low>]\n" + " -o <GPIO for MOSI> [-O <rising|FALLING>]\n" + " -i <GPIO for MISO> [-I <RISING|falling>]\n" + " [-b]\n" + " < [-r <number of bits to read>]\n" + " [-w <number of bits to write> <byte 1> [... byte N]>\n\n" + "Note: If both '-r' and '-w' are provided, 'write' will be performed\n" + " before 'read'.\n"); +} + +typedef struct { + gpio_st sc_clk; + gpio_st sc_mosi; + gpio_st sc_miso; +} spi_context_st; + +bitbang_pin_value_en spi_pin_f( + bitbang_pin_type_en pin, bitbang_pin_value_en value, void *context) +{ + spi_context_st *ctx = (spi_context_st *)context; + gpio_st *gpio; + bitbang_pin_value_en res; + + switch (pin) { + case BITBANG_CLK_PIN: + gpio = &ctx->sc_clk; + break; + case BITBANG_DATA_IN: + gpio = &ctx->sc_miso; + break; + case BITBANG_DATA_OUT: + gpio = &ctx->sc_mosi; + break; + } + if (pin == BITBANG_DATA_IN) { + res = gpio_read(gpio) ? BITBANG_PIN_HIGH : BITBANG_PIN_LOW; + } else { + res = value; + gpio_write(gpio, ((res == BITBANG_PIN_HIGH) + ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW)); + } + return res; +} + +int main(int argc, char * const argv[]) +{ + bitbang_init_st init; + bitbang_handle_st *hdl = NULL; + int cs = -1, clk = -1, in = -1, out = -1; + gpio_st cs_gpio; + int opt; + int is_write = 0; + int is_read = 0; + int read_bits = 0; + int write_bits = 0; + int read_bytes = 0; + int write_bytes = 0; + int i; + uint8_t *read_buf = NULL;; + uint8_t *write_buf = NULL;; + bitbang_clk_edge_en dout_edge = BITBANG_CLK_EDGE_FALLING; + bitbang_clk_edge_en din_edge = BITBANG_CLK_EDGE_RISING; + bitbang_pin_value_en clk_start = BITBANG_PIN_HIGH; + bitbang_pin_value_en cs_value = BITBANG_PIN_HIGH; + spi_context_st ctx; + bitbang_io_st io; + int rc = 0; + int binary = 0; + + memset(&ctx, sizeof(ctx), 0); + gpio_init_default(&ctx.sc_clk); + gpio_init_default(&ctx.sc_mosi); + gpio_init_default(&ctx.sc_miso); + gpio_init_default(&cs_gpio); + + while ((opt = getopt(argc, argv, "bs:S:c:C:o:O:i:I:w:r:")) != -1) { + switch (opt) { + case 'b': + binary = 1; + break; + case 's': + cs = atoi(optarg); + break; + case 'S': + if (!strcmp(optarg, "high")) { + cs_value = BITBANG_PIN_HIGH; + } else if (!strcmp(optarg, "low")) { + cs_value = BITBANG_PIN_LOW; + } else { + usage(); + exit(-1); + } + break; + case 'c': + clk = atoi(optarg); + break; + case 'C': + if (!strcasecmp(optarg, "high")) { + clk_start = BITBANG_PIN_HIGH; + } else if (!strcasecmp(optarg, "low")) { + clk_start = BITBANG_PIN_LOW; + } else { + usage(); + exit(-1); + } + break; + case 'o': + out = atoi(optarg); + break; + case 'O': + if (!strcasecmp(optarg, "rising")) { + dout_edge = BITBANG_CLK_EDGE_RISING; + } else if (!strcasecmp(optarg, "falling")) { + dout_edge = BITBANG_CLK_EDGE_FALLING; + } else { + usage(); + exit(-1); + } + break; + case 'i': + in = atoi(optarg); + break; + case 'I': + if (!strcasecmp(optarg, "rising")) { + din_edge = BITBANG_CLK_EDGE_RISING; + } else if (!strcasecmp(optarg, "falling")) { + din_edge = BITBANG_CLK_EDGE_FALLING; + } else { + usage(); + exit(-1); + } + break; + case 'w': + is_write = 1; + write_bits = atoi(optarg); + if (write_bits <= 0) { + usage(); + exit(-1); + } + break; + case 'r': + is_read = 1; + read_bits = atoi(optarg); + if (read_bits <= 0) { + usage(); + exit(-1); + } + break; + default: + usage(); + exit(-1); + } + } + + if (clk < 0 || in < 0 || out < 0) { + usage(); + exit(-1); + } + + if ((!is_read && !is_write)) { + usage(); + exit(-1); + } + + write_bytes = ((write_bits + 7) / 8); + if (write_bytes) { + write_buf = calloc(write_bytes, sizeof(uint8_t)); + if (!write_buf) { + goto out; + } + if (binary) { + size_t written_bytes; + written_bytes = fread(write_buf, sizeof(*write_buf), write_bytes, stdin); + if( written_bytes != write_bytes ) { + goto out; + } + } else { + for (i = 0; i < write_bytes && i + optind < argc; i++) { + write_buf[i] = strtoul(argv[i + optind], NULL, 0); + } + } + } + + read_bytes = ((read_bits + 7) / 8); + if (read_bytes) { + read_buf = calloc(read_bytes, sizeof(uint8_t)); + if (!read_buf) { + goto out; + } + } + + if (gpio_open(&ctx.sc_clk, clk) || gpio_open(&ctx.sc_miso, in) + || gpio_open(&ctx.sc_mosi, out)) { + goto out; + } + + /* change GPIO directions, only MISO is input, all others are output */ + if (gpio_change_direction(&ctx.sc_clk, GPIO_DIRECTION_OUT) + || gpio_change_direction(&ctx.sc_miso, GPIO_DIRECTION_IN) + || gpio_change_direction(&ctx.sc_mosi, GPIO_DIRECTION_OUT)) { + goto out; + } + + if (cs != -1) { + if (gpio_open(&cs_gpio, cs)) { + goto out; + } + if (gpio_change_direction(&cs_gpio, GPIO_DIRECTION_OUT)) { + goto out; + } + } + + bitbang_init_default(&init); + init.bbi_clk_start = clk_start; + init.bbi_data_out = dout_edge; + init.bbi_data_in = din_edge; + init.bbi_freq = 1000 * 1000; /* 1M Hz */ + init.bbi_pin_f = spi_pin_f; + init.bbi_context = &ctx; + + hdl = bitbang_open(&init); + if (!hdl) { + goto out; + } + + if (cs != -1) { + /* have chip select */ + gpio_write(&cs_gpio, ((cs_value == BITBANG_PIN_HIGH) + ? GPIO_VALUE_HIGH : GPIO_VALUE_LOW)); + } + + memset(&io, sizeof(io), 0); + io.bbio_in_bits = read_bits; + io.bbio_din = read_buf; + io.bbio_out_bits = write_bits; + io.bbio_dout = write_buf; + + rc = bitbang_io(hdl, &io); + if (rc != 0) { + goto out; + } + + if (binary) { + fwrite(read_buf, sizeof(*read_buf), read_bytes, stdout); + } else { + if (write_bits) { + printf("Wrote %u bits:", write_bits); + for (i = 0; i < write_bytes; i++) { + printf(" %02x", write_buf[i]); + } + printf("\n"); + } + + if (read_bits) { + printf("Read %u bits:", read_bits); + for (i = 0; i < read_bytes; i++) { + printf(" %02x", read_buf[i]); + } + printf("\n"); + } + } + + out: + if (hdl) { + bitbang_close(hdl); + } + gpio_close(&ctx.sc_clk); + gpio_close(&ctx.sc_miso); + gpio_close(&ctx.sc_mosi); + if (cs != -1) { + /* reset have chip select */ + gpio_write(&cs_gpio, ((cs_value == BITBANG_PIN_HIGH) + ? GPIO_VALUE_LOW : GPIO_VALUE_HIGH)); + gpio_close(&cs_gpio); + } + + if (read_buf) { + free(read_buf); + } + if (write_buf) { + free(write_buf); + } + return rc; +} |