diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-07-08 01:47:47 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-08 01:47:47 -0400 |
commit | 93022136fff9e6130aa128a5ed8a599e93ac813c (patch) | |
tree | 185390fb75a3d7423cc508610b76637c957205b9 /drivers/spi/spidev.c | |
parent | c49c412a47b5102516d3313d4eba38cb1e968721 (diff) | |
parent | b7279469d66b55119784b8b9529c99c1955fe747 (diff) |
Merge commit 'v2.6.26-rc9' into x86/cpu
Diffstat (limited to 'drivers/spi/spidev.c')
-rw-r--r-- | drivers/spi/spidev.c | 74 |
1 files changed, 38 insertions, 36 deletions
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 41620c0fb046..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,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; |
@@ -165,14 +167,14 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | |||
165 | 167 | ||
166 | mutex_lock(&spidev->buf_lock); | 168 | mutex_lock(&spidev->buf_lock); |
167 | status = spidev_sync_read(spidev, count); | 169 | status = spidev_sync_read(spidev, count); |
168 | if (status == 0) { | 170 | if (status > 0) { |
169 | unsigned long missing; | 171 | unsigned long missing; |
170 | 172 | ||
171 | missing = copy_to_user(buf, spidev->buffer, count); | 173 | missing = copy_to_user(buf, spidev->buffer, status); |
172 | if (count && missing == count) | 174 | if (missing == status) |
173 | status = -EFAULT; | 175 | status = -EFAULT; |
174 | else | 176 | else |
175 | status = count - missing; | 177 | status = status - missing; |
176 | } | 178 | } |
177 | mutex_unlock(&spidev->buf_lock); | 179 | mutex_unlock(&spidev->buf_lock); |
178 | 180 | ||
@@ -198,8 +200,6 @@ spidev_write(struct file *filp, const char __user *buf, | |||
198 | missing = copy_from_user(spidev->buffer, buf, count); | 200 | missing = copy_from_user(spidev->buffer, buf, count); |
199 | if (missing == 0) { | 201 | if (missing == 0) { |
200 | status = spidev_sync_write(spidev, count); | 202 | status = spidev_sync_write(spidev, count); |
201 | if (status == 0) | ||
202 | status = count; | ||
203 | } else | 203 | } else |
204 | status = -EFAULT; | 204 | status = -EFAULT; |
205 | mutex_unlock(&spidev->buf_lock); | 205 | mutex_unlock(&spidev->buf_lock); |
@@ -467,7 +467,7 @@ static int spidev_open(struct inode *inode, struct file *filp) | |||
467 | mutex_lock(&device_list_lock); | 467 | mutex_lock(&device_list_lock); |
468 | 468 | ||
469 | list_for_each_entry(spidev, &device_list, device_entry) { | 469 | list_for_each_entry(spidev, &device_list, device_entry) { |
470 | if (spidev->dev.devt == inode->i_rdev) { | 470 | if (spidev->devt == inode->i_rdev) { |
471 | status = 0; | 471 | status = 0; |
472 | break; | 472 | break; |
473 | } | 473 | } |
@@ -500,10 +500,22 @@ static int spidev_release(struct inode *inode, struct file *filp) | |||
500 | mutex_lock(&device_list_lock); | 500 | mutex_lock(&device_list_lock); |
501 | spidev = filp->private_data; | 501 | spidev = filp->private_data; |
502 | filp->private_data = NULL; | 502 | filp->private_data = NULL; |
503 | |||
504 | /* last close? */ | ||
503 | spidev->users--; | 505 | spidev->users--; |
504 | if (!spidev->users) { | 506 | if (!spidev->users) { |
507 | int dofree; | ||
508 | |||
505 | kfree(spidev->buffer); | 509 | kfree(spidev->buffer); |
506 | 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); | ||
507 | } | 519 | } |
508 | mutex_unlock(&device_list_lock); | 520 | mutex_unlock(&device_list_lock); |
509 | 521 | ||
@@ -530,19 +542,7 @@ static struct file_operations spidev_fops = { | |||
530 | * It also simplifies memory management. | 542 | * It also simplifies memory management. |
531 | */ | 543 | */ |
532 | 544 | ||
533 | static void spidev_classdev_release(struct device *dev) | 545 | 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 | 546 | ||
547 | /*-------------------------------------------------------------------------*/ | 547 | /*-------------------------------------------------------------------------*/ |
548 | 548 | ||
@@ -570,20 +570,20 @@ static int spidev_probe(struct spi_device *spi) | |||
570 | mutex_lock(&device_list_lock); | 570 | mutex_lock(&device_list_lock); |
571 | minor = find_first_zero_bit(minors, N_SPI_MINORS); | 571 | minor = find_first_zero_bit(minors, N_SPI_MINORS); |
572 | if (minor < N_SPI_MINORS) { | 572 | if (minor < N_SPI_MINORS) { |
573 | spidev->dev.parent = &spi->dev; | 573 | struct device *dev; |
574 | spidev->dev.class = &spidev_class; | 574 | |
575 | spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor); | 575 | spidev->devt = MKDEV(SPIDEV_MAJOR, minor); |
576 | snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id, | 576 | dev = device_create(spidev_class, &spi->dev, spidev->devt, |
577 | "spidev%d.%d", | 577 | "spidev%d.%d", |
578 | spi->master->bus_num, spi->chip_select); | 578 | spi->master->bus_num, spi->chip_select); |
579 | status = device_register(&spidev->dev); | 579 | status = IS_ERR(dev) ? PTR_ERR(dev) : 0; |
580 | } else { | 580 | } else { |
581 | dev_dbg(&spi->dev, "no minor number available!\n"); | 581 | dev_dbg(&spi->dev, "no minor number available!\n"); |
582 | status = -ENODEV; | 582 | status = -ENODEV; |
583 | } | 583 | } |
584 | if (status == 0) { | 584 | if (status == 0) { |
585 | set_bit(minor, minors); | 585 | set_bit(minor, minors); |
586 | dev_set_drvdata(&spi->dev, spidev); | 586 | spi_set_drvdata(spi, spidev); |
587 | list_add(&spidev->device_entry, &device_list); | 587 | list_add(&spidev->device_entry, &device_list); |
588 | } | 588 | } |
589 | mutex_unlock(&device_list_lock); | 589 | mutex_unlock(&device_list_lock); |
@@ -596,19 +596,21 @@ static int spidev_probe(struct spi_device *spi) | |||
596 | 596 | ||
597 | static int spidev_remove(struct spi_device *spi) | 597 | static int spidev_remove(struct spi_device *spi) |
598 | { | 598 | { |
599 | struct spidev_data *spidev = dev_get_drvdata(&spi->dev); | 599 | struct spidev_data *spidev = spi_get_drvdata(spi); |
600 | 600 | ||
601 | /* make sure ops on existing fds can abort cleanly */ | 601 | /* make sure ops on existing fds can abort cleanly */ |
602 | spin_lock_irq(&spidev->spi_lock); | 602 | spin_lock_irq(&spidev->spi_lock); |
603 | spidev->spi = NULL; | 603 | spidev->spi = NULL; |
604 | spi_set_drvdata(spi, NULL); | ||
604 | spin_unlock_irq(&spidev->spi_lock); | 605 | spin_unlock_irq(&spidev->spi_lock); |
605 | 606 | ||
606 | /* prevent new opens */ | 607 | /* prevent new opens */ |
607 | mutex_lock(&device_list_lock); | 608 | mutex_lock(&device_list_lock); |
608 | list_del(&spidev->device_entry); | 609 | list_del(&spidev->device_entry); |
609 | dev_set_drvdata(&spi->dev, NULL); | 610 | device_destroy(spidev_class, spidev->devt); |
610 | clear_bit(MINOR(spidev->dev.devt), minors); | 611 | clear_bit(MINOR(spidev->devt), minors); |
611 | device_unregister(&spidev->dev); | 612 | if (spidev->users == 0) |
613 | kfree(spidev); | ||
612 | mutex_unlock(&device_list_lock); | 614 | mutex_unlock(&device_list_lock); |
613 | 615 | ||
614 | return 0; | 616 | return 0; |
@@ -644,15 +646,15 @@ static int __init spidev_init(void) | |||
644 | if (status < 0) | 646 | if (status < 0) |
645 | return status; | 647 | return status; |
646 | 648 | ||
647 | status = class_register(&spidev_class); | 649 | spidev_class = class_create(THIS_MODULE, "spidev"); |
648 | if (status < 0) { | 650 | if (IS_ERR(spidev_class)) { |
649 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 651 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
650 | return status; | 652 | return PTR_ERR(spidev_class); |
651 | } | 653 | } |
652 | 654 | ||
653 | status = spi_register_driver(&spidev_spi); | 655 | status = spi_register_driver(&spidev_spi); |
654 | if (status < 0) { | 656 | if (status < 0) { |
655 | class_unregister(&spidev_class); | 657 | class_destroy(spidev_class); |
656 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 658 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
657 | } | 659 | } |
658 | return status; | 660 | return status; |
@@ -662,7 +664,7 @@ module_init(spidev_init); | |||
662 | static void __exit spidev_exit(void) | 664 | static void __exit spidev_exit(void) |
663 | { | 665 | { |
664 | spi_unregister_driver(&spidev_spi); | 666 | spi_unregister_driver(&spidev_spi); |
665 | class_unregister(&spidev_class); | 667 | class_destroy(spidev_class); |
666 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 668 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
667 | } | 669 | } |
668 | module_exit(spidev_exit); | 670 | module_exit(spidev_exit); |