blob: b662ad838940940eb39585e0443c922de54d5212 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
14#include <linux/config.h>
15#include <linux/init.h>
16#include <linux/spinlock.h>
17#include <linux/fs.h>
18#include <linux/miscdevice.h>
19
20#include <asm/uaccess.h>
21
22#define LEDS_MINOR 169 /* Minor device number, using misc major. */
23
24/* The actual LED hardware is write-only, so we hold the contents here too. */
25static unsigned char leds_image[LED_NUM_DIGITS] = { 0 };
26
27/* Spinlock protecting the above leds. */
28static DEFINE_SPINLOCK(leds_lock);
29
30/* Common body of LED read/write functions, checks POS and LEN for
31 correctness, declares a variable using IMG_DECL, initialized pointing at
32 the POS position in the LED image buffer, and and iterates COPY_EXPR
33 until BUF is equal to the last buffer position; finally, sets LEN to be
34 the amount actually copied. IMG should be a variable declaration
35 (without an initializer or a terminating semicolon); POS, BUF, and LEN
36 should all be simple variables. */
37#define DO_LED_COPY(img_decl, pos, buf, len, copy_expr) \
38do { \
39 if (pos > LED_NUM_DIGITS) \
40 len = 0; \
41 else { \
42 if (pos + len > LED_NUM_DIGITS) \
43 len = LED_NUM_DIGITS - pos; \
44 \
45 if (len > 0) { \
46 int _flags; \
47 const char *_end = buf + len; \
48 img_decl = &leds_image[pos]; \
49 \
50 spin_lock_irqsave (leds_lock, _flags); \
51 do \
52 (copy_expr); \
53 while (buf != _end); \
54 spin_unlock_irqrestore (leds_lock, _flags); \
55 } \
56 } \
57} while (0)
58
59/* Read LEN bytes from LEDs at position POS, into BUF.
60 Returns actual amount read. */
61unsigned read_leds (unsigned pos, char *buf, unsigned len)
62{
63 DO_LED_COPY (const char *img, pos, buf, len, *buf++ = *img++);
64 return len;
65}
66
67/* Write LEN bytes to LEDs at position POS, from BUF.
68 Returns actual amount written. */
69unsigned write_leds (unsigned pos, const char *buf, unsigned len)
70{
71 /* We write the actual LED values backwards, because
72 increasing memory addresses reflect LEDs right-to-left. */
73 volatile char *led = &LED (LED_NUM_DIGITS - pos - 1);
74 /* We invert the value written to the hardware, because 1 = off,
75 and 0 = on. */
76 DO_LED_COPY (char *img, pos, buf, len,
77 *led-- = 0xFF ^ (*img++ = *buf++));
78 return len;
79}
80
81
82/* Device functions. */
83
84static ssize_t leds_dev_read (struct file *file, char *buf, size_t len,
85 loff_t *pos)
86{
87 char temp_buf[LED_NUM_DIGITS];
88 len = read_leds (*pos, temp_buf, len);
89 if (copy_to_user (buf, temp_buf, len))
90 return -EFAULT;
91 *pos += len;
92 return len;
93}
94
95static ssize_t leds_dev_write (struct file *file, const char *buf, size_t len,
96 loff_t *pos)
97{
98 char temp_buf[LED_NUM_DIGITS];
99 if (copy_from_user (temp_buf, buf, min_t(size_t, len, LED_NUM_DIGITS)))
100 return -EFAULT;
101 len = write_leds (*pos, temp_buf, len);
102 *pos += len;
103 return len;
104}
105
106static loff_t leds_dev_lseek (struct file *file, loff_t offs, int whence)
107{
108 if (whence == 1)
109 offs += file->f_pos; /* relative */
110 else if (whence == 2)
111 offs += LED_NUM_DIGITS; /* end-relative */
112
113 if (offs < 0 || offs > LED_NUM_DIGITS)
114 return -EINVAL;
115
116 file->f_pos = offs;
117
118 return 0;
119}
120
121static struct file_operations leds_fops = {
122 .read = leds_dev_read,
123 .write = leds_dev_write,
124 .llseek = leds_dev_lseek
125};
126
127static struct miscdevice leds_miscdev = {
128 .name = "leds",
129 .minor = LEDS_MINOR,
130 .fops = &leds_fops
131};
132
133int __init leds_dev_init (void)
134{
135 return misc_register (&leds_miscdev);
136}
137
138__initcall (leds_dev_init);