Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Readbuffer -- read into a pre-existing bytearray, at given offset #83

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ Connects to the specified SPI device, opening `/dev/spidev<bus>.<device>`

readbytes(n)

Read n bytes from SPI device.
Read n bytes from SPI device, returning a list of integers.

readbytesb(n)

Read n bytes from SPI device, returning an (immutable) bytes object (more efficient for larger transfers).

readbuffer(buffer [, length [, offset]])

Read length bytes from SPI device into an existing bytearray object at the given offset (single SPI transfer)
If length is not specified or 0, tries to read to the end of the buffer.
If offset is not specified, default is 0.

writebytes(list of values)

Expand Down
156 changes: 129 additions & 27 deletions spidev_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,49 +223,147 @@ SpiDev_writebytes(SpiDevObject *self, PyObject *args)
return Py_None;
}


/* return true on success */
static int
SpiDev_read_lowlevel(SpiDevObject *self, uint8_t *buf, int buflen, int lenreq)
{
int status;

/* read at least 1 byte, no more than SPIDEV_MAXPATH */
if (lenreq < 1)
lenreq = 1;
else if ((unsigned)lenreq > sizeof(buflen))
lenreq = buflen;
memset(buf, 0, buflen);

//#define SPIDEV_READ_LOWLEVEL_TESTING
#ifdef SPIDEV_READ_LOWLEVEL_TESTING
{ status = lenreq; for (unsigned i = 0; i < lenreq; i++) buf[i] = 64 + i; }
#else
status = read(self->fd, &buf[0], lenreq);
#endif

if (status < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return 0; // fail
}

if (status != lenreq) {
printf("Short Read: %d/%d bytes\n", status, lenreq);
PyErr_SetString(PyExc_IOError, "SpiDev.read: I/O error: short read.");
return 0; // fail
}
return 1; // succeed
}


#define READBYTES_GENERIC_RESULTTYPE_LIST 0
#define READBYTES_GENERIC_RESULTTYPE_BYTES 1

static PyObject *
SpiDev_readbytes_generic(SpiDevObject *self, PyObject *args, int resultType)
{
static uint8_t rxbuf[SPIDEV_MAXPATH];
int len = 0;

if (!PyArg_ParseTuple(args, "i:read", &len))
return NULL;

// get the bytes here
if (!SpiDev_read_lowlevel(self, rxbuf, sizeof(rxbuf), len)) {
return NULL;
}

if (resultType == READBYTES_GENERIC_RESULTTYPE_LIST) { // list
int ii;
PyObject *list;
list = PyList_New(len);

for (ii = 0; ii < len; ii++) {
PyObject *val = Py_BuildValue("l", (long)rxbuf[ii]);
PyList_SET_ITEM(list, ii, val);
}

return list;
}
else if (resultType == READBYTES_GENERIC_RESULTTYPE_BYTES) { // bytes
PyObject *bytes;
bytes = Py_BuildValue("y#", rxbuf, len);
return bytes;
}
else { // unknown type
PyErr_SetString(PyExc_RuntimeError, "SpiDev.read: internal problem in readbytes_generic.");
return NULL;
}
}



PyDoc_STRVAR(SpiDev_read_doc,
"read(len) -> [values]\n\n"
"Read len bytes from SPI device.\n");
"Read len bytes from SPI device, returning a list.\n");

static PyObject *
SpiDev_readbytes(SpiDevObject *self, PyObject *args)
{
uint8_t rxbuf[SPIDEV_MAXPATH];
int status, len, ii;
PyObject *list;
return SpiDev_readbytes_generic(self, args, READBYTES_GENERIC_RESULTTYPE_LIST);
}

if (!PyArg_ParseTuple(args, "i:read", &len))
return NULL;
PyDoc_STRVAR(SpiDev_readb_doc,
"read(len) -> bytes(len)\n\n"
"Read len bytes from SPI device, returning a bytes object of length len.\n");

/* read at least 1 byte, no more than SPIDEV_MAXPATH */
if (len < 1)
len = 1;
else if ((unsigned)len > sizeof(rxbuf))
len = sizeof(rxbuf);
static PyObject *
SpiDev_readbytesb(SpiDevObject *self, PyObject *args)
{
return SpiDev_readbytes_generic(self, args, READBYTES_GENERIC_RESULTTYPE_BYTES);
}

memset(rxbuf, 0, sizeof rxbuf);
status = read(self->fd, &rxbuf[0], len);

if (status < 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
PyDoc_STRVAR(SpiDev_readbuffer_doc,
"read(bytearray [, length [, offset]]) -> [values]\n\n"
"Read bytes from SPI device, filling the supplied byte array.\n");

if (status != len) {
perror("short read");
return NULL;
}
static PyObject *
SpiDev_readbuffer(SpiDevObject *self, PyObject *args)
{
Py_buffer pybuff;
int offset = 0, len = 0;

list = PyList_New(len);
if (!PyArg_ParseTuple(args, "y*|II", &pybuff, &len, &offset))
return NULL;
if (!PyBuffer_IsContiguous(&pybuff, 'A')) {
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: buffer must be contiguous.");
return NULL;
}
if (pybuff.len < 1) {
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: buffer must not be empty.");
return NULL;
}
if (len < 0) {
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: length must be positive.");
return NULL;
}
if (offset < 0 || offset >= pybuff.len - 1) { // NB must be space for at least one byte
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: offset out of range.");
return NULL;
}
if (offset + len > pybuff.len) {
PyErr_SetString(PyExc_RuntimeError, "SpiDev.readbuffer: offset+length would overflow buffer.");
return NULL;
}

for (ii = 0; ii < len; ii++) {
PyObject *val = Py_BuildValue("l", (long)rxbuf[ii]);
PyList_SET_ITEM(list, ii, val);
}
if (len == 0)
len = pybuff.len - offset;

return list;
if (!SpiDev_read_lowlevel(self, pybuff.buf + offset, len, len))
return NULL;

Py_RETURN_NONE;
}


static PyObject *
SpiDev_writebytes2_buffer(SpiDevObject *self, Py_buffer *buffer)
{
Expand Down Expand Up @@ -861,6 +959,7 @@ SpiDev_xfer3(SpiDevObject *self, PyObject *args)
return rx_tuple;
}


static int __spidev_set_mode( int fd, __u8 mode) {
__u8 test;
if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) {
Expand Down Expand Up @@ -1365,6 +1464,8 @@ static PyMethodDef SpiDev_methods[] = {
SpiDev_fileno_doc},
{"readbytes", (PyCFunction)SpiDev_readbytes, METH_VARARGS,
SpiDev_read_doc},
{"readbytesb", (PyCFunction)SpiDev_readbytesb, METH_VARARGS,
SpiDev_readb_doc},
{"writebytes", (PyCFunction)SpiDev_writebytes, METH_VARARGS,
SpiDev_write_doc},
{"writebytes2", (PyCFunction)SpiDev_writebytes2, METH_VARARGS,
Expand All @@ -1375,6 +1476,7 @@ static PyMethodDef SpiDev_methods[] = {
SpiDev_xfer2_doc},
{"xfer3", (PyCFunction)SpiDev_xfer3, METH_VARARGS,
SpiDev_xfer3_doc},
{"readbuffer", (PyCFunction)SpiDev_readbuffer, METH_VARARGS, SpiDev_readbuffer_doc},
{"__enter__", (PyCFunction)SpiDev_enter, METH_VARARGS,
NULL},
{"__exit__", (PyCFunction)SpiDev_exit, METH_VARARGS,
Expand Down