home *** CD-ROM | disk | FTP | other *** search
- /*
- * led.c
- *
- * Written by: Stephen J. Friedl
- * V-Systems, Inc.
- * friedl@mtndew.Tustin.CA.US
- * 29 July, 1989
- *
- * Re-written by D'Arcy J.M. Cain
- * given version number 2.0
- *
- * This is a driver for the front-panel LEDs on an Everex
- * STEP system. This is an eight-character display, and is
- * normally driven by the BIOS to show the current drive,
- * cylinder, and head being accessed. UNIX bypasses the BIOS
- * entirely, so it freezes shortly after bootup.
- *
- * You can make the thing display some cute signon message when
- * the machine boots by setting the string LED_INIT_STR in the make
- * file. A normal write call will scroll the output across the LEDs
- * (referred to by a friend as "times squaring" the output.) There
- * are two ioctl calls available. Function 0 re-displays the signon
- * message and function 1 displays the message pointed to by the third
- * argument to ioctl. Both of these functions display all at once as
- * opposed to the scroll effect of the normal write call.
- *
- * There are multiple devices available to the user. The way that this
- * works is that any of the devices may be opened by any process but
- * each is opened exclusively and each device blocks while devices
- * with a higher minor number are open. The idea is to put something
- * like ledtime on the lowest device and letting other things interupt
- * it temporarily.
- *
- * This driver has been tested on an Everex STEP/25 running
- * Esix System V Release 4.0.4 beta. Information about the
- * usage of the 8042 UPI has been derived from the Everex
- * STEP 386 Hardware Service and Maintenance Guide, part
- * number KIT 00076-00 (about a hundred bucks from your
- * Authorized Everex dealer).
- *
- * ========================================================
- * In the spirit of free interchange of { hopefully } high-
- * quality software, this driver is released to the public
- * domain and can be used for any purposes at all, including
- * personal, commercial, military, blackmail, etc.
- *
- * Have fun, folks.
- * ========================================================
- * Additional from D'Arcy: I am including my changes in the
- * above release.
- */
-
- #define _KERNEL 1
- #include <limits.h>
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/user.h>
- #include <sys/systm.h>
- #include <sys/sysmacros.h>
- #include <sys/inline.h>
- #include <sys/errno.h>
- #include <sys/signal.h>
- #include <sys/dir.h>
- #include <sys/clock.h>
- #include <sys/cmn_err.h>
-
- #define LED_WIDTH 8
- #define LED_BLANKS " "
-
- #ifndef LED_MAX_DEV
- #define LED_MAX_DEV 4
- #endif
-
- #ifndef LED_INIT_STR
- #define LED_INIT_STR "led 2.0"
- #endif
-
- #define DEBUGx
-
- /*----------------------------------------------------------------------
- * Everex 8042 UPI controller chip port defines
- */
- #define DATA 0x60
- #define COMMAND 0x64
- #define STATUS 0x64
-
- #define BUSY 0x02
-
- #define waitbusy() while (inb(STATUS)&BUSY)
-
- static char buf[LED_WIDTH]; /* the current display */
- static int dev_open; /* currently open devices */
-
- /* check priority of current process */
- /* if higher device numbers are currently open then go to sleep */
- static void chk_pri(int d)
- {
- int k = 1 << d;
-
- d = -1 << d;
-
- while ((dev_open & d) > k)
- {
- #ifdef DEBUG
- printf("\nled: priority - dev_open = %x, this = %d", dev_open, k);
- #endif
-
- /* woken up by ledclose */
- /* note that delays sleep on buf rather than &dev_open*/
- sleep((caddr_t)(&dev_open), PZERO + 2);
- }
- }
-
- static void end_timeout(void)
- {
- #ifdef DEBUG
- printf("\nled: Rise and shine");
- #endif
-
- wakeup((caddr_t)(buf));
- }
-
- #ifndef waitbusy
- static void waitbusy(void)
- {
- while (inb(STATUS) & BUSY)
- {
- timeout(end_timeout, NULL, 1);
- sleep((caddr_t)(buf), PZERO + 2);
- }
- }
- #endif
-
- /*----------------------------------------------------------------------
- * This routine outputs the current display in buf to the leds
- */
- static void out_buf(void)
- {
- int k;
-
- waitbusy();
- outb(COMMAND, 0xb0);
-
- for (k = 0; k < LED_WIDTH; k++)
- {
- waitbusy();
- outb(DATA, buf[k]);
- }
- }
-
- /*----------------------------------------------------------------------
- * This routine outputs a string. An arg of NULL outputs the signon
- * string. At most LED_WIDTH characters are output. If there are
- * less characters, the output is space padded.
- */
- static void out_str(const char *str, int d)
- {
- int k;
-
- chk_pri(d);
-
- if (!str)
- str = LED_INIT_STR;
-
- /* fill buffer with up to 8 characters padding with spaces if necessary */
- for (k = 0; k < LED_WIDTH; k++)
- if (*str)
- buf[k] = *str++;
- else
- buf[k] = ' ';
-
- out_buf();
- }
-
- /*----------------------------------------------------------------------
- * This routine outputs a character. If the argument is -1 then it
- * displays the signon message (defined in the make file.) The
- * output (other than the signon) scrolls across the leds.
- */
- static void out_char(int c, int d)
- {
- int k;
-
- timeout(end_timeout, (caddr_t)(0), (long)(HZ/10));
- sleep((caddr_t)(buf), PZERO + 2);
-
- chk_pri(d);
-
- for (k = 0; k < LED_WIDTH - 1; k++)
- buf[k] = buf[k + 1];
-
- buf[k] = c;
- out_buf();
- }
-
- /*----------------------------------------------------------------------
- * ledinit()
- *
- * This is called by the kernel early in the boot sequence, and we
- * simply display some more reasonable message here instead of the
- * last cylinder accessed by the BIOS. This can be whatever you like,
- * Define your message in the make file.
- *
- * Be creative!
- */
- void ledinit(void)
- {
- printf("LED Device driver\n");
- dev_open = 0; /* all devices closed */
- out_str(NULL, 0); /* display signon */
- }
-
- /*----------------------------------------------------------------------
- * ledwrite()
- *
- * Do the actual write to the display.
- */
- void ledwrite(dev_t dev)
- {
- int d, k;
- static int sp = 0; /* precede next with space flag */
-
- /*--------------------------------------------------------------
- * grab the characters from the user space and return a fault
- * error on a bad address. CRs and LFs cause the display to
- * pause for 1 second while FFs display the signon message.
- * the other control characters are printed. Try them to
- * see what they display.
- */
-
- if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
- u.u_error = ENXIO;
- else while (u.u_count)
- {
- if ((k = cpass()) == '\n' || k == '\r')
- {
- sp = 2;
-
- timeout(end_timeout, (caddr_t)(0), (long)(HZ));
- sleep((caddr_t)(buf), PZERO + 2);
- }
- else if (k == '\f')
- out_str(NULL, d);
- else if (k == '\t')
- out_char(' ', d);
- else
- {
- if (sp)
- out_char(' ', d);
-
- out_char(k, d);
- }
-
- if (sp)
- sp--;
- }
- }
-
- /* open clears the display */
- void ledopen(dev_t dev)
- {
- int d;
-
- #ifdef DEBUG
- printf("\nledopen: dev = %x", dev);
- #endif
-
- if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
- u.u_error = ENXIO;
- else if ((1 << d) & dev_open)
- u.u_error = EBUSY;
- else
- {
- #ifdef DEBUG
- printf("\nledopen: d = %x, 1 << d = %x", d, 1 << d);
- #endif
-
- dev_open |= (1 << d);
- out_str(LED_BLANKS, d);
- }
-
- #ifdef DEBUG
- printf("\nledopen: error = %d, d = %x, 1<<d = %x", u.u_error, d, 1 << d);
- #endif
- }
-
- void ledclose(dev_t dev)
- {
- int d;
-
- if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
- u.u_error = ENXIO;
- else
- dev_open &= ~(1 << d);
-
- #ifdef DEBUG
- printf("\nled: Close device %d", d);
- #endif
-
- wakeup((caddr_t)(&dev_open));
- }
-
- /*
- * ioctl function allows signon message to be displayed (function
- * 0) or any string to be display (1). In the case of function 1
- * the last argument points to the string.
- */
- int ledioctl(dev_t dev, int cmd, char *cmdarg)
- {
- int d;
-
- if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
- u.u_error = ENXIO;
- else if (cmd == 0)
- out_str(NULL, d);
- else if (cmd == 1)
- {
- int k;
-
- chk_pri(d);
-
- for (k = 0; k < LED_WIDTH; k++)
- if (*cmdarg)
- buf[k] = *cmdarg++;
- else
- buf[k] = ' ';
-
- out_buf();
- }
- else
- u.u_error = EINVAL;
- }
-