rte_cb_leds.c 3.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/*
 * include/asm-v850/rte_cb_leds.c -- Midas lab RTE-CB board LED device support
 *
 *  Copyright (C) 2002,03  NEC Electronics Corporation
 *  Copyright (C) 2002,03  Miles Bader <miles@gnu.org>
 *
 * This file is subject to the terms and conditions of the GNU General
 * Public License.  See the file COPYING in the main directory of this
 * archive for more details.
 *
 * Written by Miles Bader <miles@gnu.org>
 */

#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>

#include <asm/uaccess.h>

#define LEDS_MINOR	169	/* Minor device number, using misc major.  */

/* The actual LED hardware is write-only, so we hold the contents here too.  */
static unsigned char leds_image[LED_NUM_DIGITS] = { 0 };

/* Spinlock protecting the above leds.  */
static DEFINE_SPINLOCK(leds_lock);

/* Common body of LED read/write functions, checks POS and LEN for
   correctness, declares a variable using IMG_DECL, initialized pointing at
   the POS position in the LED image buffer, and and iterates COPY_EXPR
   until BUF is equal to the last buffer position; finally, sets LEN to be
   the amount actually copied.  IMG should be a variable declaration
   (without an initializer or a terminating semicolon); POS, BUF, and LEN
   should all be simple variables.  */
#define DO_LED_COPY(img_decl, pos, buf, len, copy_expr)			\
do {									\
	if (pos > LED_NUM_DIGITS)					\
		len = 0;						\
	else {								\
		if (pos + len > LED_NUM_DIGITS)				\
			len = LED_NUM_DIGITS - pos;			\
									\
		if (len > 0) {						\
45
			unsigned long _flags;				\
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
			const char *_end = buf + len;			\
			img_decl = &leds_image[pos];			\
									\
			spin_lock_irqsave (leds_lock, _flags);		\
			do						\
				(copy_expr);				\
			while (buf != _end);				\
			spin_unlock_irqrestore (leds_lock, _flags);	\
		}							\
	}								\
} while (0)

/* Read LEN bytes from LEDs at position POS, into BUF.
   Returns actual amount read.  */
unsigned read_leds (unsigned pos, char *buf, unsigned len)
{
	DO_LED_COPY (const char *img, pos, buf, len, *buf++ = *img++);
	return len;
}

/* Write LEN bytes to LEDs at position POS, from BUF.
   Returns actual amount written.  */
unsigned write_leds (unsigned pos, const char *buf, unsigned len)
{
	/* We write the actual LED values backwards, because
	   increasing memory addresses reflect LEDs right-to-left. */
	volatile char *led = &LED (LED_NUM_DIGITS - pos - 1);
	/* We invert the value written to the hardware, because 1 = off,
	   and 0 = on.  */
	DO_LED_COPY (char *img, pos, buf, len,
		     *led-- = 0xFF ^ (*img++ = *buf++));
	return len;
}


/* Device functions.  */

static ssize_t leds_dev_read (struct file *file, char *buf, size_t len,
			      loff_t *pos)
{
	char temp_buf[LED_NUM_DIGITS];
	len = read_leds (*pos, temp_buf, len);
	if (copy_to_user (buf, temp_buf, len))
		return -EFAULT;
	*pos += len;
	return len;
}

static ssize_t leds_dev_write (struct file *file, const char *buf, size_t len,
			       loff_t *pos)
{
	char temp_buf[LED_NUM_DIGITS];
	if (copy_from_user (temp_buf, buf, min_t(size_t, len, LED_NUM_DIGITS)))
		return -EFAULT;
	len = write_leds (*pos, temp_buf, len);
	*pos += len;
	return len;
}

static loff_t leds_dev_lseek (struct file *file, loff_t offs, int whence)
{
	if (whence == 1)
		offs += file->f_pos; /* relative */
	else if (whence == 2)
		offs += LED_NUM_DIGITS; /* end-relative */

	if (offs < 0 || offs > LED_NUM_DIGITS)
		return -EINVAL;

	file->f_pos = offs;

	return 0;
}

120
static const struct file_operations leds_fops = {
L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	.read		= leds_dev_read,
	.write		= leds_dev_write,
	.llseek		= leds_dev_lseek
};

static struct miscdevice leds_miscdev = {
	.name		= "leds",
	.minor		= LEDS_MINOR,
	.fops		= &leds_fops
};

int __init leds_dev_init (void)
{
	return misc_register (&leds_miscdev);
}

__initcall (leds_dev_init);