2013-10-20 19:35:45 +00:00
|
|
|
|
2021-07-30 11:17:19 +00:00
|
|
|
#define PY_SSIZE_T_CLEAN
|
2013-10-20 19:35:45 +00:00
|
|
|
#include "Python.h"
|
|
|
|
|
#include <linux/spi/spidev.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
2021-08-01 12:42:29 +00:00
|
|
|
#ifndef DEBUG
|
|
|
|
|
# define DEBUG 1
|
|
|
|
|
#endif
|
|
|
|
|
#define dprintf(lvl, fmt...) if (DEBUG >= lvl) fprintf(stderr, "spidev: " fmt)
|
|
|
|
|
|
2013-10-20 19:35:45 +00:00
|
|
|
static struct ssp_job {
|
|
|
|
|
int fd;
|
2021-07-30 11:17:19 +00:00
|
|
|
Py_ssize_t count;
|
2013-10-27 09:35:05 +00:00
|
|
|
unsigned char *command;
|
2013-10-20 19:35:45 +00:00
|
|
|
unsigned int flags;
|
|
|
|
|
} job = {
|
|
|
|
|
.fd = -1
|
|
|
|
|
};
|
|
|
|
|
|
2013-10-27 09:35:05 +00:00
|
|
|
#define INVERT_BITS_OUT 0x00001000
|
2013-10-20 19:35:45 +00:00
|
|
|
|
|
|
|
|
static int set_job_fd(struct ssp_job *j, int fd)
|
|
|
|
|
{
|
2021-08-01 12:42:29 +00:00
|
|
|
if (fd >= 0) {
|
2013-10-20 19:35:45 +00:00
|
|
|
j->fd = fd;
|
2021-08-01 12:42:29 +00:00
|
|
|
dprintf(2, "fd = %d\n", fd);
|
|
|
|
|
}
|
2013-10-20 19:35:45 +00:00
|
|
|
else if (j->fd < 0) {
|
|
|
|
|
PyErr_SetString(PyExc_ValueError, "No file descriptor");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-11-04 22:53:14 +00:00
|
|
|
static unsigned char buf[4096];
|
2021-07-30 17:10:36 +00:00
|
|
|
#define MAX_TRANSFER ((Py_ssize_t)sizeof(buf))
|
2013-10-20 19:35:45 +00:00
|
|
|
|
2013-10-27 09:35:05 +00:00
|
|
|
static inline __u32 bit_reverse(__u32 i)
|
|
|
|
|
{
|
|
|
|
|
return (i>>7) & 0x01010101
|
|
|
|
|
| (i>>5) & 0x02020202
|
|
|
|
|
| (i>>3) & 0x04040404
|
|
|
|
|
| (i>>1) & 0x08080808
|
|
|
|
|
| (i<<1) & 0x10101010
|
|
|
|
|
| (i<<3) & 0x20202020
|
|
|
|
|
| (i<<5) & 0x40404040
|
|
|
|
|
| (i<<7) & 0x80808080
|
|
|
|
|
;
|
|
|
|
|
}
|
2021-07-30 17:10:36 +00:00
|
|
|
static inline void bit_reverse_copy(void *d, void *s, int n)
|
2013-10-27 09:35:05 +00:00
|
|
|
{
|
|
|
|
|
n = (n+3)>>2;
|
|
|
|
|
__u32 *dd=d, *ss=s;
|
|
|
|
|
while (n--)
|
|
|
|
|
*(dd++) = bit_reverse(*(ss++));
|
2013-10-20 19:35:45 +00:00
|
|
|
}
|
|
|
|
|
|
2021-08-01 13:52:02 +00:00
|
|
|
static void hexdump(FILE *f, const char *header, const unsigned char *b, Py_ssize_t n)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "%s", header);
|
|
|
|
|
for (int i=0; i<n; i++)
|
|
|
|
|
fprintf(stderr, " %02x", b[i]);
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-20 19:35:45 +00:00
|
|
|
static int do_spi_transfer(struct ssp_job *j)
|
|
|
|
|
{
|
2021-08-01 12:42:29 +00:00
|
|
|
static struct spi_ioc_transfer transfer;
|
2013-10-20 19:35:45 +00:00
|
|
|
unsigned int n = 0;
|
2021-08-01 12:42:29 +00:00
|
|
|
transfer.tx_buf = (__u64) j->command;
|
|
|
|
|
transfer.rx_buf = (__u64) buf;
|
|
|
|
|
if (j->count > MAX_TRANSFER)
|
|
|
|
|
n = MAX_TRANSFER;
|
|
|
|
|
else
|
|
|
|
|
n = j->count;
|
|
|
|
|
transfer.len = n;
|
|
|
|
|
if (j->flags & INVERT_BITS_OUT) {
|
|
|
|
|
bit_reverse_copy(buf, j->command, n);
|
|
|
|
|
transfer.tx_buf = (__u64) buf;
|
2021-07-30 17:10:36 +00:00
|
|
|
}
|
2021-08-01 13:52:02 +00:00
|
|
|
else if (DEBUG >= 3)
|
|
|
|
|
memset(buf, 0x5a, MAX_TRANSFER);
|
|
|
|
|
if (DEBUG >= 4 || n <= 128 && DEBUG >= 3)
|
|
|
|
|
hexdump(stderr, "TX:", j->command, n);
|
|
|
|
|
if (n && ioctl(j->fd, SPI_IOC_MESSAGE(1), &transfer) < 0)
|
2021-08-01 12:42:29 +00:00
|
|
|
return -1;
|
2021-08-01 13:52:02 +00:00
|
|
|
if (DEBUG>=4 || n<=128 && DEBUG>=3)
|
|
|
|
|
hexdump(stderr, "RX:", buf, n);
|
2021-08-01 12:42:29 +00:00
|
|
|
dprintf(2, "tranfered %d bytes, flags 0x%x\n", n, j->flags);
|
2021-08-01 13:52:02 +00:00
|
|
|
j->count -= n;
|
|
|
|
|
j->command += n;
|
2013-10-20 19:35:45 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PyObject *set_mode(PyObject *self, PyObject *args, PyObject *kw)
|
|
|
|
|
{
|
|
|
|
|
static char *arg_names[] = {"mode", "cpha", "cpol", "fd", NULL};
|
|
|
|
|
int mode = 0, cpha = 0, cpol = 0, fd = -1;
|
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw,
|
2021-07-30 11:17:19 +00:00
|
|
|
"|ippi", arg_names,
|
2013-10-20 19:35:45 +00:00
|
|
|
&mode, &cpha, &cpol, &fd))
|
|
|
|
|
return NULL;
|
|
|
|
|
if (set_job_fd(&job, fd))
|
|
|
|
|
return NULL;
|
|
|
|
|
if (cpha)
|
|
|
|
|
mode |= SPI_CPHA;
|
|
|
|
|
if (cpol)
|
|
|
|
|
mode |= SPI_CPOL;
|
|
|
|
|
|
|
|
|
|
__u8 val = mode & (SPI_CPHA|SPI_CPOL);
|
|
|
|
|
if (ioctl(job.fd, SPI_IOC_WR_MODE, &val) < 0) {
|
|
|
|
|
PyErr_SetFromErrno(PyExc_IOError);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2024-01-14 21:39:13 +00:00
|
|
|
dprintf(1, "CPHA: 0x%lx CPOL: 0x%lx\n", mode & SPI_CPHA, mode & SPI_CPOL);
|
2013-10-20 19:35:45 +00:00
|
|
|
Py_RETURN_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PyObject *set_lsb_first(PyObject *self, PyObject *args, PyObject *kw)
|
|
|
|
|
{
|
|
|
|
|
static char *arg_names[] = {"mode", "fd", NULL};
|
|
|
|
|
int mode = 0, fd = -1;
|
2021-07-30 11:17:19 +00:00
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|pi", arg_names, &mode, &fd))
|
2013-10-20 19:35:45 +00:00
|
|
|
return NULL;
|
|
|
|
|
if (set_job_fd(&job, fd))
|
|
|
|
|
return NULL;
|
2021-07-30 11:17:19 +00:00
|
|
|
__u8 val = mode;
|
2013-10-20 19:35:45 +00:00
|
|
|
if (ioctl(job.fd, SPI_IOC_WR_LSB_FIRST, &val) < 0) {
|
|
|
|
|
PyErr_SetFromErrno(PyExc_IOError);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2021-08-01 12:42:29 +00:00
|
|
|
dprintf(1, "lsb first mode %d\n", mode);
|
2013-10-20 19:35:45 +00:00
|
|
|
Py_RETURN_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PyObject *set_speed(PyObject *self, PyObject *args, PyObject *kw)
|
|
|
|
|
{
|
|
|
|
|
static char *arg_names[] = {"hz", "fd", NULL};
|
|
|
|
|
int hz = 0, fd = -1;
|
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|ii", arg_names, &hz, &fd))
|
|
|
|
|
return NULL;
|
|
|
|
|
if (set_job_fd(&job, fd))
|
|
|
|
|
return NULL;
|
|
|
|
|
if (ioctl(job.fd, SPI_IOC_WR_MAX_SPEED_HZ, &hz) < 0) {
|
|
|
|
|
PyErr_SetFromErrno(PyExc_IOError);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2021-08-01 12:42:29 +00:00
|
|
|
dprintf(1, "speed %d Hz\n", hz);
|
2013-10-20 19:35:45 +00:00
|
|
|
Py_RETURN_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PyObject *sync_transfer(PyObject *self, PyObject *args, PyObject *kw)
|
|
|
|
|
{
|
2013-10-27 09:35:05 +00:00
|
|
|
static char *arg_names[] = {"cmd", "csize", "rsize", "flags", "fd", NULL};
|
2021-07-30 17:10:36 +00:00
|
|
|
Py_buffer cmd;
|
2021-07-30 11:17:19 +00:00
|
|
|
Py_ssize_t csize = 0, rsize=MAX_TRANSFER;
|
|
|
|
|
int fd=-1, flags=0;
|
2013-10-20 19:35:45 +00:00
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw,
|
2021-07-30 11:17:19 +00:00
|
|
|
"y*|nnii", arg_names,
|
2013-10-27 09:35:05 +00:00
|
|
|
&cmd, &csize, &rsize, &flags, &fd))
|
2013-10-20 19:35:45 +00:00
|
|
|
return NULL;
|
|
|
|
|
if (set_job_fd(&job, fd))
|
2021-08-01 12:42:29 +00:00
|
|
|
goto error;
|
2013-10-20 19:35:45 +00:00
|
|
|
if (rsize>MAX_TRANSFER) {
|
2025-03-19 19:49:07 +00:00
|
|
|
PyErr_SetString(PyExc_ValueError, "resp request too large");
|
2021-08-01 12:42:29 +00:00
|
|
|
goto error;
|
2013-10-20 19:35:45 +00:00
|
|
|
}
|
2021-07-30 17:10:36 +00:00
|
|
|
if (csize && csize > cmd.len) {
|
2013-10-20 19:35:45 +00:00
|
|
|
PyErr_SetString(PyExc_ValueError, "cmd too short");
|
2021-08-01 12:42:29 +00:00
|
|
|
goto error;
|
2013-10-20 19:35:45 +00:00
|
|
|
}
|
|
|
|
|
if (!csize)
|
2021-07-30 17:10:36 +00:00
|
|
|
csize = cmd.len;
|
|
|
|
|
job.command = cmd.buf;
|
2013-10-27 09:35:05 +00:00
|
|
|
job.flags = flags;
|
2013-10-20 19:35:45 +00:00
|
|
|
int vsize = 0;
|
|
|
|
|
while (csize) {
|
|
|
|
|
vsize = csize;
|
|
|
|
|
if (csize>MAX_TRANSFER)
|
|
|
|
|
if (csize-MAX_TRANSFER < rsize)
|
|
|
|
|
job.count = csize - rsize;
|
|
|
|
|
else
|
|
|
|
|
job.count = MAX_TRANSFER;
|
|
|
|
|
else
|
|
|
|
|
job.count = csize;
|
|
|
|
|
csize -= job.count;
|
|
|
|
|
if (do_spi_transfer(&job)<0) {
|
|
|
|
|
PyErr_SetFromErrno(PyExc_IOError);
|
2021-08-01 12:42:29 +00:00
|
|
|
error:
|
|
|
|
|
PyBuffer_Release(&cmd);
|
2021-07-30 11:17:19 +00:00
|
|
|
return NULL;
|
2013-10-20 19:35:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
2021-08-01 12:42:29 +00:00
|
|
|
PyBuffer_Release(&cmd);
|
2013-10-20 19:35:45 +00:00
|
|
|
if (rsize > vsize)
|
|
|
|
|
rsize = vsize;
|
2013-10-27 09:35:05 +00:00
|
|
|
|
2021-07-30 11:17:19 +00:00
|
|
|
return Py_BuildValue("y#", buf+vsize-rsize, rsize);
|
2013-10-20 19:35:45 +00:00
|
|
|
}
|
|
|
|
|
|
2013-11-04 22:53:14 +00:00
|
|
|
|
2021-07-30 11:17:19 +00:00
|
|
|
static PyMethodDef methods[] = {
|
|
|
|
|
{"set_mode", (PyCFunction)set_mode, METH_VARARGS | METH_KEYWORDS, "Set clock mode cpol and cpha."},
|
|
|
|
|
{"set_speed", (PyCFunction)set_speed, METH_VARARGS | METH_KEYWORDS, "Set max speed in Hz."},
|
|
|
|
|
{"set_lsb_first", (PyCFunction)set_lsb_first, METH_VARARGS | METH_KEYWORDS, "Set LSB first mode."},
|
|
|
|
|
{"sync_transfer", (PyCFunction)sync_transfer, METH_VARARGS | METH_KEYWORDS, "Synchronous write/read"},
|
|
|
|
|
|
|
|
|
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
|
|
|
|
};
|
2013-10-20 19:35:45 +00:00
|
|
|
|
2021-07-30 11:17:19 +00:00
|
|
|
static PyModuleDef module_def = {
|
|
|
|
|
.m_base = PyModuleDef_HEAD_INIT,
|
|
|
|
|
.m_name = "spidev",
|
|
|
|
|
.m_doc = "RPiRENA spi interface",
|
|
|
|
|
.m_size = -1,
|
|
|
|
|
.m_methods = methods,
|
2013-10-20 19:35:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PyMODINIT_FUNC
|
2021-07-30 11:17:19 +00:00
|
|
|
PyInit_spidev(void)
|
2013-10-20 19:35:45 +00:00
|
|
|
{
|
2021-07-30 11:17:19 +00:00
|
|
|
PyObject *m = PyModule_Create(&module_def);
|
|
|
|
|
PyModule_AddIntMacro(m, INVERT_BITS_OUT);
|
|
|
|
|
return m;
|
2013-10-20 19:35:45 +00:00
|
|
|
}
|