![Arjan van de Ven](/assets/img/avatar_default.png)
Many struct file_operations in the kernel can be "const". Marking them const moves these to the .rodata section, which avoids false sharing with potential dirty data. In addition it'll catch accidental writes at compile time to these shared resources. [akpm@osdl.org: sparc64 fix] Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
138 lines
3.8 KiB
C
138 lines
3.8 KiB
C
/*
|
||
* 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) { \
|
||
unsigned long _flags; \
|
||
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;
|
||
}
|
||
|
||
static const struct file_operations leds_fops = {
|
||
.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);
|