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