[spidev + nixio + fluksod] allow the bitbanged spi max speed and inter-byte delay to be dynamically set from user space

This commit is contained in:
Bart Van Der Meerssche 2011-01-19 22:12:49 +01:00
parent d208545000
commit 0d3f076b5f
10 changed files with 366 additions and 127 deletions

View File

@ -37,10 +37,10 @@ local SPI_MAX_READ_BYTES = 256
-- Attributes:
-- { to = ctrl | delta | uart
-- body = <string>
-- parsed = { <command>, <arg1>, <arg2>, ... }
-- parsed = { 'cmd' = <command>, 1 = <arg1>, 2 = <arg2>, ... }
-- encoded = <string>
-- received = { raw = <string>, l = <string>, crc = <string>, u = <string> }
-- decoded = { l = ..., u = ... }
-- decoded = { args = <string>, cmd = <string>, 1 = <arg1>, 2= <arg2>, ..., u = <string> }
-- reply = <string>
-- }
--
@ -54,7 +54,7 @@ end
function parse(msg)
msg.parsed = {}
msg.parsed[1] = msg.body:match('^%l%l')
msg.parsed.cmd = msg.body:match('^%l%l')
for arg in msg.body:gmatch('%d+') do
msg.parsed[#msg.parsed + 1] = arg
end
@ -68,33 +68,33 @@ function encode(msg)
return
end
if msg.parsed[1] == 'gd' then
msg.encoded = msg.parsed[1]
elseif msg.parsed[1] == 'gv' then
if msg.parsed.cmd == 'gd' then
msg.encoded = msg.parsed.cmd
elseif msg.parsed.cmd == 'gv' then
elseif msg.parsed[1] == 'gp' then
elseif msg.parsed.cmd == 'gp' then
elseif msg.parsed[1] == 'gc' then
elseif msg.parsed.cmd == 'gc' then
elseif msg.parsed[1] == 'gm' then
elseif msg.parsed.cmd == 'gm' then
elseif msg.parsed[1] == 'gw' then
elseif msg.parsed.cmd == 'gw' then
elseif msg.parsed[1] == 'gb' then
elseif msg.parsed.cmd == 'gb' then
elseif msg.parsed[1] == 'sv' then
elseif msg.parsed.cmd == 'sv' then
elseif msg.parsed[1] == 'sp' then
elseif msg.parsed.cmd == 'sp' then
elseif msg.parsed[1] == 'sc' then
elseif msg.parsed.cmd == 'sc' then
elseif msg.parsed[1] == 'sm' then
elseif msg.parsed.cmd == 'sm' then
elseif msg.parsed[1] == 'sw' then
elseif msg.parsed.cmd == 'sw' then
elseif msg.parsed[1] == 'sb' then
elseif msg.parsed.cmd == 'sb' then
elseif msg.parsed[1] == 'ct' then
elseif msg.parsed.cmd == 'ct' then
else
@ -116,15 +116,20 @@ function rx(msg, cdev)
msg.received.raw = cdev:read(SPI_MAX_READ_BYTES)
msg.received.l, msg.received.u = msg.received.raw:match('^l(%w*)%.?u(%w*)%.?$')
-- protect against nil values when match should fail
-- TODO error handling when no reply due to:
-- * sensor board not operational
-- * state machine not synced
msg.received.l, msg.received.u = msg.received.l or '', msg.received.u or ''
if msg.received.l ~= '' and msg.received.l:sub(1, 2) == msg.parsed[1] then
if msg.received.l ~= '' then
msg.received.crc = msg.received.l:sub(-2, -1)
msg.received.l = msg.received.l:sub(1, -3)
msg.received.l = msg.received.l:sub( 1, -3)
if nixio.bin.dow_crc(msg.received.l) ~= nixio.bin.hextonum(msg.received.crc) then
--> TODO implement crc error counter
--> TODO implement near-end crc error counter
msg.received.l = ''
else
end
end
end
@ -137,43 +142,46 @@ function decode(msg)
end
if msg.received.l ~= '' then
msg.decoded.largs = msg.received.l:sub(3, -1)
msg.decoded.cmd = msg.received.l:sub(1, 2)
msg.decoded.args = msg.received.l:sub(3, -1)
if msg.parsed[1] == 'gd' then
for i = 1, msg.decoded.largs:len() / 18 do
if msg.decoded.cmd == 'gd' then
for i = 1, msg.decoded.args:len() / 18 do
msg.decoded[(i-1)*3 + 1] =
nixio.bin.hextonum(msg.decoded.largs:sub((i-1)*18 + 1, (i-1)*18 + 2))
nixio.bin.hextonum(msg.decoded.args:sub((i-1)*18 + 1, (i-1)*18 + 2))
msg.decoded[(i-1)*3 + 2] =
nixio.bin.hextonum(msg.decoded.largs:sub((i-1)*18 + 3, (i-1)*18 + 10))
nixio.bin.hextonum(msg.decoded.args:sub((i-1)*18 + 3, (i-1)*18 + 10))
msg.decoded[(i-1)*3 + 3] =
nixio.bin.hextonum(msg.decoded.largs:sub((i-1)*18 + 11, (i-1)*18 + 18))
nixio.bin.hextonum(msg.decoded.args:sub((i-1)*18 + 11, (i-1)*18 + 18))
end
elseif msg.parsed[1] == 'gv' then
elseif msg.parsed.cmd == 'gv' then
elseif msg.parsed[1] == 'gp' then
elseif msg.parsed.cmd == 'gp' then
elseif msg.parsed[1] == 'gc' then
elseif msg.parsed.cmd == 'gc' then
elseif msg.parsed[1] == 'gm' then
elseif msg.parsed.cmd == 'gm' then
elseif msg.parsed[1] == 'gw' then
elseif msg.parsed.cmd == 'gw' then
elseif msg.parsed[1] == 'gb' then
elseif msg.parsed.cmd == 'gb' then
elseif msg.parsed[1] == 'sv' then
elseif msg.parsed.cmd == 'sv' then
elseif msg.parsed[1] == 'sp' then
elseif msg.parsed.cmd == 'sp' then
elseif msg.parsed[1] == 'sc' then
elseif msg.parsed.cmd == 'sc' then
elseif msg.parsed[1] == 'sm' then
elseif msg.parsed.cmd == 'sm' then
elseif msg.parsed[1] == 'sw' then
elseif msg.parsed.cmd == 'sw' then
elseif msg.parsed[1] == 'sb' then
elseif msg.parsed.cmd == 'sb' then
elseif msg.parsed[1] == 'ct' then
elseif msg.parsed.cmd == 'ct' then
elseif msg.decoded.cmd == 'zz' then
--> TODO implement far-end crc error counter
end
end
end

View File

@ -72,7 +72,7 @@ while true do
msg:rx(spidev)
msg:decode()
--> dbg.vardump(msg)
delta.fdout:write(msg.parsed[1] .. ' ' .. table.concat(msg.decoded, ' ') .. '\n')
delta.fdout:write(msg.decoded.cmd .. ' ' .. table.concat(msg.decoded, ' ') .. '\n')
end
end
end

View File

@ -15,7 +15,7 @@ NIXIO_SO = nixio.so
NIXIO_OBJ = src/nixio.o src/socket.o src/sockopt.o src/bind.o src/address.o \
src/poll.o src/io.o src/file.o src/splice.o src/process.o src/syslog.o \
src/bit.o src/binary.o src/fs.o src/user.o \
src/bit.o src/binary.o src/fs.o src/user.o src/spi.o \
$(if $(NIXIO_TLS),src/tls-crypto.o src/tls-context.o src/tls-socket.o,)
ifeq ($(NIXIO_TLS),axtls)

View File

@ -223,68 +223,6 @@ static int nixio_file_read(lua_State *L) {
}
}
static int nixio_file_spi_read(lua_State *L) {
int fd = nixio__checkfd(L, 1);
char buffer[NIXIO_BUFFERSIZE];
int readc;
size_t len;
char last = 0;
for (size_t i = 0; i < NIXIO_BUFFERSIZE; i++) {
do {
readc = read(fd, buffer + i, 1);
} while (readc == -1 && errno == EINTR);
if (readc < 0) {
return nixio__perror(L);
}
if (last) {
break;
}
if (buffer[i] == 0x00) {
len = i;
last = 1; /* one last pass through the for loop to sync the state machine */
}
}
/* REVISIT: a read on spidev doens't work for numbytes > 1 */
/* for (size_t i = 0; i < NIXIO_BUFFERSIZE; i += 2) {
do {
readc = read(fd, buffer + i, 2);
} while (readc == -1 && errno == EINTR);
if (readc < 0) {
return nixio__perror(L);
}
if (buffer[i + 1] == 0x00) {
len = i;
if (buffer[i] != 0x00) {
len = i + 1;
do {
readc = read(fd, buffer + i + 2, 1);
} while (readc == -1 && errno == EINTR);
if (readc < 0) {
return nixio__perror(L);
}
}
break;
}
}
*/
lua_pushlstring(L, buffer, len);
return 1;
}
static int nixio_file_seek(lua_State *L) {
int fd = nixio__checkfd(L, 1);
@ -412,7 +350,6 @@ static int nixio_file__tostring(lua_State *L) {
static const luaL_reg M[] = {
{"write", nixio_file_write},
{"read", nixio_file_read},
{"spiread", nixio_file_spi_read},
{"tell", nixio_file_tell},
{"seek", nixio_file_seek},
{"stat", nixio_file_stat},

View File

@ -142,6 +142,7 @@ NIXIO_API int luaopen_nixio(lua_State *L) {
nixio_open_bin(L);
nixio_open_fs(L);
nixio_open_user(L);
nixio_open_spi(L);
#ifndef NO_TLS
nixio_open_tls_crypto(L);

View File

@ -120,6 +120,7 @@ void nixio_open_bit(lua_State *L);
void nixio_open_bin(lua_State *L);
void nixio_open_fs(lua_State *L);
void nixio_open_user(lua_State *L);
void nixio_open_spi(lua_State *L);
#ifndef NO_TLS
void nixio_open_tls_crypto(lua_State *L);

View File

@ -0,0 +1,89 @@
/*
* nixio - Linux I/O library for lua
*
* Copyright (C) 2011 Bart Van Der Meerssche <bart.vandermeerssche@flukso.net>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nixio.h"
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
/* Defined in linux/spi/spidev.h, but this doesn't seem to propagate to the openwrt staging dir */
/* Read / Write SPI device default delay us */
#define SPI_IOC_RD_DELAY_US _IOR(SPI_IOC_MAGIC, 5, __u32)
#define SPI_IOC_WR_DELAY_US _IOW(SPI_IOC_MAGIC, 5, __u32)
static int nixio_spi_setspeed(lua_State *L) {
int fd = nixio__checkfd(L, 1);
ulong speed_hz = luaL_checkinteger(L, 2);
uint delay_usecs = luaL_checkinteger(L, 3);
if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz) < 0) {
return nixio__perror(L);
} else if (ioctl(fd, SPI_IOC_WR_DELAY_US, &delay_usecs) < 0) {
return nixio__perror(L);
}
return 0;
}
/* not really needed anymore since this is now coded into the spi_bitbang kmod */
static int nixio_spi_read(lua_State *L) {
int fd = nixio__checkfd(L, 1);
char buffer[NIXIO_BUFFERSIZE];
int readc;
size_t len;
char last = 0;
for (size_t i = 0; i < NIXIO_BUFFERSIZE; i++) {
do {
readc = read(fd, buffer + i, 1);
} while (readc == -1 && errno == EINTR);
if (readc < 0) {
return nixio__perror(L);
}
if (last) {
break;
}
if (buffer[i] == 0x00) {
len = i;
last = 1; /* one last pass through the for loop to sync the state machine */
}
}
lua_pushlstring(L, buffer, len);
return 1;
}
/* module table */
static const luaL_reg R[] = {
{"setspeed", nixio_spi_setspeed},
{"read", nixio_spi_read},
{NULL, NULL}
};
void nixio_open_spi(lua_State *L) {
lua_newtable(L);
luaL_register(L, NULL, R);
lua_setfield(L, -2, "spi");
}

View File

@ -41,7 +41,7 @@
+ .modalias = "spidev",
+ .controller_data = (void *) SPI_GPIO_NO_CHIPSELECT,
+ .mode = SPI_MODE_0,
+ .max_speed_hz = 16000000, /* 16kHz */
+ .max_speed_hz = 1000000, /* 1Mhz max, but the bitbanged spi doesn't clock faster than 572kHz */
+ .bus_num = 0,
+},
+};

View File

@ -15,5 +15,5 @@
- .controller_data = (void *) SPI_GPIO_NO_CHIPSELECT,
+ .controller_data = (void *) SPI_GPIO_CS,
.mode = SPI_MODE_0,
.max_speed_hz = 10000000, /* 10kHz */
.max_speed_hz = 1000000, /* 1Mhz max, but the bitbanged spi doesn't clock faster than 572kHz */
.bus_num = 0,

View File

@ -1,32 +1,173 @@
--- a/drivers/spi/spi_gpio.c 2010-12-14 01:02:26.673204002 +0100
+++ b/drivers/spi/spi_gpio.c 2011-01-15 00:26:44.437652996 +0100
@@ -158,7 +158,9 @@
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+ /* shift out a byte in 165us, then pause for 335us */
+ ndelay(335);
+ return bitbang_txrx_be_cpha0(spi, 5, 0, word, 8);
}
--- a/include/linux/spi/spi.h 2009-12-04 07:00:07.000000000 +0100
+++ b/include/linux/spi/spi.h 2011-01-18 14:30:48.919450001 +0100
@@ -68,6 +68,7 @@
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
+ u16 delay_usecs;
u8 chip_select;
u8 mode;
#define SPI_CPHA 0x01 /* clock phase */
--- a/include/linux/spi/spidev.h 2009-12-04 07:00:07.000000000 +0100
+++ b/include/linux/spi/spidev.h 2011-01-18 14:09:47.719449999 +0100
@@ -124,6 +124,8 @@
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
-
+/* Read / Write SPI device default delay us */
+#define SPI_IOC_RD_DELAY_US _IOR(SPI_IOC_MAGIC, 5, __u32)
+#define SPI_IOC_WR_DELAY_US _IOW(SPI_IOC_MAGIC, 5, __u32)
#endif /* SPIDEV_H */
--- a/include/linux/spi/spi_bitbang.h 2009-12-04 07:00:07.000000000 +0100
+++ b/include/linux/spi/spi_bitbang.h 2011-01-18 13:33:26.731450001 +0100
@@ -50,7 +50,7 @@
/* txrx_word[SPI_MODE_*]() just looks like a shift register */
u32 (*txrx_word[4])(struct spi_device *spi,
- unsigned nsecs,
+ unsigned bit_delay, unsigned byte_delay,
u32 word, u8 bits);
};
@@ -102,20 +102,22 @@
static inline u32
bitbang_txrx_be_cpha0(struct spi_device *spi,
- unsigned nsecs, unsigned cpol,
- u32 word, u8 bits)
+ unsigned bit_delay, unsigned byte_delay,
+ unsigned cpol, u32 word, u8 bits)
{
/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
+ spidelay(byte_delay);
+
/* clock starts at inactive polarity */
for (word <<= (32 - bits); likely(bits); bits--) {
/* setup MSB (to slave) on trailing edge */
setmosi(spi, word & (1 << 31));
- spidelay(nsecs); /* T(setup) */
+ spidelay(bit_delay); /* T(setup) */
setsck(spi, !cpol);
- spidelay(nsecs);
+ spidelay(bit_delay);
/* sample MSB (from slave) on leading edge */
word <<= 1;
@@ -127,21 +129,23 @@
static inline u32
bitbang_txrx_be_cpha1(struct spi_device *spi,
- unsigned nsecs, unsigned cpol,
- u32 word, u8 bits)
+ unsigned bit_delay, unsigned byte_delay,
+ unsigned cpol, u32 word, u8 bits)
{
/* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */
+ spidelay(byte_delay);
+
/* clock starts at inactive polarity */
for (word <<= (32 - bits); likely(bits); bits--) {
/* setup MSB (to slave) on leading edge */
setsck(spi, !cpol);
setmosi(spi, word & (1 << 31));
- spidelay(nsecs); /* T(setup) */
+ spidelay(bit_delay); /* T(setup) */
setsck(spi, cpol);
- spidelay(nsecs);
+ spidelay(bit_delay);
/* sample MSB (from slave) on trailing edge */
word <<= 1;
--- a/drivers/spi/spidev.c 2009-12-04 07:00:07.000000000 +0100
+++ b/drivers/spi/spidev.c 2011-01-18 14:15:00.971449999 +0100
@@ -362,6 +362,9 @@
case SPI_IOC_RD_MAX_SPEED_HZ:
retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
break;
+ case SPI_IOC_RD_DELAY_US:
+ retval = __put_user(spi->delay_usecs, (__u32 __user *)arg);
+ break;
/* write requests */
case SPI_IOC_WR_MODE:
@@ -426,7 +429,19 @@
dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
}
break;
+ case SPI_IOC_WR_DELAY_US:
+ retval = __get_user(tmp, (__u32 __user *)arg);
+ if (retval == 0) {
+ u32 save = spi->delay_usecs;
+ spi->delay_usecs = tmp;
+ retval = spi_setup(spi);
+ if (retval < 0)
+ spi->delay_usecs = save;
+ else
+ dev_dbg(&spi->dev, "%d us delay\n", tmp);
+ }
+ break;
default:
/* segmented and/or full-duplex I/O request */
if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
--- a/drivers/spi/spi_bitbang.c 2009-12-04 07:00:07.000000000 +0100
+++ b/drivers/spi/spi_bitbang.c 2011-01-15 00:45:44.257653000 +0100
@@ -75,13 +75,28 @@
+++ b/drivers/spi/spi_bitbang.c 2011-01-18 20:53:42.079449999 +0100
@@ -49,12 +49,14 @@
struct spi_bitbang_cs {
unsigned nsecs; /* (clock cycle time)/2 */
- u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs,
+ u32 (*txrx_word)(struct spi_device *spi,
+ unsigned bit_delay, unsigned byte_delay,
u32 word, u8 bits);
unsigned (*txrx_bufs)(struct spi_device *,
u32 (*txrx_word)(
struct spi_device *spi,
- unsigned nsecs,
+ unsigned bit_delay,
+ unsigned byte_delay,
u32 word, u8 bits),
unsigned, struct spi_transfer *);
};
@@ -62,26 +64,42 @@
static unsigned bitbang_txrx_8(
struct spi_device *spi,
u32 (*txrx_word)(struct spi_device *spi,
- unsigned nsecs,
+ unsigned bit_delay, unsigned byte_delay,
u32 word, u8 bits),
- unsigned ns,
+ unsigned bit_delay,
struct spi_transfer *t
) {
unsigned bits = spi->bits_per_word;
unsigned count = t->len;
const u8 *tx = t->tx_buf;
u8 *rx = t->rx_buf;
+ unsigned byte_delay = spi->delay_usecs;
while (likely(count > 0)) {
u8 word = 0;
- if (tx)
+ if (unlikely(tx))
word = *tx++;
word = txrx_word(spi, ns, word, bits);
- word = txrx_word(spi, ns, word, bits);
- if (rx)
+ word = txrx_word(spi, bit_delay, byte_delay, word, bits);
+ if (likely(rx)) {
+ /* If we receive a 0x00, fetch one extra byte to sync
+ the state machine, then break out of the while loop. */
+ if (unlikely(!word)) {
+ txrx_word(spi, ns, 0x00, bits); /* discard */
+ txrx_word(spi, bit_delay, byte_delay, 0x00, bits); /* discard */
+ break;
+ }
+
@ -37,14 +178,38 @@
+
+ if (unlikely(tx)) {
+ /* Signal the end of tx by sending two 0x00's. */
+ txrx_word(spi, ns, 0x00, bits);
+ txrx_word(spi, ns, 0x00, bits);
+ txrx_word(spi, bit_delay, byte_delay, 0x00, bits);
+ txrx_word(spi, bit_delay, byte_delay, 0x00, bits);
+ }
+
return t->len - count;
}
@@ -346,12 +361,6 @@
@@ -156,10 +174,10 @@
bits_per_word = spi->bits_per_word;
if (bits_per_word <= 8)
cs->txrx_bufs = bitbang_txrx_8;
- else if (bits_per_word <= 16)
+/* else if (bits_per_word <= 16)
cs->txrx_bufs = bitbang_txrx_16;
else if (bits_per_word <= 32)
- cs->txrx_bufs = bitbang_txrx_32;
+ cs->txrx_bufs = bitbang_txrx_32; */
else
return -EINVAL;
@@ -167,8 +185,8 @@
if (!hz)
hz = spi->max_speed_hz;
if (hz) {
- cs->nsecs = (1000000000/2) / hz;
- if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))
+ cs->nsecs = (1000000/2) / hz;
+ if (cs->nsecs > (MAX_UDELAY_MS * 1000))
return -EINVAL;
}
@@ -346,12 +364,6 @@
}
if (status > 0)
m->actual_length += status;
@ -57,3 +222,41 @@
status = 0;
/* protocol tweaks before next transfer */
--- a/drivers/spi/spi_gpio.c 2010-12-14 01:02:26.673204002 +0100
+++ b/drivers/spi/spi_gpio.c 2011-01-18 13:29:17.915450000 +0100
@@ -156,27 +156,27 @@
*/
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
+ unsigned bit_delay, unsigned byte_delay, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha0(spi, bit_delay, byte_delay, 0, word, bits);
}
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
+ unsigned bit_delay, unsigned byte_delay, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
+ return bitbang_txrx_be_cpha1(spi, bit_delay, byte_delay, 0, word, bits);
}
static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
+ unsigned bit_delay, unsigned byte_delay, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha0(spi, bit_delay, byte_delay, 1, word, bits);
}
static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
- unsigned nsecs, u32 word, u8 bits)
+ unsigned bit_delay, unsigned byte_delay, u32 word, u8 bits)
{
- return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
+ return bitbang_txrx_be_cpha1(spi, bit_delay, byte_delay, 1, word, bits);
}
/*----------------------------------------------------------------------*/