diff options
| -rw-r--r-- | drivers/spi/spidev.c | 102 |
1 files changed, 89 insertions, 13 deletions
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index b3518ca9f04e..41620c0fb046 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c | |||
| @@ -68,6 +68,7 @@ static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG]; | |||
| 68 | 68 | ||
| 69 | struct spidev_data { | 69 | struct spidev_data { |
| 70 | struct device dev; | 70 | struct device dev; |
| 71 | spinlock_t spi_lock; | ||
| 71 | struct spi_device *spi; | 72 | struct spi_device *spi; |
| 72 | struct list_head device_entry; | 73 | struct list_head device_entry; |
| 73 | 74 | ||
| @@ -85,12 +86,75 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); | |||
| 85 | 86 | ||
| 86 | /*-------------------------------------------------------------------------*/ | 87 | /*-------------------------------------------------------------------------*/ |
| 87 | 88 | ||
| 89 | /* | ||
| 90 | * We can't use the standard synchronous wrappers for file I/O; we | ||
| 91 | * need to protect against async removal of the underlying spi_device. | ||
| 92 | */ | ||
| 93 | static void spidev_complete(void *arg) | ||
| 94 | { | ||
| 95 | complete(arg); | ||
| 96 | } | ||
| 97 | |||
| 98 | static ssize_t | ||
| 99 | spidev_sync(struct spidev_data *spidev, struct spi_message *message) | ||
| 100 | { | ||
| 101 | DECLARE_COMPLETION_ONSTACK(done); | ||
| 102 | int status; | ||
| 103 | |||
| 104 | message->complete = spidev_complete; | ||
| 105 | message->context = &done; | ||
| 106 | |||
| 107 | spin_lock_irq(&spidev->spi_lock); | ||
| 108 | if (spidev->spi == NULL) | ||
| 109 | status = -ESHUTDOWN; | ||
| 110 | else | ||
| 111 | status = spi_async(spidev->spi, message); | ||
| 112 | spin_unlock_irq(&spidev->spi_lock); | ||
| 113 | |||
| 114 | if (status == 0) { | ||
| 115 | wait_for_completion(&done); | ||
| 116 | status = message->status; | ||
| 117 | if (status == 0) | ||
| 118 | status = message->actual_length; | ||
| 119 | } | ||
| 120 | return status; | ||
| 121 | } | ||
| 122 | |||
| 123 | static inline ssize_t | ||
| 124 | spidev_sync_write(struct spidev_data *spidev, size_t len) | ||
| 125 | { | ||
| 126 | struct spi_transfer t = { | ||
| 127 | .tx_buf = spidev->buffer, | ||
| 128 | .len = len, | ||
| 129 | }; | ||
| 130 | struct spi_message m; | ||
| 131 | |||
| 132 | spi_message_init(&m); | ||
| 133 | spi_message_add_tail(&t, &m); | ||
| 134 | return spidev_sync(spidev, &m); | ||
| 135 | } | ||
| 136 | |||
| 137 | static inline ssize_t | ||
| 138 | spidev_sync_read(struct spidev_data *spidev, size_t len) | ||
| 139 | { | ||
| 140 | struct spi_transfer t = { | ||
| 141 | .rx_buf = spidev->buffer, | ||
| 142 | .len = len, | ||
| 143 | }; | ||
| 144 | struct spi_message m; | ||
| 145 | |||
| 146 | spi_message_init(&m); | ||
| 147 | spi_message_add_tail(&t, &m); | ||
| 148 | return spidev_sync(spidev, &m); | ||
| 149 | } | ||
| 150 | |||
| 151 | /*-------------------------------------------------------------------------*/ | ||
| 152 | |||
| 88 | /* Read-only message with current device setup */ | 153 | /* Read-only message with current device setup */ |
| 89 | static ssize_t | 154 | static ssize_t |
| 90 | spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | 155 | spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) |
| 91 | { | 156 | { |
| 92 | struct spidev_data *spidev; | 157 | struct spidev_data *spidev; |
| 93 | struct spi_device *spi; | ||
| 94 | ssize_t status = 0; | 158 | ssize_t status = 0; |
| 95 | 159 | ||
| 96 | /* chipselect only toggles at start or end of operation */ | 160 | /* chipselect only toggles at start or end of operation */ |
| @@ -98,10 +162,9 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | |||
| 98 | return -EMSGSIZE; | 162 | return -EMSGSIZE; |
| 99 | 163 | ||
| 100 | spidev = filp->private_data; | 164 | spidev = filp->private_data; |
| 101 | spi = spidev->spi; | ||
| 102 | 165 | ||
| 103 | mutex_lock(&spidev->buf_lock); | 166 | mutex_lock(&spidev->buf_lock); |
| 104 | status = spi_read(spi, spidev->buffer, count); | 167 | status = spidev_sync_read(spidev, count); |
| 105 | if (status == 0) { | 168 | if (status == 0) { |
| 106 | unsigned long missing; | 169 | unsigned long missing; |
| 107 | 170 | ||
| @@ -122,7 +185,6 @@ spidev_write(struct file *filp, const char __user *buf, | |||
| 122 | size_t count, loff_t *f_pos) | 185 | size_t count, loff_t *f_pos) |
| 123 | { | 186 | { |
| 124 | struct spidev_data *spidev; | 187 | struct spidev_data *spidev; |
| 125 | struct spi_device *spi; | ||
| 126 | ssize_t status = 0; | 188 | ssize_t status = 0; |
| 127 | unsigned long missing; | 189 | unsigned long missing; |
| 128 | 190 | ||
| @@ -131,12 +193,11 @@ spidev_write(struct file *filp, const char __user *buf, | |||
| 131 | return -EMSGSIZE; | 193 | return -EMSGSIZE; |
| 132 | 194 | ||
| 133 | spidev = filp->private_data; | 195 | spidev = filp->private_data; |
| 134 | spi = spidev->spi; | ||
| 135 | 196 | ||
| 136 | mutex_lock(&spidev->buf_lock); | 197 | mutex_lock(&spidev->buf_lock); |
| 137 | missing = copy_from_user(spidev->buffer, buf, count); | 198 | missing = copy_from_user(spidev->buffer, buf, count); |
| 138 | if (missing == 0) { | 199 | if (missing == 0) { |
| 139 | status = spi_write(spi, spidev->buffer, count); | 200 | status = spidev_sync_write(spidev, count); |
| 140 | if (status == 0) | 201 | if (status == 0) |
| 141 | status = count; | 202 | status = count; |
| 142 | } else | 203 | } else |
| @@ -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 | ||
| @@ -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); |
| @@ -526,13 +598,17 @@ 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 = dev_get_drvdata(&spi->dev); |
| 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 | spin_unlock_irq(&spidev->spi_lock); | ||
| 530 | 605 | ||
| 606 | /* prevent new opens */ | ||
| 607 | mutex_lock(&device_list_lock); | ||
| 531 | list_del(&spidev->device_entry); | 608 | list_del(&spidev->device_entry); |
| 532 | dev_set_drvdata(&spi->dev, NULL); | 609 | dev_set_drvdata(&spi->dev, NULL); |
| 533 | clear_bit(MINOR(spidev->dev.devt), minors); | 610 | clear_bit(MINOR(spidev->dev.devt), minors); |
| 534 | device_unregister(&spidev->dev); | 611 | device_unregister(&spidev->dev); |
| 535 | |||
| 536 | mutex_unlock(&device_list_lock); | 612 | mutex_unlock(&device_list_lock); |
| 537 | 613 | ||
| 538 | return 0; | 614 | return 0; |
