aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAndrea Paterniani <a.paterniani@swapp-eng.it>2007-05-08 03:32:15 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:15:15 -0400
commit814a8d50eb1d88cedcef97567be53ee0d4512631 (patch)
treeedf10598ae95e5729edca3095b60641606b62939 /drivers
parent735ce95e6b9a262d1fbc0ddb5620deb3a29d1067 (diff)
/dev/spidevB.C interface
Add a filesystem API for <linux/spi/spi.h> stack. The initial version of this interface is purely synchronous. dbrownell@users.sourceforge.net: Cleaned up, bugfixed; much simplified; added preliminary documentation. Works with mdev given CONFIG_SYSFS_DEPRECATED; and presumably udev. Updated SPI_IOC_MESSAGE ioctl to full spi_message semantics, supporting groups of one or more transfers (each of which may be full duplex if desired). This is marked as EXPERIMENTAL with an explicit disclaimer that the API (notably the ioctls) is subject to change. Signed-off-by: Andrea Paterniani <a.paterniani@swapp-eng.it> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Cc: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig9
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spidev.c584
3 files changed, 594 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4a012d9acbff..584ed9f74700 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -159,6 +159,15 @@ config SPI_AT25
159 This driver can also be built as a module. If so, the module 159 This driver can also be built as a module. If so, the module
160 will be called at25. 160 will be called at25.
161 161
162config SPI_SPIDEV
163 tristate "User mode SPI device driver support"
164 depends on SPI_MASTER && EXPERIMENTAL
165 help
166 This supports user mode SPI protocol drivers.
167
168 Note that this application programming interface is EXPERIMENTAL
169 and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
170
162# 171#
163# Add new SPI protocol masters in alphabetical order above this line 172# Add new SPI protocol masters in alphabetical order above this line
164# 173#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index a95ade857a2f..4cc5e99dd59a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
25 25
26# SPI protocol drivers (device/link on bus) 26# SPI protocol drivers (device/link on bus)
27obj-$(CONFIG_SPI_AT25) += at25.o 27obj-$(CONFIG_SPI_AT25) += at25.o
28obj-$(CONFIG_SPI_SPIDEV) += spidev.o
28# ... add above this line ... 29# ... add above this line ...
29 30
30# SPI slave controller drivers (upstream link) 31# SPI slave controller drivers (upstream link)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
new file mode 100644
index 000000000000..c0a6dce800a3
--- /dev/null
+++ b/drivers/spi/spidev.c
@@ -0,0 +1,584 @@
1/*
2 * spidev.c -- simple synchronous userspace interface to SPI devices
3 *
4 * Copyright (C) 2006 SWAPP
5 * Andrea Paterniani <a.paterniani@swapp-eng.it>
6 * Copyright (C) 2007 David Brownell (simplification, cleanup)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/ioctl.h>
26#include <linux/fs.h>
27#include <linux/device.h>
28#include <linux/list.h>
29#include <linux/errno.h>
30#include <linux/mutex.h>
31#include <linux/slab.h>
32
33#include <linux/spi/spi.h>
34#include <linux/spi/spidev.h>
35
36#include <asm/uaccess.h>
37
38
39/*
40 * This supports acccess to SPI devices using normal userspace I/O calls.
41 * Note that while traditional UNIX/POSIX I/O semantics are half duplex,
42 * and often mask message boundaries, full SPI support requires full duplex
43 * transfers. There are several kinds of of internal message boundaries to
44 * handle chipselect management and other protocol options.
45 *
46 * SPI has a character major number assigned. We allocate minor numbers
47 * dynamically using a bitmask. You must use hotplug tools, such as udev
48 * (or mdev with busybox) to create and destroy the /dev/spidevB.C device
49 * nodes, since there is no fixed association of minor numbers with any
50 * particular SPI bus or device.
51 */
52#define SPIDEV_MAJOR 153 /* assigned */
53#define N_SPI_MINORS 32 /* ... up to 256 */
54
55static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG];
56
57
58/* Bit masks for spi_device.mode management */
59#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL)
60
61
62struct spidev_data {
63 struct device dev;
64 struct spi_device *spi;
65 struct list_head device_entry;
66
67 struct mutex buf_lock;
68 unsigned users;
69 u8 *buffer;
70};
71
72static LIST_HEAD(device_list);
73static DEFINE_MUTEX(device_list_lock);
74
75static unsigned bufsiz = 4096;
76module_param(bufsiz, uint, S_IRUGO);
77MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
78
79/*-------------------------------------------------------------------------*/
80
81/* Read-only message with current device setup */
82static ssize_t
83spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
84{
85 struct spidev_data *spidev;
86 struct spi_device *spi;
87 ssize_t status = 0;
88
89 /* chipselect only toggles at start or end of operation */
90 if (count > bufsiz)
91 return -EMSGSIZE;
92
93 spidev = filp->private_data;
94 spi = spidev->spi;
95
96 mutex_lock(&spidev->buf_lock);
97 status = spi_read(spi, spidev->buffer, count);
98 if (status == 0) {
99 unsigned long missing;
100
101 missing = copy_to_user(buf, spidev->buffer, count);
102 if (count && missing == count)
103 status = -EFAULT;
104 else
105 status = count - missing;
106 }
107 mutex_unlock(&spidev->buf_lock);
108
109 return status;
110}
111
112/* Write-only message with current device setup */
113static ssize_t
114spidev_write(struct file *filp, const char __user *buf,
115 size_t count, loff_t *f_pos)
116{
117 struct spidev_data *spidev;
118 struct spi_device *spi;
119 ssize_t status = 0;
120 unsigned long missing;
121
122 /* chipselect only toggles at start or end of operation */
123 if (count > bufsiz)
124 return -EMSGSIZE;
125
126 spidev = filp->private_data;
127 spi = spidev->spi;
128
129 mutex_lock(&spidev->buf_lock);
130 missing = copy_from_user(spidev->buffer, buf, count);
131 if (missing == 0) {
132 status = spi_write(spi, spidev->buffer, count);
133 if (status == 0)
134 status = count;
135 } else
136 status = -EFAULT;
137 mutex_unlock(&spidev->buf_lock);
138
139 return status;
140}
141
142static int spidev_message(struct spidev_data *spidev,
143 struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
144{
145 struct spi_message msg;
146 struct spi_transfer *k_xfers;
147 struct spi_transfer *k_tmp;
148 struct spi_ioc_transfer *u_tmp;
149 struct spi_device *spi = spidev->spi;
150 unsigned n, total;
151 u8 *buf;
152 int status = -EFAULT;
153
154 spi_message_init(&msg);
155 k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
156 if (k_xfers == NULL)
157 return -ENOMEM;
158
159 /* Construct spi_message, copying any tx data to bounce buffer.
160 * We walk the array of user-provided transfers, using each one
161 * to initialize a kernel version of the same transfer.
162 */
163 mutex_lock(&spidev->buf_lock);
164 buf = spidev->buffer;
165 total = 0;
166 for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
167 n;
168 n--, k_tmp++, u_tmp++) {
169 k_tmp->len = u_tmp->len;
170
171 if (u_tmp->rx_buf) {
172 k_tmp->rx_buf = buf;
173 if (!access_ok(VERIFY_WRITE, u_tmp->rx_buf, u_tmp->len))
174 goto done;
175 }
176 if (u_tmp->tx_buf) {
177 k_tmp->tx_buf = buf;
178 if (copy_from_user(buf, (const u8 __user *)u_tmp->tx_buf,
179 u_tmp->len))
180 goto done;
181 }
182
183 total += k_tmp->len;
184 if (total > bufsiz) {
185 status = -EMSGSIZE;
186 goto done;
187 }
188 buf += k_tmp->len;
189
190 k_tmp->cs_change = !!u_tmp->cs_change;
191 k_tmp->bits_per_word = u_tmp->bits_per_word;
192 k_tmp->delay_usecs = u_tmp->delay_usecs;
193 k_tmp->speed_hz = u_tmp->speed_hz;
194#ifdef VERBOSE
195 dev_dbg(&spi->dev,
196 " xfer len %zd %s%s%s%dbits %u usec %uHz\n",
197 u_tmp->len,
198 u_tmp->rx_buf ? "rx " : "",
199 u_tmp->tx_buf ? "tx " : "",
200 u_tmp->cs_change ? "cs " : "",
201 u_tmp->bits_per_word ? : spi->bits_per_word,
202 u_tmp->delay_usecs,
203 u_tmp->speed_hz ? : spi->max_speed_hz);
204#endif
205 spi_message_add_tail(k_tmp, &msg);
206 }
207
208 status = spi_sync(spi, &msg);
209 if (status < 0)
210 goto done;
211
212 /* copy any rx data out of bounce buffer */
213 buf = spidev->buffer;
214 for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
215 if (u_tmp->rx_buf) {
216 if (__copy_to_user((u8 __user *)u_tmp->rx_buf, buf,
217 u_tmp->len)) {
218 status = -EFAULT;
219 goto done;
220 }
221 }
222 buf += u_tmp->len;
223 }
224 status = total;
225
226done:
227 mutex_unlock(&spidev->buf_lock);
228 kfree(k_xfers);
229 return status;
230}
231
232static int
233spidev_ioctl(struct inode *inode, struct file *filp,
234 unsigned int cmd, unsigned long arg)
235{
236 int err = 0;
237 int retval = 0;
238 struct spidev_data *spidev;
239 struct spi_device *spi;
240 u32 tmp;
241 unsigned n_ioc;
242 struct spi_ioc_transfer *ioc;
243
244 /* Check type and command number */
245 if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
246 return -ENOTTY;
247
248 /* Check access direction once here; don't repeat below.
249 * IOC_DIR is from the user perspective, while access_ok is
250 * from the kernel perspective; so they look reversed.
251 */
252 if (_IOC_DIR(cmd) & _IOC_READ)
253 err = !access_ok(VERIFY_WRITE,
254 (void __user *)arg, _IOC_SIZE(cmd));
255 if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
256 err = !access_ok(VERIFY_READ,
257 (void __user *)arg, _IOC_SIZE(cmd));
258 if (err)
259 return -EFAULT;
260
261 spidev = filp->private_data;
262 spi = spidev->spi;
263
264 switch (cmd) {
265 /* read requests */
266 case SPI_IOC_RD_MODE:
267 retval = __put_user(spi->mode & SPI_MODE_MASK,
268 (__u8 __user *)arg);
269 break;
270 case SPI_IOC_RD_LSB_FIRST:
271 retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
272 (__u8 __user *)arg);
273 break;
274 case SPI_IOC_RD_BITS_PER_WORD:
275 retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
276 break;
277 case SPI_IOC_RD_MAX_SPEED_HZ:
278 retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
279 break;
280
281 /* write requests */
282 case SPI_IOC_WR_MODE:
283 retval = __get_user(tmp, (u8 __user *)arg);
284 if (retval == 0) {
285 u8 save = spi->mode;
286
287 if (tmp & ~SPI_MODE_MASK) {
288 retval = -EINVAL;
289 break;
290 }
291
292 tmp |= spi->mode & ~SPI_MODE_MASK;
293 spi->mode = (u8)tmp;
294 retval = spi_setup(spi);
295 if (retval < 0)
296 spi->mode = save;
297 else
298 dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
299 }
300 break;
301 case SPI_IOC_WR_LSB_FIRST:
302 retval = __get_user(tmp, (__u8 __user *)arg);
303 if (retval == 0) {
304 u8 save = spi->mode;
305
306 if (tmp)
307 spi->mode |= SPI_LSB_FIRST;
308 else
309 spi->mode &= ~SPI_LSB_FIRST;
310 retval = spi_setup(spi);
311 if (retval < 0)
312 spi->mode = save;
313 else
314 dev_dbg(&spi->dev, "%csb first\n",
315 tmp ? 'l' : 'm');
316 }
317 break;
318 case SPI_IOC_WR_BITS_PER_WORD:
319 retval = __get_user(tmp, (__u8 __user *)arg);
320 if (retval == 0) {
321 u8 save = spi->bits_per_word;
322
323 spi->bits_per_word = tmp;
324 retval = spi_setup(spi);
325 if (retval < 0)
326 spi->bits_per_word = save;
327 else
328 dev_dbg(&spi->dev, "%d bits per word\n", tmp);
329 }
330 break;
331 case SPI_IOC_WR_MAX_SPEED_HZ:
332 retval = __get_user(tmp, (__u32 __user *)arg);
333 if (retval == 0) {
334 u32 save = spi->max_speed_hz;
335
336 spi->max_speed_hz = tmp;
337 retval = spi_setup(spi);
338 if (retval < 0)
339 spi->max_speed_hz = save;
340 else
341 dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
342 }
343 break;
344
345 default:
346 /* segmented and/or full-duplex I/O request */
347 if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
348 || _IOC_DIR(cmd) != _IOC_WRITE)
349 return -ENOTTY;
350
351 tmp = _IOC_SIZE(cmd);
352 if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
353 retval = -EINVAL;
354 break;
355 }
356 n_ioc = tmp / sizeof(struct spi_ioc_transfer);
357 if (n_ioc == 0)
358 break;
359
360 /* copy into scratch area */
361 ioc = kmalloc(tmp, GFP_KERNEL);
362 if (!ioc) {
363 retval = -ENOMEM;
364 break;
365 }
366 if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
367 retval = -EFAULT;
368 break;
369 }
370
371 /* translate to spi_message, execute */
372 retval = spidev_message(spidev, ioc, n_ioc);
373 kfree(ioc);
374 break;
375 }
376 return retval;
377}
378
379static int spidev_open(struct inode *inode, struct file *filp)
380{
381 struct spidev_data *spidev;
382 int status = -ENXIO;
383
384 mutex_lock(&device_list_lock);
385
386 list_for_each_entry(spidev, &device_list, device_entry) {
387 if (spidev->dev.devt == inode->i_rdev) {
388 status = 0;
389 break;
390 }
391 }
392 if (status == 0) {
393 if (!spidev->buffer) {
394 spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
395 if (!spidev->buffer) {
396 dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
397 status = -ENOMEM;
398 }
399 }
400 if (status == 0) {
401 spidev->users++;
402 filp->private_data = spidev;
403 nonseekable_open(inode, filp);
404 }
405 } else
406 pr_debug("spidev: nothing for minor %d\n", iminor(inode));
407
408 mutex_unlock(&device_list_lock);
409 return status;
410}
411
412static int spidev_release(struct inode *inode, struct file *filp)
413{
414 struct spidev_data *spidev;
415 int status = 0;
416
417 mutex_lock(&device_list_lock);
418 spidev = filp->private_data;
419 filp->private_data = NULL;
420 spidev->users--;
421 if (!spidev->users) {
422 kfree(spidev->buffer);
423 spidev->buffer = NULL;
424 }
425 mutex_unlock(&device_list_lock);
426
427 return status;
428}
429
430static struct file_operations spidev_fops = {
431 .owner = THIS_MODULE,
432 /* REVISIT switch to aio primitives, so that userspace
433 * gets more complete API coverage. It'll simplify things
434 * too, except for the locking.
435 */
436 .write = spidev_write,
437 .read = spidev_read,
438 .ioctl = spidev_ioctl,
439 .open = spidev_open,
440 .release = spidev_release,
441};
442
443/*-------------------------------------------------------------------------*/
444
445/* The main reason to have this class is to make mdev/udev create the
446 * /dev/spidevB.C character device nodes exposing our userspace API.
447 * It also simplifies memory management.
448 */
449
450static void spidev_classdev_release(struct device *dev)
451{
452 struct spidev_data *spidev;
453
454 spidev = container_of(dev, struct spidev_data, dev);
455 kfree(spidev);
456}
457
458static struct class spidev_class = {
459 .name = "spidev",
460 .owner = THIS_MODULE,
461 .dev_release = spidev_classdev_release,
462};
463
464/*-------------------------------------------------------------------------*/
465
466static int spidev_probe(struct spi_device *spi)
467{
468 struct spidev_data *spidev;
469 int status;
470 unsigned long minor;
471
472 /* Allocate driver data */
473 spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
474 if (!spidev)
475 return -ENOMEM;
476
477 /* Initialize the driver data */
478 spidev->spi = spi;
479 mutex_init(&spidev->buf_lock);
480
481 INIT_LIST_HEAD(&spidev->device_entry);
482
483 /* If we can allocate a minor number, hook up this device.
484 * Reusing minors is fine so long as udev or mdev is working.
485 */
486 mutex_lock(&device_list_lock);
487 minor = find_first_zero_bit(minors, ARRAY_SIZE(minors));
488 if (minor < N_SPI_MINORS) {
489 spidev->dev.parent = &spi->dev;
490 spidev->dev.class = &spidev_class;
491 spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor);
492 snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id,
493 "spidev%d.%d",
494 spi->master->bus_num, spi->chip_select);
495 status = device_register(&spidev->dev);
496 } else {
497 dev_dbg(&spi->dev, "no minor number available!\n");
498 status = -ENODEV;
499 }
500 if (status == 0) {
501 set_bit(minor, minors);
502 dev_set_drvdata(&spi->dev, spidev);
503 list_add(&spidev->device_entry, &device_list);
504 }
505 mutex_unlock(&device_list_lock);
506
507 if (status != 0)
508 kfree(spidev);
509
510 return status;
511}
512
513static int spidev_remove(struct spi_device *spi)
514{
515 struct spidev_data *spidev = dev_get_drvdata(&spi->dev);
516
517 mutex_lock(&device_list_lock);
518
519 list_del(&spidev->device_entry);
520 dev_set_drvdata(&spi->dev, NULL);
521 clear_bit(MINOR(spidev->dev.devt), minors);
522 device_unregister(&spidev->dev);
523
524 mutex_unlock(&device_list_lock);
525
526 return 0;
527}
528
529static struct spi_driver spidev_spi = {
530 .driver = {
531 .name = "spidev",
532 .owner = THIS_MODULE,
533 },
534 .probe = spidev_probe,
535 .remove = __devexit_p(spidev_remove),
536
537 /* NOTE: suspend/resume methods are not necessary here.
538 * We don't do anything except pass the requests to/from
539 * the underlying controller. The refrigerator handles
540 * most issues; the controller driver handles the rest.
541 */
542};
543
544/*-------------------------------------------------------------------------*/
545
546static int __init spidev_init(void)
547{
548 int status;
549
550 /* Claim our 256 reserved device numbers. Then register a class
551 * that will key udev/mdev to add/remove /dev nodes. Last, register
552 * the driver which manages those device numbers.
553 */
554 BUILD_BUG_ON(N_SPI_MINORS > 256);
555 status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
556 if (status < 0)
557 return status;
558
559 status = class_register(&spidev_class);
560 if (status < 0) {
561 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
562 return status;
563 }
564
565 status = spi_register_driver(&spidev_spi);
566 if (status < 0) {
567 class_unregister(&spidev_class);
568 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
569 }
570 return status;
571}
572module_init(spidev_init);
573
574static void __exit spidev_exit(void)
575{
576 spi_unregister_driver(&spidev_spi);
577 class_unregister(&spidev_class);
578 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
579}
580module_exit(spidev_exit);
581
582MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
583MODULE_DESCRIPTION("User mode SPI device interface");
584MODULE_LICENSE("GPL");