rte_cb_leds.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * include/asm-v850/rte_cb_leds.c -- Midas lab RTE-CB board LED device support
  3. *
  4. * Copyright (C) 2002,03 NEC Electronics Corporation
  5. * Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
  6. *
  7. * This file is subject to the terms and conditions of the GNU General
  8. * Public License. See the file COPYING in the main directory of this
  9. * archive for more details.
  10. *
  11. * Written by Miles Bader <miles@gnu.org>
  12. */
  13. #include <linux/config.h>
  14. #include <linux/init.h>
  15. #include <linux/spinlock.h>
  16. #include <linux/fs.h>
  17. #include <linux/miscdevice.h>
  18. #include <asm/uaccess.h>
  19. #define LEDS_MINOR 169 /* Minor device number, using misc major. */
  20. /* The actual LED hardware is write-only, so we hold the contents here too. */
  21. static unsigned char leds_image[LED_NUM_DIGITS] = { 0 };
  22. /* Spinlock protecting the above leds. */
  23. static DEFINE_SPINLOCK(leds_lock);
  24. /* Common body of LED read/write functions, checks POS and LEN for
  25. correctness, declares a variable using IMG_DECL, initialized pointing at
  26. the POS position in the LED image buffer, and and iterates COPY_EXPR
  27. until BUF is equal to the last buffer position; finally, sets LEN to be
  28. the amount actually copied. IMG should be a variable declaration
  29. (without an initializer or a terminating semicolon); POS, BUF, and LEN
  30. should all be simple variables. */
  31. #define DO_LED_COPY(img_decl, pos, buf, len, copy_expr) \
  32. do { \
  33. if (pos > LED_NUM_DIGITS) \
  34. len = 0; \
  35. else { \
  36. if (pos + len > LED_NUM_DIGITS) \
  37. len = LED_NUM_DIGITS - pos; \
  38. \
  39. if (len > 0) { \
  40. int _flags; \
  41. const char *_end = buf + len; \
  42. img_decl = &leds_image[pos]; \
  43. \
  44. spin_lock_irqsave (leds_lock, _flags); \
  45. do \
  46. (copy_expr); \
  47. while (buf != _end); \
  48. spin_unlock_irqrestore (leds_lock, _flags); \
  49. } \
  50. } \
  51. } while (0)
  52. /* Read LEN bytes from LEDs at position POS, into BUF.
  53. Returns actual amount read. */
  54. unsigned read_leds (unsigned pos, char *buf, unsigned len)
  55. {
  56. DO_LED_COPY (const char *img, pos, buf, len, *buf++ = *img++);
  57. return len;
  58. }
  59. /* Write LEN bytes to LEDs at position POS, from BUF.
  60. Returns actual amount written. */
  61. unsigned write_leds (unsigned pos, const char *buf, unsigned len)
  62. {
  63. /* We write the actual LED values backwards, because
  64. increasing memory addresses reflect LEDs right-to-left. */
  65. volatile char *led = &LED (LED_NUM_DIGITS - pos - 1);
  66. /* We invert the value written to the hardware, because 1 = off,
  67. and 0 = on. */
  68. DO_LED_COPY (char *img, pos, buf, len,
  69. *led-- = 0xFF ^ (*img++ = *buf++));
  70. return len;
  71. }
  72. /* Device functions. */
  73. static ssize_t leds_dev_read (struct file *file, char *buf, size_t len,
  74. loff_t *pos)
  75. {
  76. char temp_buf[LED_NUM_DIGITS];
  77. len = read_leds (*pos, temp_buf, len);
  78. if (copy_to_user (buf, temp_buf, len))
  79. return -EFAULT;
  80. *pos += len;
  81. return len;
  82. }
  83. static ssize_t leds_dev_write (struct file *file, const char *buf, size_t len,
  84. loff_t *pos)
  85. {
  86. char temp_buf[LED_NUM_DIGITS];
  87. if (copy_from_user (temp_buf, buf, min_t(size_t, len, LED_NUM_DIGITS)))
  88. return -EFAULT;
  89. len = write_leds (*pos, temp_buf, len);
  90. *pos += len;
  91. return len;
  92. }
  93. static loff_t leds_dev_lseek (struct file *file, loff_t offs, int whence)
  94. {
  95. if (whence == 1)
  96. offs += file->f_pos; /* relative */
  97. else if (whence == 2)
  98. offs += LED_NUM_DIGITS; /* end-relative */
  99. if (offs < 0 || offs > LED_NUM_DIGITS)
  100. return -EINVAL;
  101. file->f_pos = offs;
  102. return 0;
  103. }
  104. static struct file_operations leds_fops = {
  105. .read = leds_dev_read,
  106. .write = leds_dev_write,
  107. .llseek = leds_dev_lseek
  108. };
  109. static struct miscdevice leds_miscdev = {
  110. .name = "leds",
  111. .minor = LEDS_MINOR,
  112. .fops = &leds_fops
  113. };
  114. int __init leds_dev_init (void)
  115. {
  116. return misc_register (&leds_miscdev);
  117. }
  118. __initcall (leds_dev_init);