diff options
Diffstat (limited to 'drivers/spi/spidev.c')
| -rw-r--r-- | drivers/spi/spidev.c | 125 |
1 files changed, 97 insertions, 28 deletions
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 6941e04afb8c..4eb7a980e670 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c | |||
| @@ -14,10 +14,6 @@ | |||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 21 | */ | 17 | */ |
| 22 | 18 | ||
| 23 | #include <linux/init.h> | 19 | #include <linux/init.h> |
| @@ -317,6 +313,37 @@ done: | |||
| 317 | return status; | 313 | return status; |
| 318 | } | 314 | } |
| 319 | 315 | ||
| 316 | static struct spi_ioc_transfer * | ||
| 317 | spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc, | ||
| 318 | unsigned *n_ioc) | ||
| 319 | { | ||
| 320 | struct spi_ioc_transfer *ioc; | ||
| 321 | u32 tmp; | ||
| 322 | |||
| 323 | /* Check type, command number and direction */ | ||
| 324 | if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC | ||
| 325 | || _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) | ||
| 326 | || _IOC_DIR(cmd) != _IOC_WRITE) | ||
| 327 | return ERR_PTR(-ENOTTY); | ||
| 328 | |||
| 329 | tmp = _IOC_SIZE(cmd); | ||
| 330 | if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) | ||
| 331 | return ERR_PTR(-EINVAL); | ||
| 332 | *n_ioc = tmp / sizeof(struct spi_ioc_transfer); | ||
| 333 | if (*n_ioc == 0) | ||
| 334 | return NULL; | ||
| 335 | |||
| 336 | /* copy into scratch area */ | ||
| 337 | ioc = kmalloc(tmp, GFP_KERNEL); | ||
| 338 | if (!ioc) | ||
| 339 | return ERR_PTR(-ENOMEM); | ||
| 340 | if (__copy_from_user(ioc, u_ioc, tmp)) { | ||
| 341 | kfree(ioc); | ||
| 342 | return ERR_PTR(-EFAULT); | ||
| 343 | } | ||
| 344 | return ioc; | ||
| 345 | } | ||
| 346 | |||
| 320 | static long | 347 | static long |
| 321 | spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 348 | spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
| 322 | { | 349 | { |
| @@ -456,32 +483,15 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
| 456 | 483 | ||
| 457 | default: | 484 | default: |
| 458 | /* segmented and/or full-duplex I/O request */ | 485 | /* segmented and/or full-duplex I/O request */ |
| 459 | if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) | 486 | /* Check message and copy into scratch area */ |
| 460 | || _IOC_DIR(cmd) != _IOC_WRITE) { | 487 | ioc = spidev_get_ioc_message(cmd, |
| 461 | retval = -ENOTTY; | 488 | (struct spi_ioc_transfer __user *)arg, &n_ioc); |
| 462 | break; | 489 | if (IS_ERR(ioc)) { |
| 463 | } | 490 | retval = PTR_ERR(ioc); |
| 464 | |||
| 465 | tmp = _IOC_SIZE(cmd); | ||
| 466 | if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { | ||
| 467 | retval = -EINVAL; | ||
| 468 | break; | ||
| 469 | } | ||
| 470 | n_ioc = tmp / sizeof(struct spi_ioc_transfer); | ||
| 471 | if (n_ioc == 0) | ||
| 472 | break; | ||
| 473 | |||
| 474 | /* copy into scratch area */ | ||
| 475 | ioc = kmalloc(tmp, GFP_KERNEL); | ||
| 476 | if (!ioc) { | ||
| 477 | retval = -ENOMEM; | ||
| 478 | break; | ||
| 479 | } | ||
| 480 | if (__copy_from_user(ioc, (void __user *)arg, tmp)) { | ||
| 481 | kfree(ioc); | ||
| 482 | retval = -EFAULT; | ||
| 483 | break; | 491 | break; |
| 484 | } | 492 | } |
| 493 | if (!ioc) | ||
| 494 | break; /* n_ioc is also 0 */ | ||
| 485 | 495 | ||
| 486 | /* translate to spi_message, execute */ | 496 | /* translate to spi_message, execute */ |
| 487 | retval = spidev_message(spidev, ioc, n_ioc); | 497 | retval = spidev_message(spidev, ioc, n_ioc); |
| @@ -496,8 +506,67 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
| 496 | 506 | ||
| 497 | #ifdef CONFIG_COMPAT | 507 | #ifdef CONFIG_COMPAT |
| 498 | static long | 508 | static long |
| 509 | spidev_compat_ioc_message(struct file *filp, unsigned int cmd, | ||
| 510 | unsigned long arg) | ||
| 511 | { | ||
| 512 | struct spi_ioc_transfer __user *u_ioc; | ||
| 513 | int retval = 0; | ||
| 514 | struct spidev_data *spidev; | ||
| 515 | struct spi_device *spi; | ||
| 516 | unsigned n_ioc, n; | ||
| 517 | struct spi_ioc_transfer *ioc; | ||
| 518 | |||
| 519 | u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg); | ||
| 520 | if (!access_ok(VERIFY_READ, u_ioc, _IOC_SIZE(cmd))) | ||
| 521 | return -EFAULT; | ||
| 522 | |||
| 523 | /* guard against device removal before, or while, | ||
| 524 | * we issue this ioctl. | ||
| 525 | */ | ||
| 526 | spidev = filp->private_data; | ||
| 527 | spin_lock_irq(&spidev->spi_lock); | ||
| 528 | spi = spi_dev_get(spidev->spi); | ||
| 529 | spin_unlock_irq(&spidev->spi_lock); | ||
| 530 | |||
| 531 | if (spi == NULL) | ||
| 532 | return -ESHUTDOWN; | ||
| 533 | |||
| 534 | /* SPI_IOC_MESSAGE needs the buffer locked "normally" */ | ||
| 535 | mutex_lock(&spidev->buf_lock); | ||
| 536 | |||
| 537 | /* Check message and copy into scratch area */ | ||
| 538 | ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc); | ||
| 539 | if (IS_ERR(ioc)) { | ||
| 540 | retval = PTR_ERR(ioc); | ||
| 541 | goto done; | ||
| 542 | } | ||
| 543 | if (!ioc) | ||
| 544 | goto done; /* n_ioc is also 0 */ | ||
| 545 | |||
| 546 | /* Convert buffer pointers */ | ||
| 547 | for (n = 0; n < n_ioc; n++) { | ||
| 548 | ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf); | ||
| 549 | ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf); | ||
| 550 | } | ||
| 551 | |||
| 552 | /* translate to spi_message, execute */ | ||
| 553 | retval = spidev_message(spidev, ioc, n_ioc); | ||
| 554 | kfree(ioc); | ||
| 555 | |||
| 556 | done: | ||
| 557 | mutex_unlock(&spidev->buf_lock); | ||
| 558 | spi_dev_put(spi); | ||
| 559 | return retval; | ||
| 560 | } | ||
| 561 | |||
| 562 | static long | ||
| 499 | spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 563 | spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
| 500 | { | 564 | { |
| 565 | if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC | ||
| 566 | && _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0)) | ||
| 567 | && _IOC_DIR(cmd) == _IOC_WRITE) | ||
| 568 | return spidev_compat_ioc_message(filp, cmd, arg); | ||
| 569 | |||
| 501 | return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); | 570 | return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); |
| 502 | } | 571 | } |
| 503 | #else | 572 | #else |
