aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spidev.c
diff options
context:
space:
mode:
authorJonathan Corbet <corbet@lwn.net>2008-07-14 17:29:34 -0400
committerJonathan Corbet <corbet@lwn.net>2008-07-14 17:29:34 -0400
commit2fceef397f9880b212a74c418290ce69e7ac00eb (patch)
treed9cc09ab992825ef7fede4a688103503e3caf655 /drivers/spi/spidev.c
parentfeae1ef116ed381625d3731c5ae4f4ebcb3fa302 (diff)
parentbce7f793daec3e65ec5c5705d2457b81fe7b5725 (diff)
Merge commit 'v2.6.26' into bkl-removal
Diffstat (limited to 'drivers/spi/spidev.c')
-rw-r--r--drivers/spi/spidev.c176
1 files changed, 127 insertions, 49 deletions
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index ab2b769c83ca..ddbe1a5e970e 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -25,6 +25,7 @@
25#include <linux/ioctl.h> 25#include <linux/ioctl.h>
26#include <linux/fs.h> 26#include <linux/fs.h>
27#include <linux/device.h> 27#include <linux/device.h>
28#include <linux/err.h>
28#include <linux/list.h> 29#include <linux/list.h>
29#include <linux/errno.h> 30#include <linux/errno.h>
30#include <linux/mutex.h> 31#include <linux/mutex.h>
@@ -68,10 +69,12 @@ static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG];
68 | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP) 69 | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP)
69 70
70struct spidev_data { 71struct spidev_data {
71 struct device dev; 72 dev_t devt;
73 spinlock_t spi_lock;
72 struct spi_device *spi; 74 struct spi_device *spi;
73 struct list_head device_entry; 75 struct list_head device_entry;
74 76
77 /* buffer is NULL unless this device is open (users > 0) */
75 struct mutex buf_lock; 78 struct mutex buf_lock;
76 unsigned users; 79 unsigned users;
77 u8 *buffer; 80 u8 *buffer;
@@ -86,12 +89,75 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
86 89
87/*-------------------------------------------------------------------------*/ 90/*-------------------------------------------------------------------------*/
88 91
92/*
93 * We can't use the standard synchronous wrappers for file I/O; we
94 * need to protect against async removal of the underlying spi_device.
95 */
96static void spidev_complete(void *arg)
97{
98 complete(arg);
99}
100
101static ssize_t
102spidev_sync(struct spidev_data *spidev, struct spi_message *message)
103{
104 DECLARE_COMPLETION_ONSTACK(done);
105 int status;
106
107 message->complete = spidev_complete;
108 message->context = &done;
109
110 spin_lock_irq(&spidev->spi_lock);
111 if (spidev->spi == NULL)
112 status = -ESHUTDOWN;
113 else
114 status = spi_async(spidev->spi, message);
115 spin_unlock_irq(&spidev->spi_lock);
116
117 if (status == 0) {
118 wait_for_completion(&done);
119 status = message->status;
120 if (status == 0)
121 status = message->actual_length;
122 }
123 return status;
124}
125
126static inline ssize_t
127spidev_sync_write(struct spidev_data *spidev, size_t len)
128{
129 struct spi_transfer t = {
130 .tx_buf = spidev->buffer,
131 .len = len,
132 };
133 struct spi_message m;
134
135 spi_message_init(&m);
136 spi_message_add_tail(&t, &m);
137 return spidev_sync(spidev, &m);
138}
139
140static inline ssize_t
141spidev_sync_read(struct spidev_data *spidev, size_t len)
142{
143 struct spi_transfer t = {
144 .rx_buf = spidev->buffer,
145 .len = len,
146 };
147 struct spi_message m;
148
149 spi_message_init(&m);
150 spi_message_add_tail(&t, &m);
151 return spidev_sync(spidev, &m);
152}
153
154/*-------------------------------------------------------------------------*/
155
89/* Read-only message with current device setup */ 156/* Read-only message with current device setup */
90static ssize_t 157static ssize_t
91spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) 158spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
92{ 159{
93 struct spidev_data *spidev; 160 struct spidev_data *spidev;
94 struct spi_device *spi;
95 ssize_t status = 0; 161 ssize_t status = 0;
96 162
97 /* chipselect only toggles at start or end of operation */ 163 /* chipselect only toggles at start or end of operation */
@@ -99,18 +165,17 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
99 return -EMSGSIZE; 165 return -EMSGSIZE;
100 166
101 spidev = filp->private_data; 167 spidev = filp->private_data;
102 spi = spidev->spi;
103 168
104 mutex_lock(&spidev->buf_lock); 169 mutex_lock(&spidev->buf_lock);
105 status = spi_read(spi, spidev->buffer, count); 170 status = spidev_sync_read(spidev, count);
106 if (status == 0) { 171 if (status > 0) {
107 unsigned long missing; 172 unsigned long missing;
108 173
109 missing = copy_to_user(buf, spidev->buffer, count); 174 missing = copy_to_user(buf, spidev->buffer, status);
110 if (count && missing == count) 175 if (missing == status)
111 status = -EFAULT; 176 status = -EFAULT;
112 else 177 else
113 status = count - missing; 178 status = status - missing;
114 } 179 }
115 mutex_unlock(&spidev->buf_lock); 180 mutex_unlock(&spidev->buf_lock);
116 181
@@ -123,7 +188,6 @@ spidev_write(struct file *filp, const char __user *buf,
123 size_t count, loff_t *f_pos) 188 size_t count, loff_t *f_pos)
124{ 189{
125 struct spidev_data *spidev; 190 struct spidev_data *spidev;
126 struct spi_device *spi;
127 ssize_t status = 0; 191 ssize_t status = 0;
128 unsigned long missing; 192 unsigned long missing;
129 193
@@ -132,14 +196,11 @@ spidev_write(struct file *filp, const char __user *buf,
132 return -EMSGSIZE; 196 return -EMSGSIZE;
133 197
134 spidev = filp->private_data; 198 spidev = filp->private_data;
135 spi = spidev->spi;
136 199
137 mutex_lock(&spidev->buf_lock); 200 mutex_lock(&spidev->buf_lock);
138 missing = copy_from_user(spidev->buffer, buf, count); 201 missing = copy_from_user(spidev->buffer, buf, count);
139 if (missing == 0) { 202 if (missing == 0) {
140 status = spi_write(spi, spidev->buffer, count); 203 status = spidev_sync_write(spidev, count);
141 if (status == 0)
142 status = count;
143 } else 204 } else
144 status = -EFAULT; 205 status = -EFAULT;
145 mutex_unlock(&spidev->buf_lock); 206 mutex_unlock(&spidev->buf_lock);
@@ -154,7 +215,6 @@ static int spidev_message(struct spidev_data *spidev,
154 struct spi_transfer *k_xfers; 215 struct spi_transfer *k_xfers;
155 struct spi_transfer *k_tmp; 216 struct spi_transfer *k_tmp;
156 struct spi_ioc_transfer *u_tmp; 217 struct spi_ioc_transfer *u_tmp;
157 struct spi_device *spi = spidev->spi;
158 unsigned n, total; 218 unsigned n, total;
159 u8 *buf; 219 u8 *buf;
160 int status = -EFAULT; 220 int status = -EFAULT;
@@ -216,7 +276,7 @@ static int spidev_message(struct spidev_data *spidev,
216 spi_message_add_tail(k_tmp, &msg); 276 spi_message_add_tail(k_tmp, &msg);
217 } 277 }
218 278
219 status = spi_sync(spi, &msg); 279 status = spidev_sync(spidev, &msg);
220 if (status < 0) 280 if (status < 0)
221 goto done; 281 goto done;
222 282
@@ -270,8 +330,16 @@ spidev_ioctl(struct inode *inode, struct file *filp,
270 if (err) 330 if (err)
271 return -EFAULT; 331 return -EFAULT;
272 332
333 /* guard against device removal before, or while,
334 * we issue this ioctl.
335 */
273 spidev = filp->private_data; 336 spidev = filp->private_data;
274 spi = spidev->spi; 337 spin_lock_irq(&spidev->spi_lock);
338 spi = spi_dev_get(spidev->spi);
339 spin_unlock_irq(&spidev->spi_lock);
340
341 if (spi == NULL)
342 return -ESHUTDOWN;
275 343
276 switch (cmd) { 344 switch (cmd) {
277 /* read requests */ 345 /* read requests */
@@ -357,8 +425,10 @@ spidev_ioctl(struct inode *inode, struct file *filp,
357 default: 425 default:
358 /* segmented and/or full-duplex I/O request */ 426 /* segmented and/or full-duplex I/O request */
359 if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) 427 if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
360 || _IOC_DIR(cmd) != _IOC_WRITE) 428 || _IOC_DIR(cmd) != _IOC_WRITE) {
361 return -ENOTTY; 429 retval = -ENOTTY;
430 break;
431 }
362 432
363 tmp = _IOC_SIZE(cmd); 433 tmp = _IOC_SIZE(cmd);
364 if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { 434 if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
@@ -386,6 +456,7 @@ spidev_ioctl(struct inode *inode, struct file *filp,
386 kfree(ioc); 456 kfree(ioc);
387 break; 457 break;
388 } 458 }
459 spi_dev_put(spi);
389 return retval; 460 return retval;
390} 461}
391 462
@@ -398,7 +469,7 @@ static int spidev_open(struct inode *inode, struct file *filp)
398 mutex_lock(&device_list_lock); 469 mutex_lock(&device_list_lock);
399 470
400 list_for_each_entry(spidev, &device_list, device_entry) { 471 list_for_each_entry(spidev, &device_list, device_entry) {
401 if (spidev->dev.devt == inode->i_rdev) { 472 if (spidev->devt == inode->i_rdev) {
402 status = 0; 473 status = 0;
403 break; 474 break;
404 } 475 }
@@ -432,10 +503,22 @@ static int spidev_release(struct inode *inode, struct file *filp)
432 mutex_lock(&device_list_lock); 503 mutex_lock(&device_list_lock);
433 spidev = filp->private_data; 504 spidev = filp->private_data;
434 filp->private_data = NULL; 505 filp->private_data = NULL;
506
507 /* last close? */
435 spidev->users--; 508 spidev->users--;
436 if (!spidev->users) { 509 if (!spidev->users) {
510 int dofree;
511
437 kfree(spidev->buffer); 512 kfree(spidev->buffer);
438 spidev->buffer = NULL; 513 spidev->buffer = NULL;
514
515 /* ... after we unbound from the underlying device? */
516 spin_lock_irq(&spidev->spi_lock);
517 dofree = (spidev->spi == NULL);
518 spin_unlock_irq(&spidev->spi_lock);
519
520 if (dofree)
521 kfree(spidev);
439 } 522 }
440 mutex_unlock(&device_list_lock); 523 mutex_unlock(&device_list_lock);
441 524
@@ -462,19 +545,7 @@ static struct file_operations spidev_fops = {
462 * It also simplifies memory management. 545 * It also simplifies memory management.
463 */ 546 */
464 547
465static void spidev_classdev_release(struct device *dev) 548static struct class *spidev_class;
466{
467 struct spidev_data *spidev;
468
469 spidev = container_of(dev, struct spidev_data, dev);
470 kfree(spidev);
471}
472
473static struct class spidev_class = {
474 .name = "spidev",
475 .owner = THIS_MODULE,
476 .dev_release = spidev_classdev_release,
477};
478 549
479/*-------------------------------------------------------------------------*/ 550/*-------------------------------------------------------------------------*/
480 551
@@ -491,6 +562,7 @@ static int spidev_probe(struct spi_device *spi)
491 562
492 /* Initialize the driver data */ 563 /* Initialize the driver data */
493 spidev->spi = spi; 564 spidev->spi = spi;
565 spin_lock_init(&spidev->spi_lock);
494 mutex_init(&spidev->buf_lock); 566 mutex_init(&spidev->buf_lock);
495 567
496 INIT_LIST_HEAD(&spidev->device_entry); 568 INIT_LIST_HEAD(&spidev->device_entry);
@@ -501,20 +573,20 @@ static int spidev_probe(struct spi_device *spi)
501 mutex_lock(&device_list_lock); 573 mutex_lock(&device_list_lock);
502 minor = find_first_zero_bit(minors, N_SPI_MINORS); 574 minor = find_first_zero_bit(minors, N_SPI_MINORS);
503 if (minor < N_SPI_MINORS) { 575 if (minor < N_SPI_MINORS) {
504 spidev->dev.parent = &spi->dev; 576 struct device *dev;
505 spidev->dev.class = &spidev_class; 577
506 spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor); 578 spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
507 snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id, 579 dev = device_create(spidev_class, &spi->dev, spidev->devt,
508 "spidev%d.%d", 580 "spidev%d.%d",
509 spi->master->bus_num, spi->chip_select); 581 spi->master->bus_num, spi->chip_select);
510 status = device_register(&spidev->dev); 582 status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
511 } else { 583 } else {
512 dev_dbg(&spi->dev, "no minor number available!\n"); 584 dev_dbg(&spi->dev, "no minor number available!\n");
513 status = -ENODEV; 585 status = -ENODEV;
514 } 586 }
515 if (status == 0) { 587 if (status == 0) {
516 set_bit(minor, minors); 588 set_bit(minor, minors);
517 dev_set_drvdata(&spi->dev, spidev); 589 spi_set_drvdata(spi, spidev);
518 list_add(&spidev->device_entry, &device_list); 590 list_add(&spidev->device_entry, &device_list);
519 } 591 }
520 mutex_unlock(&device_list_lock); 592 mutex_unlock(&device_list_lock);
@@ -527,15 +599,21 @@ static int spidev_probe(struct spi_device *spi)
527 599
528static int spidev_remove(struct spi_device *spi) 600static int spidev_remove(struct spi_device *spi)
529{ 601{
530 struct spidev_data *spidev = dev_get_drvdata(&spi->dev); 602 struct spidev_data *spidev = spi_get_drvdata(spi);
531 603
532 mutex_lock(&device_list_lock); 604 /* make sure ops on existing fds can abort cleanly */
605 spin_lock_irq(&spidev->spi_lock);
606 spidev->spi = NULL;
607 spi_set_drvdata(spi, NULL);
608 spin_unlock_irq(&spidev->spi_lock);
533 609
610 /* prevent new opens */
611 mutex_lock(&device_list_lock);
534 list_del(&spidev->device_entry); 612 list_del(&spidev->device_entry);
535 dev_set_drvdata(&spi->dev, NULL); 613 device_destroy(spidev_class, spidev->devt);
536 clear_bit(MINOR(spidev->dev.devt), minors); 614 clear_bit(MINOR(spidev->devt), minors);
537 device_unregister(&spidev->dev); 615 if (spidev->users == 0)
538 616 kfree(spidev);
539 mutex_unlock(&device_list_lock); 617 mutex_unlock(&device_list_lock);
540 618
541 return 0; 619 return 0;
@@ -571,15 +649,15 @@ static int __init spidev_init(void)
571 if (status < 0) 649 if (status < 0)
572 return status; 650 return status;
573 651
574 status = class_register(&spidev_class); 652 spidev_class = class_create(THIS_MODULE, "spidev");
575 if (status < 0) { 653 if (IS_ERR(spidev_class)) {
576 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); 654 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
577 return status; 655 return PTR_ERR(spidev_class);
578 } 656 }
579 657
580 status = spi_register_driver(&spidev_spi); 658 status = spi_register_driver(&spidev_spi);
581 if (status < 0) { 659 if (status < 0) {
582 class_unregister(&spidev_class); 660 class_destroy(spidev_class);
583 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); 661 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
584 } 662 }
585 return status; 663 return status;
@@ -589,7 +667,7 @@ module_init(spidev_init);
589static void __exit spidev_exit(void) 667static void __exit spidev_exit(void)
590{ 668{
591 spi_unregister_driver(&spidev_spi); 669 spi_unregister_driver(&spidev_spi);
592 class_unregister(&spidev_class); 670 class_destroy(spidev_class);
593 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); 671 unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
594} 672}
595module_exit(spidev_exit); 673module_exit(spidev_exit);