Logo Search packages:      
Sourcecode: linux version File versions  Download package

rte_cb_leds.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);

Generated by  Doxygen 1.6.0   Back to index