home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / everex-led / part01 / led.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-20  |  7.7 KB  |  333 lines

  1. /*
  2.  * led.c
  3.  *
  4.  * Written by:    Stephen J. Friedl
  5.  *        V-Systems, Inc.
  6.  *        friedl@mtndew.Tustin.CA.US
  7.  *        29 July, 1989
  8.  *
  9.  *  Re-written by D'Arcy J.M. Cain
  10.  *  given version number 2.0
  11.  *
  12.  *    This is a driver for the front-panel LEDs on an Everex
  13.  *    STEP system.  This is an eight-character display, and is
  14.  *    normally driven by the BIOS to show the current drive,
  15.  *    cylinder, and head being accessed.  UNIX bypasses the BIOS
  16.  *    entirely, so it freezes shortly after bootup.
  17.  *
  18.  *    You can make the thing display some cute signon message when
  19.  *    the machine boots by setting the string LED_INIT_STR in the make
  20.  *  file.  A normal write call will scroll the output across the LEDs
  21.  *  (referred to by a friend as "times squaring" the output.)  There
  22.  *  are two ioctl calls available.  Function 0 re-displays the signon
  23.  *  message and function 1 displays the message pointed to by the third
  24.  *  argument to ioctl.  Both of these functions display all at once as
  25.  *  opposed to the scroll effect of the normal write call.
  26.  *
  27.  *  There are multiple devices available to the user.  The way that this
  28.  *  works is that any of the devices may be opened by any process but
  29.  *  each is opened exclusively and each device blocks while devices
  30.  *  with a higher minor number are open.  The idea is to put something
  31.  *  like ledtime on the lowest device and letting other things interupt
  32.  *  it temporarily.
  33.  *
  34.  *  This driver has been tested on an Everex STEP/25 running
  35.  *    Esix System V Release 4.0.4 beta.  Information about the
  36.  *    usage of the 8042 UPI has been derived from the Everex
  37.  *    STEP 386 Hardware Service and Maintenance Guide, part
  38.  *    number KIT 00076-00 (about a hundred bucks from your
  39.  *    Authorized Everex dealer).
  40.  *
  41.  *    ========================================================
  42.  *    In the spirit of free interchange of { hopefully } high-
  43.  *    quality software, this driver is released to the public
  44.  *    domain and can be used for any purposes at all, including
  45.  *    personal, commercial, military, blackmail, etc.
  46.  *
  47.  *    Have fun, folks.
  48.  *    ========================================================
  49.  *  Additional from D'Arcy:  I am including my changes in the
  50.  *  above release.
  51.  */
  52.  
  53. #define        _KERNEL        1
  54. #include    <limits.h>
  55. #include    <sys/types.h>
  56. #include    <sys/param.h>
  57. #include    <sys/user.h>
  58. #include    <sys/systm.h>
  59. #include    <sys/sysmacros.h>
  60. #include    <sys/inline.h>
  61. #include    <sys/errno.h>
  62. #include    <sys/signal.h>
  63. #include    <sys/dir.h>
  64. #include    <sys/clock.h>
  65. #include    <sys/cmn_err.h>
  66.  
  67. #define        LED_WIDTH    8
  68. #define        LED_BLANKS    "        "
  69.  
  70. #ifndef        LED_MAX_DEV
  71. #define        LED_MAX_DEV        4
  72. #endif
  73.  
  74. #ifndef        LED_INIT_STR
  75. #define        LED_INIT_STR    "led 2.0"
  76. #endif
  77.  
  78. #define        DEBUGx
  79.  
  80. /*----------------------------------------------------------------------
  81.  * Everex 8042 UPI controller chip port defines
  82.  */
  83. #define     DATA        0x60
  84. #define     COMMAND        0x64
  85. #define     STATUS        0x64
  86.  
  87. #define     BUSY        0x02
  88.  
  89. #define        waitbusy()    while (inb(STATUS)&BUSY)
  90.  
  91. static char    buf[LED_WIDTH];        /* the current display */
  92. static int    dev_open;            /* currently open devices */
  93.  
  94. /* check priority of current process */
  95. /* if higher device numbers are currently open then go to sleep */
  96. static void    chk_pri(int d)
  97. {
  98.     int        k = 1 << d;
  99.  
  100.     d = -1 << d;
  101.  
  102.     while ((dev_open & d) > k)
  103.     {
  104. #ifdef    DEBUG
  105.         printf("\nled: priority - dev_open = %x, this = %d", dev_open, k);
  106. #endif
  107.  
  108.         /* woken up by ledclose */
  109.         /* note that delays sleep on buf rather than &dev_open*/
  110.         sleep((caddr_t)(&dev_open), PZERO + 2);
  111.     }
  112. }
  113.  
  114. static void    end_timeout(void)
  115. {
  116. #ifdef    DEBUG
  117.     printf("\nled: Rise and shine");
  118. #endif
  119.  
  120.     wakeup((caddr_t)(buf));
  121. }
  122.  
  123. #ifndef        waitbusy
  124. static void    waitbusy(void)
  125. {
  126.     while (inb(STATUS) & BUSY)
  127.     {
  128.         timeout(end_timeout, NULL, 1);
  129.         sleep((caddr_t)(buf), PZERO + 2);
  130.     }
  131. }
  132. #endif
  133.  
  134. /*----------------------------------------------------------------------
  135.  * This routine outputs the current display in buf to the leds
  136.  */
  137. static void    out_buf(void)
  138. {
  139.     int        k;
  140.  
  141.     waitbusy();
  142.     outb(COMMAND, 0xb0);
  143.  
  144.     for (k = 0; k < LED_WIDTH; k++)
  145.     {
  146.         waitbusy();
  147.         outb(DATA, buf[k]);
  148.     }
  149. }
  150.  
  151. /*----------------------------------------------------------------------
  152.  * This routine outputs a string.  An arg of NULL outputs the signon
  153.  * string.  At most LED_WIDTH characters are output.  If there are
  154.  * less characters, the output is space padded.
  155.  */
  156. static void    out_str(const char *str, int d)
  157. {
  158.     int        k;
  159.  
  160.     chk_pri(d);
  161.  
  162.     if (!str)
  163.         str = LED_INIT_STR;
  164.  
  165.     /* fill buffer with up to 8 characters padding with spaces if necessary */
  166.     for (k = 0; k < LED_WIDTH; k++)
  167.         if (*str)
  168.             buf[k] = *str++;
  169.         else
  170.             buf[k] = ' ';
  171.  
  172.     out_buf();
  173. }
  174.  
  175. /*----------------------------------------------------------------------
  176.  * This routine outputs a character.  If the argument is -1 then it
  177.  * displays the signon message (defined in the make file.)  The
  178.  * output (other than the signon) scrolls across the leds.
  179.  */
  180. static void    out_char(int c, int d)
  181. {
  182.     int        k;
  183.  
  184.     timeout(end_timeout, (caddr_t)(0), (long)(HZ/10));
  185.     sleep((caddr_t)(buf), PZERO + 2);
  186.     
  187.     chk_pri(d);
  188.     
  189.     for (k = 0; k < LED_WIDTH - 1; k++)
  190.         buf[k] = buf[k + 1];
  191.  
  192.     buf[k] = c;
  193.     out_buf();
  194. }
  195.  
  196. /*----------------------------------------------------------------------
  197.  * ledinit()
  198.  *
  199.  *    This is called by the kernel early in the boot sequence, and we
  200.  *    simply display some more reasonable message here instead of the
  201.  *    last cylinder accessed by the BIOS.  This can be whatever you like,
  202.  *    Define your message in the make file.
  203.  *
  204.  *    Be creative!
  205.  */
  206. void ledinit(void)
  207. {
  208.     printf("LED Device driver\n");
  209.     dev_open = 0;        /* all devices closed */
  210.     out_str(NULL, 0);    /* display signon */
  211. }
  212.  
  213. /*----------------------------------------------------------------------
  214.  * ledwrite()
  215.  *
  216.  *    Do the actual write to the display.
  217.  */
  218. void ledwrite(dev_t dev)
  219. {
  220.     int            d, k;
  221.     static int    sp = 0;        /* precede next with space flag */
  222.  
  223.     /*--------------------------------------------------------------
  224.      * grab the characters from the user space and return a fault
  225.      * error on a bad address.  CRs and LFs cause the display to
  226.      * pause for 1 second while FFs display the signon message.
  227.      * the other control characters are printed.  Try them to
  228.      * see what they display.
  229.      */
  230.  
  231.     if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
  232.         u.u_error = ENXIO;
  233.     else while (u.u_count)
  234.     {
  235.         if ((k = cpass()) == '\n' || k == '\r')
  236.         {
  237.             sp = 2;
  238.  
  239.             timeout(end_timeout, (caddr_t)(0), (long)(HZ));
  240.             sleep((caddr_t)(buf), PZERO + 2);
  241.         }
  242.         else if (k == '\f')
  243.             out_str(NULL, d);
  244.         else if (k == '\t')
  245.             out_char(' ', d);
  246.         else
  247.         {
  248.             if (sp)
  249.                 out_char(' ', d);
  250.  
  251.             out_char(k, d);
  252.         }
  253.  
  254.         if (sp)
  255.             sp--;
  256.     }
  257. }
  258.  
  259. /* open clears the display */
  260. void    ledopen(dev_t dev)
  261. {
  262.     int        d;
  263.  
  264. #ifdef    DEBUG
  265.     printf("\nledopen: dev = %x", dev);
  266. #endif
  267.  
  268.     if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
  269.         u.u_error = ENXIO;
  270.     else if ((1 << d) & dev_open)
  271.         u.u_error = EBUSY;
  272.     else
  273.     {
  274. #ifdef    DEBUG
  275.         printf("\nledopen: d = %x, 1 << d = %x", d, 1 << d);
  276. #endif
  277.  
  278.         dev_open |= (1 << d);
  279.         out_str(LED_BLANKS, d);
  280.     }
  281.  
  282. #ifdef    DEBUG
  283.     printf("\nledopen: error = %d, d = %x, 1<<d = %x", u.u_error, d, 1 << d);
  284. #endif
  285. }
  286.  
  287. void    ledclose(dev_t dev)
  288. {
  289.     int        d;
  290.  
  291.     if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
  292.         u.u_error = ENXIO;
  293.     else
  294.         dev_open &= ~(1 << d);
  295.  
  296. #ifdef    DEBUG
  297.     printf("\nled: Close device %d", d);
  298. #endif
  299.  
  300.     wakeup((caddr_t)(&dev_open));
  301. }
  302.  
  303. /*
  304.  * ioctl function allows signon message to be displayed (function
  305.  * 0) or any string to be display (1).  In the case of function 1
  306.  * the last argument points to the string.
  307. */
  308. int ledioctl(dev_t dev, int cmd, char *cmdarg)
  309. {
  310.     int        d;
  311.  
  312.     if ((d = minor(dev)) < 0 || d >= LED_MAX_DEV)
  313.         u.u_error = ENXIO;
  314.     else if (cmd == 0)
  315.         out_str(NULL, d);
  316.     else if (cmd == 1)
  317.     {
  318.         int        k;
  319.  
  320.         chk_pri(d);
  321.  
  322.         for (k = 0; k < LED_WIDTH; k++)
  323.             if (*cmdarg)
  324.                 buf[k] = *cmdarg++;
  325.             else
  326.                 buf[k] = ' ';
  327.  
  328.         out_buf();
  329.     }
  330.     else
  331.         u.u_error = EINVAL;
  332. }
  333.