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