aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/spi/spidev307
-rw-r--r--drivers/spi/Kconfig9
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spidev.c584
-rw-r--r--include/linux/Kbuild1
-rw-r--r--include/linux/spi/Kbuild1
-rw-r--r--include/linux/spi/spidev.h124
7 files changed, 1027 insertions, 0 deletions
diff --git a/Documentation/spi/spidev b/Documentation/spi/spidev
new file mode 100644
index 00000000000..5c8e1b988a0
--- /dev/null
+++ b/Documentation/spi/spidev
@@ -0,0 +1,307 @@
+SPI devices have a limited userspace API, supporting basic half-duplex
+read() and write() access to SPI slave devices. Using ioctl() requests,
+full duplex transfers and device I/O configuration are also available.
+
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <sys/ioctl.h>
+ #include <linux/types.h>
+ #include <linux/spi/spidev.h>
+
+Some reasons you might want to use this programming interface include:
+
+ * Prototyping in an environment that's not crash-prone; stray pointers
+ in userspace won't normally bring down any Linux system.
+
+ * Developing simple protocols used to talk to microcontrollers acting
+ as SPI slaves, which you may need to change quite often.
+
+Of course there are drivers that can never be written in userspace, because
+they need to access kernel interfaces (such as IRQ handlers or other layers
+of the driver stack) that are not accessible to userspace.
+
+
+DEVICE CREATION, DRIVER BINDING
+===============================
+The simplest way to arrange to use this driver is to just list it in the
+spi_board_info for a device as the driver it should use: the "modalias"
+entry is "spidev", matching the name of the driver exposing this API.
+Set up the other device characteristics (bits per word, SPI clocking,
+chipselect polarity, etc) as usual, so you won't always need to override
+them later.
+
+(Sysfs also supports userspace driven binding/unbinding of drivers to
+devices. That mechanism might be supported here in the future.)
+
+When you do that, the sysfs node for the SPI device will include a child
+device node with a "dev" attribute that will be understood by udev or mdev.
+(Larger systems will have "udev". Smaller ones may configure "mdev" into
+busybox; it's less featureful, but often enough.) For a SPI device with
+chipselect C on bus B, you should see:
+
+ /dev/spidevB.C ... character special device, major number 153 with
+ a dynamically chosen minor device number. This is the node
+ that userspace programs will open, created by "udev" or "mdev".
+
+ /sys/devices/.../spiB.C ... as usual, the SPI device node will
+ be a child of its SPI master controller.
+
+ /sys/class/spidev/spidevB.C ... created when the "spidev" driver
+ binds to that device. (Directory or symlink, based on whether
+ or not you enabled the "deprecated sysfs files" Kconfig option.)
+
+Do not try to manage the /dev character device special file nodes by hand.
+That's error prone, and you'd need to pay careful attention to system
+security issues; udev/mdev should already be configured securely.
+
+If you unbind the "spidev" driver from that device, those two "spidev" nodes
+(in sysfs and in /dev) should automatically be removed (respectively by the
+kernel and by udev/mdev). You can unbind by removing the "spidev" driver
+module, which will affect all devices using this driver. You can also unbind
+by having kernel code remove the SPI device, probably by removing the driver
+for its SPI controller (so its spi_master vanishes).
+
+Since this is a standard Linux device driver -- even though it just happens
+to expose a low level API to userspace -- it can be associated with any number
+of devices at a time. Just provide one spi_board_info record for each such
+SPI device, and you'll get a /dev device node for each device.
+
+
+BASIC CHARACTER DEVICE API
+==========================
+Normal open() and close() operations on /dev/spidevB.D files work as you
+would expect.
+
+Standard read() and write() operations are obviously only half-duplex, and
+the chipselect is deactivated between those operations. Full-duplex access,
+and composite operation without chipselect de-activation, is available using
+the SPI_IOC_MESSAGE(N) request.
+
+Several ioctl() requests let your driver read or override the device's current
+settings for data transfer parameters:
+
+ SPI_IOC_RD_MODE, SPI_IOC_WR_MODE ... pass a pointer to a byte which will
+ return (RD) or assign (WR) the SPI transfer mode. Use the constants
+ SPI_MODE_0..SPI_MODE_3; or if you prefer you can combine SPI_CPOL
+ (clock polarity, idle high iff this is set) or SPI_CPHA (clock phase,
+ sample on trailing edge iff this is set) flags.
+
+ SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST ... pass a pointer to a byte
+ which will return (RD) or assign (WR) the bit justification used to
+ transfer SPI words. Zero indicates MSB-first; other values indicate
+ the less common LSB-first encoding. In both cases the specified value
+ is right-justified in each word, so that unused (TX) or undefined (RX)
+ bits are in the MSBs.
+
+ SPI_IOC_RD_BITS_PER_WORD, SPI_IOC_WR_BITS_PER_WORD ... pass a pointer to
+ a byte which will return (RD) or assign (WR) the number of bits in
+ each SPI transfer word. The value zero signifies eight bits.
+
+ SPI_IOC_RD_MAX_SPEED_HZ, SPI_IOC_WR_MAX_SPEED_HZ ... pass a pointer to a
+ u32 which will return (RD) or assign (WR) the maximum SPI transfer
+ speed, in Hz. The controller can't necessarily assign that specific
+ clock speed.
+
+NOTES:
+
+ - At this time there is no async I/O support; everything is purely
+ synchronous.
+
+ - There's currently no way to report the actual bit rate used to
+ shift data to/from a given device.
+
+ - From userspace, you can't currently change the chip select polarity;
+ that could corrupt transfers to other devices sharing the SPI bus.
+ Each SPI device is deselected when it's not in active use, allowing
+ other drivers to talk to other devices.
+
+ - There's a limit on the number of bytes each I/O request can transfer
+ to the SPI device. It defaults to one page, but that can be changed
+ using a module parameter.
+
+ - Because SPI has no low-level transfer acknowledgement, you usually
+ won't see any I/O errors when talking to a non-existent device.
+
+
+FULL DUPLEX CHARACTER DEVICE API
+================================
+
+See the sample program below for one example showing the use of the full
+duplex programming interface. (Although it doesn't perform a full duplex
+transfer.) The model is the same as that used in the kernel spi_sync()
+request; the individual transfers offer the same capabilities as are
+available to kernel drivers (except that it's not asynchronous).
+
+The example shows one half-duplex RPC-style request and response message.
+These requests commonly require that the chip not be deselected between
+the request and response. Several such requests could be chained into
+a single kernel request, even allowing the chip to be deselected after
+each response. (Other protocol options include changing the word size
+and bitrate for each transfer segment.)
+
+To make a full duplex request, provide both rx_buf and tx_buf for the
+same transfer. It's even OK if those are the same buffer.
+
+
+SAMPLE PROGRAM
+==============
+
+-------------------------------- CUT HERE
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+
+
+static int verbose;
+
+static void do_read(int fd, int len)
+{
+ unsigned char buf[32], *bp;
+ int status;
+
+ /* read at least 2 bytes, no more than 32 */
+ if (len < 2)
+ len = 2;
+ else if (len > sizeof(buf))
+ len = sizeof(buf);
+ memset(buf, 0, sizeof buf);
+
+ status = read(fd, buf, len);
+ if (status < 0) {
+ perror("read");
+ return;
+ }
+ if (status != len) {
+ fprintf(stderr, "short read\n");
+ return;
+ }
+
+ printf("read(%2d, %2d): %02x %02x,", len, status,
+ buf[0], buf[1]);
+ status -= 2;
+ bp = buf + 2;
+ while (status-- > 0)
+ printf(" %02x", *bp++);
+ printf("\n");
+}
+
+static void do_msg(int fd, int len)
+{
+ struct spi_ioc_transfer xfer[2];
+ unsigned char buf[32], *bp;
+ int status;
+
+ memset(xfer, 0, sizeof xfer);
+ memset(buf, 0, sizeof buf);
+
+ if (len > sizeof buf)
+ len = sizeof buf;
+
+ buf[0] = 0xaa;
+ xfer[0].tx_buf = (__u64) buf;
+ xfer[0].len = 1;
+
+ xfer[1].rx_buf = (__u64) buf;
+ xfer[1].len = len;
+
+ status = ioctl(fd, SPI_IOC_MESSAGE(2), xfer);
+ if (status < 0) {
+ perror("SPI_IOC_MESSAGE");
+ return;
+ }
+
+ printf("response(%2d, %2d): ", len, status);
+ for (bp = buf; len; len--)
+ printf(" %02x", *bp++);
+ printf("\n");
+}
+
+static void dumpstat(const char *name, int fd)
+{
+ __u8 mode, lsb, bits;
+ __u32 speed;
+
+ if (ioctl(fd, SPI_IOC_RD_MODE, &mode) < 0) {
+ perror("SPI rd_mode");
+ return;
+ }
+ if (ioctl(fd, SPI_IOC_RD_LSB_FIRST, &lsb) < 0) {
+ perror("SPI rd_lsb_fist");
+ return;
+ }
+ if (ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) {
+ perror("SPI bits_per_word");
+ return;
+ }
+ if (ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) {
+ perror("SPI max_speed_hz");
+ return;
+ }
+
+ printf("%s: spi mode %d, %d bits %sper word, %d Hz max\n",
+ name, mode, bits, lsb ? "(lsb first) " : "", speed);
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ int readcount = 0;
+ int msglen = 0;
+ int fd;
+ const char *name;
+
+ while ((c = getopt(argc, argv, "hm:r:v")) != EOF) {
+ switch (c) {
+ case 'm':
+ msglen = atoi(optarg);
+ if (msglen < 0)
+ goto usage;
+ continue;
+ case 'r':
+ readcount = atoi(optarg);
+ if (readcount < 0)
+ goto usage;
+ continue;
+ case 'v':
+ verbose++;
+ continue;
+ case 'h':
+ case '?':
+usage:
+ fprintf(stderr,
+ "usage: %s [-h] [-m N] [-r N] /dev/spidevB.D\n",
+ argv[0]);
+ return 1;
+ }
+ }
+
+ if ((optind + 1) != argc)
+ goto usage;
+ name = argv[optind];
+
+ fd = open(name, O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ dumpstat(name, fd);
+
+ if (msglen)
+ do_msg(fd, msglen);
+
+ if (readcount)
+ do_read(fd, readcount);
+
+ close(fd);
+ return 0;
+}
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4a012d9acbf..584ed9f7470 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -159,6 +159,15 @@ config SPI_AT25
This driver can also be built as a module. If so, the module
will be called at25.
+config SPI_SPIDEV
+ tristate "User mode SPI device driver support"
+ depends on SPI_MASTER && EXPERIMENTAL
+ help
+ This supports user mode SPI protocol drivers.
+
+ Note that this application programming interface is EXPERIMENTAL
+ and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
+
#
# Add new SPI protocol masters in alphabetical order above this line
#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index a95ade857a2..4cc5e99dd59 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
# SPI protocol drivers (device/link on bus)
obj-$(CONFIG_SPI_AT25) += at25.o
+obj-$(CONFIG_SPI_SPIDEV) += spidev.o
# ... add above this line ...
# SPI slave controller drivers (upstream link)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
new file mode 100644
index 00000000000..c0a6dce800a
--- /dev/null
+++ b/drivers/spi/spidev.c
@@ -0,0 +1,584 @@
+/*
+ * spidev.c -- simple synchronous userspace interface to SPI devices
+ *
+ * Copyright (C) 2006 SWAPP
+ * Andrea Paterniani <a.paterniani@swapp-eng.it>
+ * Copyright (C) 2007 David Brownell (simplification, cleanup)
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spidev.h>
+
+#include <asm/uaccess.h>
+
+
+/*
+ * This supports acccess to SPI devices using normal userspace I/O calls.
+ * Note that while traditional UNIX/POSIX I/O semantics are half duplex,
+ * and often mask message boundaries, full SPI support requires full duplex
+ * transfers. There are several kinds of of internal message boundaries to
+ * handle chipselect management and other protocol options.
+ *
+ * SPI has a character major number assigned. We allocate minor numbers
+ * dynamically using a bitmask. You must use hotplug tools, such as udev
+ * (or mdev with busybox) to create and destroy the /dev/spidevB.C device
+ * nodes, since there is no fixed association of minor numbers with any
+ * particular SPI bus or device.
+ */
+#define SPIDEV_MAJOR 153 /* assigned */
+#define N_SPI_MINORS 32 /* ... up to 256 */
+
+static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG];
+
+
+/* Bit masks for spi_device.mode management */
+#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL)
+
+
+struct spidev_data {
+ struct device dev;
+ struct spi_device *spi;
+ struct list_head device_entry;
+
+ struct mutex buf_lock;
+ unsigned users;
+ u8 *buffer;
+};
+
+static LIST_HEAD(device_list);
+static DEFINE_MUTEX(device_list_lock);
+
+static unsigned bufsiz = 4096;
+module_param(bufsiz, uint, S_IRUGO);
+MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
+
+/*-------------------------------------------------------------------------*/
+
+/* Read-only message with current device setup */
+static ssize_t
+spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+ struct spidev_data *spidev;
+ struct spi_device *spi;
+ ssize_t status = 0;
+
+ /* chipselect only toggles at start or end of operation */
+ if (count > bufsiz)
+ return -EMSGSIZE;
+
+ spidev = filp->private_data;
+ spi = spidev->spi;
+
+ mutex_lock(&spidev->buf_lock);
+ status = spi_read(spi, spidev->buffer, count);
+ if (status == 0) {
+ unsigned long missing;
+
+ missing = copy_to_user(buf, spidev->buffer, count);
+ if (count && missing == count)
+ status = -EFAULT;
+ else
+ status = count - missing;
+ }
+ mutex_unlock(&spidev->buf_lock);
+
+ return status;
+}
+
+/* Write-only message with current device setup */
+static ssize_t
+spidev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct spidev_data *spidev;
+ struct spi_device *spi;
+ ssize_t status = 0;
+ unsigned long missing;
+
+ /* chipselect only toggles at start or end of operation */
+ if (count > bufsiz)
+ return -EMSGSIZE;
+
+ spidev = filp->private_data;
+ spi = spidev->spi;
+
+ mutex_lock(&spidev->buf_lock);
+ missing = copy_from_user(spidev->buffer, buf, count);
+ if (missing == 0) {
+ status = spi_write(spi, spidev->buffer, count);
+ if (status == 0)
+ status = count;
+ } else
+ status = -EFAULT;
+ mutex_unlock(&spidev->buf_lock);
+
+ return status;
+}
+
+static int spidev_message(struct spidev_data *spidev,
+ struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
+{
+ struct spi_message msg;
+ struct spi_transfer *k_xfers;
+ struct spi_transfer *k_tmp;
+ struct spi_ioc_transfer *u_tmp;
+ struct spi_device *spi = spidev->spi;
+ unsigned n, total;
+ u8 *buf;
+ int status = -EFAULT;
+
+ spi_message_init(&msg);
+ k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
+ if (k_xfers == NULL)
+ return -ENOMEM;
+
+ /* Construct spi_message, copying any tx data to bounce buffer.
+ * We walk the array of user-provided transfers, using each one
+ * to initialize a kernel version of the same transfer.
+ */
+ mutex_lock(&spidev->buf_lock);
+ buf = spidev->buffer;
+ total = 0;
+ for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
+ n;
+ n--, k_tmp++, u_tmp++) {
+ k_tmp->len = u_tmp->len;
+
+ if (u_tmp->rx_buf) {
+ k_tmp->rx_buf = buf;
+ if (!access_ok(VERIFY_WRITE, u_tmp->rx_buf, u_tmp->len))
+ goto done;
+ }
+ if (u_tmp->tx_buf) {
+ k_tmp->tx_buf = buf;
+ if (copy_from_user(buf, (const u8 __user *)u_tmp->tx_buf,
+ u_tmp->len))
+ goto done;
+ }
+
+ total += k_tmp->len;
+ if (total > bufsiz) {
+ status = -EMSGSIZE;
+ goto done;
+ }
+ buf += k_tmp->len;
+
+ k_tmp->cs_change = !!u_tmp->cs_change;
+ k_tmp->bits_per_word = u_tmp->bits_per_word;
+ k_tmp->delay_usecs = u_tmp->delay_usecs;
+ k_tmp->speed_hz = u_tmp->speed_hz;
+#ifdef VERBOSE
+ dev_dbg(&spi->dev,
+ " xfer len %zd %s%s%s%dbits %u usec %uHz\n",
+ u_tmp->len,
+ u_tmp->rx_buf ? "rx " : "",
+ u_tmp->tx_buf ? "tx " : "",
+ u_tmp->cs_change ? "cs " : "",
+ u_tmp->bits_per_word ? : spi->bits_per_word,
+ u_tmp->delay_usecs,
+ u_tmp->speed_hz ? : spi->max_speed_hz);
+#endif
+ spi_message_add_tail(k_tmp, &msg);
+ }
+
+ status = spi_sync(spi, &msg);
+ if (status < 0)
+ goto done;
+
+ /* copy any rx data out of bounce buffer */
+ buf = spidev->buffer;
+ for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
+ if (u_tmp->rx_buf) {
+ if (__copy_to_user((u8 __user *)u_tmp->rx_buf, buf,
+ u_tmp->len)) {
+ status = -EFAULT;
+ goto done;
+ }
+ }
+ buf += u_tmp->len;
+ }
+ status = total;
+
+done:
+ mutex_unlock(&spidev->buf_lock);
+ kfree(k_xfers);
+ return status;
+}
+
+static int
+spidev_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ int retval = 0;
+ struct spidev_data *spidev;
+ struct spi_device *spi;
+ u32 tmp;
+ unsigned n_ioc;
+ struct spi_ioc_transfer *ioc;
+
+ /* Check type and command number */
+ if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
+ return -ENOTTY;
+
+ /* Check access direction once here; don't repeat below.
+ * IOC_DIR is from the user perspective, while access_ok is
+ * from the kernel perspective; so they look reversed.
+ */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ err = !access_ok(VERIFY_WRITE,
+ (void __user *)arg, _IOC_SIZE(cmd));
+ if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
+ err = !access_ok(VERIFY_READ,
+ (void __user *)arg, _IOC_SIZE(cmd));
+ if (err)
+ return -EFAULT;
+
+ spidev = filp->private_data;
+ spi = spidev->spi;
+
+ switch (cmd) {
+ /* read requests */
+ case SPI_IOC_RD_MODE:
+ retval = __put_user(spi->mode & SPI_MODE_MASK,
+ (__u8 __user *)arg);
+ break;
+ case SPI_IOC_RD_LSB_FIRST:
+ retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
+ (__u8 __user *)arg);
+ break;
+ case SPI_IOC_RD_BITS_PER_WORD:
+ retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
+ break;
+ case SPI_IOC_RD_MAX_SPEED_HZ:
+ retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
+ break;
+
+ /* write requests */
+ case SPI_IOC_WR_MODE:
+ retval = __get_user(tmp, (u8 __user *)arg);
+ if (retval == 0) {
+ u8 save = spi->mode;
+
+ if (tmp & ~SPI_MODE_MASK) {
+ retval = -EINVAL;
+ break;
+ }
+
+ tmp |= spi->mode & ~SPI_MODE_MASK;
+ spi->mode = (u8)tmp;
+ retval = spi_setup(spi);
+ if (retval < 0)
+ spi->mode = save;
+ else
+ dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
+ }
+ break;
+ case SPI_IOC_WR_LSB_FIRST:
+ retval = __get_user(tmp, (__u8 __user *)arg);
+ if (retval == 0) {
+ u8 save = spi->mode;
+
+ if (tmp)
+ spi->mode |= SPI_LSB_FIRST;
+ else
+ spi->mode &= ~SPI_LSB_FIRST;
+ retval = spi_setup(spi);
+ if (retval < 0)
+ spi->mode = save;
+ else
+ dev_dbg(&spi->dev, "%csb first\n",
+ tmp ? 'l' : 'm');
+ }
+ break;
+ case SPI_IOC_WR_BITS_PER_WORD:
+ retval = __get_user(tmp, (__u8 __user *)arg);
+ if (retval == 0) {
+ u8 save = spi->bits_per_word;
+
+ spi->bits_per_word = tmp;
+ retval = spi_setup(spi);
+ if (retval < 0)
+ spi->bits_per_word = save;
+ else
+ dev_dbg(&spi->dev, "%d bits per word\n", tmp);
+ }
+ break;
+ case SPI_IOC_WR_MAX_SPEED_HZ:
+ retval = __get_user(tmp, (__u32 __user *)arg);
+ if (retval == 0) {
+ u32 save = spi->max_speed_hz;
+
+ spi->max_speed_hz = tmp;
+ retval = spi_setup(spi);
+ if (retval < 0)
+ spi->max_speed_hz = save;
+ else
+ dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
+ }
+ break;
+
+ default:
+ /* segmented and/or full-duplex I/O request */
+ if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
+ || _IOC_DIR(cmd) != _IOC_WRITE)
+ return -ENOTTY;
+
+ tmp = _IOC_SIZE(cmd);
+ if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
+ retval = -EINVAL;
+ break;
+ }
+ n_ioc = tmp / sizeof(struct spi_ioc_transfer);
+ if (n_ioc == 0)
+ break;
+
+ /* copy into scratch area */
+ ioc = kmalloc(tmp, GFP_KERNEL);
+ if (!ioc) {
+ retval = -ENOMEM;
+ break;
+ }
+ if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
+ retval = -EFAULT;
+ break;
+ }
+
+ /* translate to spi_message, execute */
+ retval = spidev_message(spidev, ioc, n_ioc);
+ kfree(ioc);
+ break;
+ }
+ return retval;
+}
+
+static int spidev_open(struct inode *inode, struct file *filp)
+{
+ struct spidev_data *spidev;
+ int status = -ENXIO;
+
+ mutex_lock(&device_list_lock);
+
+ list_for_each_entry(spidev, &device_list, device_entry) {
+ if (spidev->dev.devt == inode->i_rdev) {
+ status = 0;
+ break;
+ }
+ }
+ if (status == 0) {
+ if (!spidev->buffer) {
+ spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
+ if (!spidev->buffer) {
+ dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
+ status = -ENOMEM;
+ }
+ }
+ if (status == 0) {
+ spidev->users++;
+ filp->private_data = spidev;
+ nonseekable_open(inode, filp);
+ }
+ } else
+ pr_debug("spidev: nothing for minor %d\n", iminor(inode));
+
+ mutex_unlock(&device_list_lock);
+ return status;
+}
+
+static int spidev_release(struct inode *inode, struct file *filp)
+{
+ struct spidev_data *spidev;
+ int status = 0;
+
+ mutex_lock(&device_list_lock);
+ spidev = filp->private_data;
+ filp->private_data = NULL;
+ spidev->users--;
+ if (!spidev->users) {
+ kfree(spidev->buffer);
+ spidev->buffer = NULL;
+ }
+ mutex_unlock(&device_list_lock);
+
+ return status;
+}
+
+static struct file_operations spidev_fops = {
+ .owner = THIS_MODULE,
+ /* REVISIT switch to aio primitives, so that userspace
+ * gets more complete API coverage. It'll simplify things
+ * too, except for the locking.
+ */
+ .write = spidev_write,
+ .read = spidev_read,
+ .ioctl = spidev_ioctl,
+ .open = spidev_open,
+ .release = spidev_release,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* The main reason to have this class is to make mdev/udev create the
+ * /dev/spidevB.C character device nodes exposing our userspace API.
+ * It also simplifies memory management.
+ */
+
+static void spidev_classdev_release(struct device *dev)
+{
+ struct spidev_data *spidev;
+
+ spidev = container_of(dev, struct spidev_data, dev);
+ kfree(spidev);
+}
+
+static struct class spidev_class = {
+ .name = "spidev",
+ .owner = THIS_MODULE,
+ .dev_release = spidev_classdev_release,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int spidev_probe(struct spi_device *spi)
+{
+ struct spidev_data *spidev;
+ int status;
+ unsigned long minor;
+
+ /* Allocate driver data */
+ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
+ if (!spidev)
+ return -ENOMEM;
+
+ /* Initialize the driver data */
+ spidev->spi = spi;
+ mutex_init(&spidev->buf_lock);
+
+ INIT_LIST_HEAD(&spidev->device_entry);
+
+ /* If we can allocate a minor number, hook up this device.
+ * Reusing minors is fine so long as udev or mdev is working.
+ */
+ mutex_lock(&device_list_lock);
+ minor = find_first_zero_bit(minors, ARRAY_SIZE(minors));
+ if (minor < N_SPI_MINORS) {
+ spidev->dev.parent = &spi->dev;
+ spidev->dev.class = &spidev_class;
+ spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor);
+ snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id,
+ "spidev%d.%d",
+ spi->master->bus_num, spi->chip_select);
+ status = device_register(&spidev->dev);
+ } else {
+ dev_dbg(&spi->dev, "no minor number available!\n");
+ status = -ENODEV;
+ }
+ if (status == 0) {
+ set_bit(minor, minors);
+ dev_set_drvdata(&spi->dev, spidev);
+ list_add(&spidev->device_entry, &device_list);
+ }
+ mutex_unlock(&device_list_lock);
+
+ if (status != 0)
+ kfree(spidev);
+
+ return status;
+}
+
+static int spidev_remove(struct spi_device *spi)
+{
+ struct spidev_data *spidev = dev_get_drvdata(&spi->dev);
+
+ mutex_lock(&device_list_lock);
+
+ list_del(&spidev->device_entry);
+ dev_set_drvdata(&spi->dev, NULL);
+ clear_bit(MINOR(spidev->dev.devt), minors);
+ device_unregister(&spidev->dev);
+
+ mutex_unlock(&device_list_lock);
+
+ return 0;
+}
+
+static struct spi_driver spidev_spi = {
+ .driver = {
+ .name = "spidev",
+ .owner = THIS_MODULE,
+ },
+ .probe = spidev_probe,
+ .remove = __devexit_p(spidev_remove),
+
+ /* NOTE: suspend/resume methods are not necessary here.
+ * We don't do anything except pass the requests to/from
+ * the underlying controller. The refrigerator handles
+ * most issues; the controller driver handles the rest.
+ */
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init spidev_init(void)
+{
+ int status;
+
+ /* Claim our 256 reserved device numbers. Then register a class
+ * that will key udev/mdev to add/remove /dev nodes. Last, register
+ * the driver which manages those device numbers.
+ */
+ BUILD_BUG_ON(N_SPI_MINORS > 256);
+ status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
+ if (status < 0)
+ return status;
+
+ status = class_register(&spidev_class);
+ if (status < 0) {
+ unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+ return status;
+ }
+
+ status = spi_register_driver(&spidev_spi);
+ if (status < 0) {
+ class_unregister(&spidev_class);
+ unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+ }
+ return status;
+}
+module_init(spidev_init);
+
+static void __exit spidev_exit(void)
+{
+ spi_unregister_driver(&spidev_spi);
+ class_unregister(&spidev_class);
+ unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
+}
+module_exit(spidev_exit);
+
+MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
+MODULE_DESCRIPTION("User mode SPI device interface");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 71538614262..ccc6fc15706 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -4,6 +4,7 @@ header-y += hdlc/
header-y += isdn/
header-y += nfsd/
header-y += raid/
+header-y += spi/
header-y += sunrpc/
header-y += tc_act/
header-y += netfilter/
diff --git a/include/linux/spi/Kbuild b/include/linux/spi/Kbuild
new file mode 100644
index 00000000000..d375a082986
--- /dev/null
+++ b/include/linux/spi/Kbuild
@@ -0,0 +1 @@
+header-y += spidev.h
diff --git a/include/linux/spi/spidev.h b/include/linux/spi/spidev.h
new file mode 100644
index 00000000000..7d700be5749
--- /dev/null
+++ b/include/linux/spi/spidev.h
@@ -0,0 +1,124 @@
+/*
+ * include/linux/spi/spidev.h
+ *
+ * Copyright (C) 2006 SWAPP
+ * Andrea Paterniani <a.paterniani@swapp-eng.it>
+ *
+ * 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 SPIDEV_H
+#define SPIDEV_H
+
+
+/* User space versions of kernel symbols for SPI clocking modes,
+ * matching <linux/spi/spi.h>
+ */
+
+#define SPI_CPHA 0x01
+#define SPI_CPOL 0x02
+
+#define SPI_MODE_0 (0|0)
+#define SPI_MODE_1 (0|SPI_CPHA)
+#define SPI_MODE_2 (SPI_CPOL|0)
+#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
+
+
+/*---------------------------------------------------------------------------*/
+
+/* IOCTL commands */
+
+#define SPI_IOC_MAGIC 'k'
+
+/**
+ * struct spi_ioc_transfer - describes a single SPI transfer
+ * @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
+ * If no data is provided, zeroes are shifted out.
+ * @rx_buf: Holds pointer to userspace buffer for receive data, or null.
+ * @len: Length of tx and rx buffers, in bytes.
+ * @speed_hz: Temporary override of the device's bitrate.
+ * @bits_per_word: Temporary override of the device's wordsize.
+ * @delay_usecs: If nonzero, how long to delay after the last bit transfer
+ * before optionally deselecting the device before the next transfer.
+ * @cs_change: True to deselect device before starting the next transfer.
+ *
+ * This structure is mapped directly to the kernel spi_transfer structure;
+ * the fields have the same meanings, except of course that the pointers
+ * are in a different address space (and may be of different sizes in some
+ * cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
+ * Zero-initialize the structure, including currently unused fields, to
+ * accomodate potential future updates.
+ *
+ * SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
+ * Pass it an array of related transfers, they'll execute together.
+ * Each transfer may be half duplex (either direction) or full duplex.
+ *
+ * struct spi_ioc_transfer mesg[4];
+ * ...
+ * status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
+ *
+ * So for example one transfer might send a nine bit command (right aligned
+ * in a 16-bit word), the next could read a block of 8-bit data before
+ * terminating that command by temporarily deselecting the chip; the next
+ * could send a different nine bit command (re-selecting the chip), and the
+ * last transfer might write some register values.
+ */
+struct spi_ioc_transfer {
+ __u64 tx_buf;
+ __u64 rx_buf;
+
+ __u32 len;
+ __u32 speed_hz;
+
+ __u16 delay_usecs;
+ __u8 bits_per_word;
+ __u8 cs_change;
+ __u32 pad;
+
+ /* If the contents of 'struct spi_ioc_transfer' ever change
+ * incompatibly, then the ioctl number (currently 0) must change;
+ * ioctls with constant size fields get a bit more in the way of
+ * error checking than ones (like this) where that field varies.
+ *
+ * NOTE: struct layout is the same in 64bit and 32bit userspace.
+ */
+};
+
+/* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
+#define SPI_MSGSIZE(N) \
+ ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
+ ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
+#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
+
+
+/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) */
+#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
+#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
+
+/* Read / Write SPI bit justification */
+#define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, __u8)
+#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, __u8)
+
+/* Read / Write SPI device word length (1..N) */
+#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, __u8)
+#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, __u8)
+
+/* Read / Write SPI device default max speed hz */
+#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)
+
+
+
+#endif /* SPIDEV_H */