diff options
Diffstat (limited to 'drivers/spi')
| -rw-r--r-- | drivers/spi/spidev.c | 64 |
1 files changed, 34 insertions, 30 deletions
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 41620c0fb046..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,11 +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; |
| 71 | spinlock_t spi_lock; | 72 | spinlock_t spi_lock; |
| 72 | struct spi_device *spi; | 73 | struct spi_device *spi; |
| 73 | struct list_head device_entry; | 74 | struct list_head device_entry; |
| 74 | 75 | ||
| 76 | /* buffer is NULL unless this device is open (users > 0) */ | ||
| 75 | struct mutex buf_lock; | 77 | struct mutex buf_lock; |
| 76 | unsigned users; | 78 | unsigned users; |
| 77 | u8 *buffer; | 79 | u8 *buffer; |
| @@ -467,7 +469,7 @@ static int spidev_open(struct inode *inode, struct file *filp) | |||
| 467 | mutex_lock(&device_list_lock); | 469 | mutex_lock(&device_list_lock); |
| 468 | 470 | ||
| 469 | list_for_each_entry(spidev, &device_list, device_entry) { | 471 | list_for_each_entry(spidev, &device_list, device_entry) { |
| 470 | if (spidev->dev.devt == inode->i_rdev) { | 472 | if (spidev->devt == inode->i_rdev) { |
| 471 | status = 0; | 473 | status = 0; |
| 472 | break; | 474 | break; |
| 473 | } | 475 | } |
| @@ -500,10 +502,22 @@ static int spidev_release(struct inode *inode, struct file *filp) | |||
| 500 | mutex_lock(&device_list_lock); | 502 | mutex_lock(&device_list_lock); |
| 501 | spidev = filp->private_data; | 503 | spidev = filp->private_data; |
| 502 | filp->private_data = NULL; | 504 | filp->private_data = NULL; |
| 505 | |||
| 506 | /* last close? */ | ||
| 503 | spidev->users--; | 507 | spidev->users--; |
| 504 | if (!spidev->users) { | 508 | if (!spidev->users) { |
| 509 | int dofree; | ||
| 510 | |||
| 505 | kfree(spidev->buffer); | 511 | kfree(spidev->buffer); |
| 506 | 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); | ||
| 507 | } | 521 | } |
| 508 | mutex_unlock(&device_list_lock); | 522 | mutex_unlock(&device_list_lock); |
| 509 | 523 | ||
| @@ -530,19 +544,7 @@ static struct file_operations spidev_fops = { | |||
| 530 | * It also simplifies memory management. | 544 | * It also simplifies memory management. |
| 531 | */ | 545 | */ |
| 532 | 546 | ||
| 533 | static void spidev_classdev_release(struct device *dev) | 547 | static struct class *spidev_class; |
| 534 | { | ||
| 535 | struct spidev_data *spidev; | ||
| 536 | |||
| 537 | spidev = container_of(dev, struct spidev_data, dev); | ||
| 538 | kfree(spidev); | ||
| 539 | } | ||
| 540 | |||
| 541 | static struct class spidev_class = { | ||
| 542 | .name = "spidev", | ||
| 543 | .owner = THIS_MODULE, | ||
| 544 | .dev_release = spidev_classdev_release, | ||
| 545 | }; | ||
| 546 | 548 | ||
| 547 | /*-------------------------------------------------------------------------*/ | 549 | /*-------------------------------------------------------------------------*/ |
| 548 | 550 | ||
| @@ -570,20 +572,20 @@ static int spidev_probe(struct spi_device *spi) | |||
| 570 | mutex_lock(&device_list_lock); | 572 | mutex_lock(&device_list_lock); |
| 571 | minor = find_first_zero_bit(minors, N_SPI_MINORS); | 573 | minor = find_first_zero_bit(minors, N_SPI_MINORS); |
| 572 | if (minor < N_SPI_MINORS) { | 574 | if (minor < N_SPI_MINORS) { |
| 573 | spidev->dev.parent = &spi->dev; | 575 | struct device *dev; |
| 574 | spidev->dev.class = &spidev_class; | 576 | |
| 575 | spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor); | 577 | spidev->devt = MKDEV(SPIDEV_MAJOR, minor); |
| 576 | snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id, | 578 | dev = device_create(spidev_class, &spi->dev, spidev->devt, |
| 577 | "spidev%d.%d", | 579 | "spidev%d.%d", |
| 578 | spi->master->bus_num, spi->chip_select); | 580 | spi->master->bus_num, spi->chip_select); |
| 579 | status = device_register(&spidev->dev); | 581 | status = IS_ERR(dev) ? PTR_ERR(dev) : 0; |
| 580 | } else { | 582 | } else { |
| 581 | dev_dbg(&spi->dev, "no minor number available!\n"); | 583 | dev_dbg(&spi->dev, "no minor number available!\n"); |
| 582 | status = -ENODEV; | 584 | status = -ENODEV; |
| 583 | } | 585 | } |
| 584 | if (status == 0) { | 586 | if (status == 0) { |
| 585 | set_bit(minor, minors); | 587 | set_bit(minor, minors); |
| 586 | dev_set_drvdata(&spi->dev, spidev); | 588 | spi_set_drvdata(spi, spidev); |
| 587 | list_add(&spidev->device_entry, &device_list); | 589 | list_add(&spidev->device_entry, &device_list); |
| 588 | } | 590 | } |
| 589 | mutex_unlock(&device_list_lock); | 591 | mutex_unlock(&device_list_lock); |
| @@ -596,19 +598,21 @@ static int spidev_probe(struct spi_device *spi) | |||
| 596 | 598 | ||
| 597 | static int spidev_remove(struct spi_device *spi) | 599 | static int spidev_remove(struct spi_device *spi) |
| 598 | { | 600 | { |
| 599 | struct spidev_data *spidev = dev_get_drvdata(&spi->dev); | 601 | struct spidev_data *spidev = spi_get_drvdata(spi); |
| 600 | 602 | ||
| 601 | /* make sure ops on existing fds can abort cleanly */ | 603 | /* make sure ops on existing fds can abort cleanly */ |
| 602 | spin_lock_irq(&spidev->spi_lock); | 604 | spin_lock_irq(&spidev->spi_lock); |
| 603 | spidev->spi = NULL; | 605 | spidev->spi = NULL; |
| 606 | spi_set_drvdata(spi, NULL); | ||
| 604 | spin_unlock_irq(&spidev->spi_lock); | 607 | spin_unlock_irq(&spidev->spi_lock); |
| 605 | 608 | ||
| 606 | /* prevent new opens */ | 609 | /* prevent new opens */ |
| 607 | mutex_lock(&device_list_lock); | 610 | mutex_lock(&device_list_lock); |
| 608 | list_del(&spidev->device_entry); | 611 | list_del(&spidev->device_entry); |
| 609 | dev_set_drvdata(&spi->dev, NULL); | 612 | device_destroy(spidev_class, spidev->devt); |
| 610 | clear_bit(MINOR(spidev->dev.devt), minors); | 613 | clear_bit(MINOR(spidev->devt), minors); |
| 611 | device_unregister(&spidev->dev); | 614 | if (spidev->users == 0) |
| 615 | kfree(spidev); | ||
| 612 | mutex_unlock(&device_list_lock); | 616 | mutex_unlock(&device_list_lock); |
| 613 | 617 | ||
| 614 | return 0; | 618 | return 0; |
| @@ -644,15 +648,15 @@ static int __init spidev_init(void) | |||
| 644 | if (status < 0) | 648 | if (status < 0) |
| 645 | return status; | 649 | return status; |
| 646 | 650 | ||
| 647 | status = class_register(&spidev_class); | 651 | spidev_class = class_create(THIS_MODULE, "spidev"); |
| 648 | if (status < 0) { | 652 | if (IS_ERR(spidev_class)) { |
| 649 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 653 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
| 650 | return status; | 654 | return PTR_ERR(spidev_class); |
| 651 | } | 655 | } |
| 652 | 656 | ||
| 653 | status = spi_register_driver(&spidev_spi); | 657 | status = spi_register_driver(&spidev_spi); |
| 654 | if (status < 0) { | 658 | if (status < 0) { |
| 655 | class_unregister(&spidev_class); | 659 | class_destroy(spidev_class); |
| 656 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 660 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
| 657 | } | 661 | } |
| 658 | return status; | 662 | return status; |
| @@ -662,7 +666,7 @@ module_init(spidev_init); | |||
| 662 | static void __exit spidev_exit(void) | 666 | static void __exit spidev_exit(void) |
| 663 | { | 667 | { |
| 664 | spi_unregister_driver(&spidev_spi); | 668 | spi_unregister_driver(&spidev_spi); |
| 665 | class_unregister(&spidev_class); | 669 | class_destroy(spidev_class); |
| 666 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 670 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
| 667 | } | 671 | } |
| 668 | module_exit(spidev_exit); | 672 | module_exit(spidev_exit); |
