diff options
Diffstat (limited to 'drivers/spi/spidev.c')
| -rw-r--r-- | drivers/spi/spidev.c | 166 |
1 files changed, 123 insertions, 43 deletions
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index b3518ca9f04e..799337f7fde1 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,10 +164,9 @@ 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 | ||
| @@ -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,12 +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) | 203 | if (status == 0) |
| 141 | status = count; | 204 | status = count; |
| 142 | } else | 205 | } else |
| @@ -153,7 +216,6 @@ static int spidev_message(struct spidev_data *spidev, | |||
| 153 | struct spi_transfer *k_xfers; | 216 | struct spi_transfer *k_xfers; |
| 154 | struct spi_transfer *k_tmp; | 217 | struct spi_transfer *k_tmp; |
| 155 | struct spi_ioc_transfer *u_tmp; | 218 | struct spi_ioc_transfer *u_tmp; |
| 156 | struct spi_device *spi = spidev->spi; | ||
| 157 | unsigned n, total; | 219 | unsigned n, total; |
| 158 | u8 *buf; | 220 | u8 *buf; |
| 159 | int status = -EFAULT; | 221 | int status = -EFAULT; |
| @@ -215,7 +277,7 @@ static int spidev_message(struct spidev_data *spidev, | |||
| 215 | spi_message_add_tail(k_tmp, &msg); | 277 | spi_message_add_tail(k_tmp, &msg); |
| 216 | } | 278 | } |
| 217 | 279 | ||
| 218 | status = spi_sync(spi, &msg); | 280 | status = spidev_sync(spidev, &msg); |
| 219 | if (status < 0) | 281 | if (status < 0) |
| 220 | goto done; | 282 | goto done; |
| 221 | 283 | ||
| @@ -269,8 +331,16 @@ spidev_ioctl(struct inode *inode, struct file *filp, | |||
| 269 | if (err) | 331 | if (err) |
| 270 | return -EFAULT; | 332 | return -EFAULT; |
| 271 | 333 | ||
| 334 | /* guard against device removal before, or while, | ||
| 335 | * we issue this ioctl. | ||
| 336 | */ | ||
| 272 | spidev = filp->private_data; | 337 | spidev = filp->private_data; |
| 273 | spi = spidev->spi; | 338 | spin_lock_irq(&spidev->spi_lock); |
| 339 | spi = spi_dev_get(spidev->spi); | ||
| 340 | spin_unlock_irq(&spidev->spi_lock); | ||
| 341 | |||
| 342 | if (spi == NULL) | ||
| 343 | return -ESHUTDOWN; | ||
| 274 | 344 | ||
| 275 | switch (cmd) { | 345 | switch (cmd) { |
| 276 | /* read requests */ | 346 | /* read requests */ |
| @@ -356,8 +426,10 @@ spidev_ioctl(struct inode *inode, struct file *filp, | |||
| 356 | default: | 426 | default: |
| 357 | /* segmented and/or full-duplex I/O request */ | 427 | /* segmented and/or full-duplex I/O request */ |
| 358 | if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) | 428 | if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) |
| 359 | || _IOC_DIR(cmd) != _IOC_WRITE) | 429 | || _IOC_DIR(cmd) != _IOC_WRITE) { |
| 360 | return -ENOTTY; | 430 | retval = -ENOTTY; |
| 431 | break; | ||
| 432 | } | ||
| 361 | 433 | ||
| 362 | tmp = _IOC_SIZE(cmd); | 434 | tmp = _IOC_SIZE(cmd); |
| 363 | if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { | 435 | if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { |
| @@ -385,6 +457,7 @@ spidev_ioctl(struct inode *inode, struct file *filp, | |||
| 385 | kfree(ioc); | 457 | kfree(ioc); |
| 386 | break; | 458 | break; |
| 387 | } | 459 | } |
| 460 | spi_dev_put(spi); | ||
| 388 | return retval; | 461 | return retval; |
| 389 | } | 462 | } |
| 390 | 463 | ||
| @@ -396,7 +469,7 @@ static int spidev_open(struct inode *inode, struct file *filp) | |||
| 396 | mutex_lock(&device_list_lock); | 469 | mutex_lock(&device_list_lock); |
| 397 | 470 | ||
| 398 | list_for_each_entry(spidev, &device_list, device_entry) { | 471 | list_for_each_entry(spidev, &device_list, device_entry) { |
| 399 | if (spidev->dev.devt == inode->i_rdev) { | 472 | if (spidev->devt == inode->i_rdev) { |
| 400 | status = 0; | 473 | status = 0; |
| 401 | break; | 474 | break; |
| 402 | } | 475 | } |
| @@ -429,10 +502,22 @@ static int spidev_release(struct inode *inode, struct file *filp) | |||
| 429 | mutex_lock(&device_list_lock); | 502 | mutex_lock(&device_list_lock); |
| 430 | spidev = filp->private_data; | 503 | spidev = filp->private_data; |
| 431 | filp->private_data = NULL; | 504 | filp->private_data = NULL; |
| 505 | |||
| 506 | /* last close? */ | ||
| 432 | spidev->users--; | 507 | spidev->users--; |
| 433 | if (!spidev->users) { | 508 | if (!spidev->users) { |
| 509 | int dofree; | ||
| 510 | |||
| 434 | kfree(spidev->buffer); | 511 | kfree(spidev->buffer); |
| 435 | spidev->buffer = NULL; | 512 | spidev->buffer = NULL; |
| 513 | |||
| 514 | /* ... after we unbound from the underlying device? */ | ||
| 515 | spin_lock_irq(&spidev->spi_lock); | ||
| 516 | dofree = (spidev->spi == NULL); | ||
| 517 | spin_unlock_irq(&spidev->spi_lock); | ||
| 518 | |||
| 519 | if (dofree) | ||
| 520 | kfree(spidev); | ||
| 436 | } | 521 | } |
| 437 | mutex_unlock(&device_list_lock); | 522 | mutex_unlock(&device_list_lock); |
| 438 | 523 | ||
| @@ -459,19 +544,7 @@ static struct file_operations spidev_fops = { | |||
| 459 | * It also simplifies memory management. | 544 | * It also simplifies memory management. |
| 460 | */ | 545 | */ |
| 461 | 546 | ||
| 462 | static void spidev_classdev_release(struct device *dev) | 547 | 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 | 548 | ||
| 476 | /*-------------------------------------------------------------------------*/ | 549 | /*-------------------------------------------------------------------------*/ |
| 477 | 550 | ||
| @@ -488,6 +561,7 @@ static int spidev_probe(struct spi_device *spi) | |||
| 488 | 561 | ||
| 489 | /* Initialize the driver data */ | 562 | /* Initialize the driver data */ |
| 490 | spidev->spi = spi; | 563 | spidev->spi = spi; |
| 564 | spin_lock_init(&spidev->spi_lock); | ||
| 491 | mutex_init(&spidev->buf_lock); | 565 | mutex_init(&spidev->buf_lock); |
| 492 | 566 | ||
| 493 | INIT_LIST_HEAD(&spidev->device_entry); | 567 | INIT_LIST_HEAD(&spidev->device_entry); |
| @@ -498,20 +572,20 @@ static int spidev_probe(struct spi_device *spi) | |||
| 498 | mutex_lock(&device_list_lock); | 572 | mutex_lock(&device_list_lock); |
| 499 | minor = find_first_zero_bit(minors, N_SPI_MINORS); | 573 | minor = find_first_zero_bit(minors, N_SPI_MINORS); |
| 500 | if (minor < N_SPI_MINORS) { | 574 | if (minor < N_SPI_MINORS) { |
| 501 | spidev->dev.parent = &spi->dev; | 575 | struct device *dev; |
| 502 | spidev->dev.class = &spidev_class; | 576 | |
| 503 | spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor); | 577 | spidev->devt = MKDEV(SPIDEV_MAJOR, minor); |
| 504 | snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id, | 578 | dev = device_create(spidev_class, &spi->dev, spidev->devt, |
| 505 | "spidev%d.%d", | 579 | "spidev%d.%d", |
| 506 | spi->master->bus_num, spi->chip_select); | 580 | spi->master->bus_num, spi->chip_select); |
| 507 | status = device_register(&spidev->dev); | 581 | status = IS_ERR(dev) ? PTR_ERR(dev) : 0; |
| 508 | } else { | 582 | } else { |
| 509 | dev_dbg(&spi->dev, "no minor number available!\n"); | 583 | dev_dbg(&spi->dev, "no minor number available!\n"); |
| 510 | status = -ENODEV; | 584 | status = -ENODEV; |
| 511 | } | 585 | } |
| 512 | if (status == 0) { | 586 | if (status == 0) { |
| 513 | set_bit(minor, minors); | 587 | set_bit(minor, minors); |
| 514 | dev_set_drvdata(&spi->dev, spidev); | 588 | spi_set_drvdata(spi, spidev); |
| 515 | list_add(&spidev->device_entry, &device_list); | 589 | list_add(&spidev->device_entry, &device_list); |
| 516 | } | 590 | } |
| 517 | mutex_unlock(&device_list_lock); | 591 | mutex_unlock(&device_list_lock); |
| @@ -524,15 +598,21 @@ static int spidev_probe(struct spi_device *spi) | |||
| 524 | 598 | ||
| 525 | static int spidev_remove(struct spi_device *spi) | 599 | static int spidev_remove(struct spi_device *spi) |
| 526 | { | 600 | { |
| 527 | struct spidev_data *spidev = dev_get_drvdata(&spi->dev); | 601 | struct spidev_data *spidev = spi_get_drvdata(spi); |
| 528 | 602 | ||
| 529 | mutex_lock(&device_list_lock); | 603 | /* make sure ops on existing fds can abort cleanly */ |
| 604 | spin_lock_irq(&spidev->spi_lock); | ||
| 605 | spidev->spi = NULL; | ||
| 606 | spi_set_drvdata(spi, NULL); | ||
| 607 | spin_unlock_irq(&spidev->spi_lock); | ||
| 530 | 608 | ||
| 609 | /* prevent new opens */ | ||
| 610 | mutex_lock(&device_list_lock); | ||
| 531 | list_del(&spidev->device_entry); | 611 | list_del(&spidev->device_entry); |
| 532 | dev_set_drvdata(&spi->dev, NULL); | 612 | device_destroy(spidev_class, spidev->devt); |
| 533 | clear_bit(MINOR(spidev->dev.devt), minors); | 613 | clear_bit(MINOR(spidev->devt), minors); |
| 534 | device_unregister(&spidev->dev); | 614 | if (spidev->users == 0) |
| 535 | 615 | kfree(spidev); | |
| 536 | mutex_unlock(&device_list_lock); | 616 | mutex_unlock(&device_list_lock); |
| 537 | 617 | ||
| 538 | return 0; | 618 | return 0; |
| @@ -568,15 +648,15 @@ static int __init spidev_init(void) | |||
| 568 | if (status < 0) | 648 | if (status < 0) |
| 569 | return status; | 649 | return status; |
| 570 | 650 | ||
| 571 | status = class_register(&spidev_class); | 651 | spidev_class = class_create(THIS_MODULE, "spidev"); |
| 572 | if (status < 0) { | 652 | if (IS_ERR(spidev_class)) { |
| 573 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 653 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
| 574 | return status; | 654 | return PTR_ERR(spidev_class); |
| 575 | } | 655 | } |
| 576 | 656 | ||
| 577 | status = spi_register_driver(&spidev_spi); | 657 | status = spi_register_driver(&spidev_spi); |
| 578 | if (status < 0) { | 658 | if (status < 0) { |
| 579 | class_unregister(&spidev_class); | 659 | class_destroy(spidev_class); |
| 580 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 660 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
| 581 | } | 661 | } |
| 582 | return status; | 662 | return status; |
| @@ -586,7 +666,7 @@ module_init(spidev_init); | |||
| 586 | static void __exit spidev_exit(void) | 666 | static void __exit spidev_exit(void) |
| 587 | { | 667 | { |
| 588 | spi_unregister_driver(&spidev_spi); | 668 | spi_unregister_driver(&spidev_spi); |
| 589 | class_unregister(&spidev_class); | 669 | class_destroy(spidev_class); |
| 590 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 670 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
| 591 | } | 671 | } |
| 592 | module_exit(spidev_exit); | 672 | module_exit(spidev_exit); |
