4.4BSD/usr/src/contrib/X11R5-hp300/mit/server/ddx/hpbsd/input/beeper.c

/*

Copyright (c) 1986, 1987 by Hewlett-Packard Company
Copyright (c) 1986, 1987 by the Massachusetts Institute of Technology

Permission to use, copy, modify, and distribute this
software and its documentation for any purpose and without
fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting
documentation, and that the name of M.I.T. not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

HEWLETT-PACKARD MAKES NO WARRANTY OF ANY KIND WITH REGARD
TO THIS SOFWARE, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE.  Hewlett-Packard shall not be liable for errors 
contained herein or direct, indirect, special, incidental or 
consequential damages in connection with the furnishing, 
performance, or use of this material.

This software is not subject to any license of the American
Telephone and Telegraph Company or of the Regents of the
University of California.

*/
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; File:         beeper.c
; SCCS:         %A% %G% %U%
; Description:  Access Gator/Bobcat beeper
; Author:       Andreas Paepcke, HPLabs/ATL
; Created:      2-Aug-85
; Modified:     Thu Oct 15 12:53:00 1987 (Don Bennett) bennett@hpldpb
; Language:     C
; Package:      PSL
; Status:       Experimental (Do Not Distribute)
;
; (c) Copyright 1985, Hewlett-Packard Company, all rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/

/* Public functions:

       beep

   We offer three voices and a noise source. Each sound is controllable
   in pitch, volume and duration. Pitch goes from 0 to 1023, volume goes
   from 0 to 15, duration is between 0 and 255 10msec intervalls. A
   duration of 0 turns the voice on continuously. A volume of 0 turns
   it off.
   The manufacturing specs give details
   on the programming interface. Here is a summary:

   The beeper is accessed through ioctl calls. The request argument is
   either "Send data to beeper" or "Read voice values from beeper". The
   argument is a pointer to a 4 byte buffer. These four bytes
   are defined here.

   R0-R3: Register address field. In the order R2, R1, R0:
     
     0 0 0: Voice 1 frequency
     0 0 1: Voice 1 attenuation
     0 1 0: Voice 2 frequency
     0 1 1: Voice 2 attenuation
     1 0 0: Voice 3 frequency
     1 0 1: Voice 3 attenuation
     1 1 0: Noise control
     1 1 1: Noise attentuation

  F0-F9: 10 bits pitch
  A0-A3: Attenuation
  D0-D7: Duration in 10msec's

  The placement of data in the buffer is a bit srewy:

  Byte 0 (Frequency 1):  1 R2 R1 R0 F3 F2 F1 F0     LSB
  Byte 1(Frequency 2):  0  0 F9 F8 F7 F6 F5 F4
  Byte 2 (Attenuator) :  1 R2 R1 R0 A3 A2 A1 A0
  Byte 3 (Duration)   : D7 D6 D5 D4 D3 D2 D1 D0

  The volume is inversely proportional to the attenuation. In order
  to provide rising numbers for rising loudness to the user, we
  expect a volume and modify to get the attenuation. The same goes
  for the pitch. In order to calculate frequency of the pitch,
  use: 

           83333/(1023-pitch)

  It is possible at any time to request the time any voice has
  left to run. This is done by:
 
  F4: Read voice1 timer
  F5: Read voice2 timer
  F6: Read voice3 timer
  F7: Read voice4 timer (noise)

  Noise is generated using a shift register. The following controls
  are possible for noise:
 
  - Attenuation
  - Duration
  - Periodic or white noise
  - 3 shift rates or output of voice 4 as shift rate

  Bytes 0 and 1 of the data buffer must both have identical contents
  to control the noise. Attenuation and duration are as in the other
  voices. Bytes 0 and 1 should look like this:

  1 R2 R1 R0 0 FB NF1 NF0   LSB

  R2, R1 and R0 must be 1, 1 and 0. If FB is 0, periodic noise
  is generated. If FB is 1, white noise is produced.

  NF1 and NF2 control the shift rate of the noise generator:

  NF1     NF2     Shift Rate
  --------------------------
  0       0       M/64
  0       1       M/128
  1       0       M/256
  1       1       Uses tone generator 3 output


  M is related to the clock rate.

  The voice start routines return 0 if all is well, -1 if we had
  trouble accessing the device file for the beeper and -2 if given
  parameters were out of range:
*/

#define NEED_EVENTS
#include <fcntl.h>

#include <sys/types.h>
#include "X.h"
#include "Xproto.h"
#include "input.h"
#include "inputstr.h"
#include "hildef.h"

#if defined(__apollo)
#include <apollo/base.h>
#endif /* __apollo */

#if defined(__hpux) || defined(__hp_osf) || defined(hp9000)

#if defined(hp9000)
#include <sys/ioctl.h>
#include <hilioctl.h>
#else
#if defined(__hp_osf)
#include <hp/hilioctl.h>
#else
#include <sys/hilioctl.h>
#endif /* __hp_osf */
#endif

#endif /* __hpux || __hp_osf */

/*********************************************************************
*DEFINES:                                                            *
*********************************************************************/

#define ALL_OK           0
#define ACCESS_PROBLEM  -1
#define BAD_RANGE       -2

#define VOICE1_FREQ_REG 0x80         /* Top nibbles for byte0 for all voices: */
#define VOICE2_FREQ_REG 0xA0
#define VOICE3_FREQ_REG 0xC0
#define NOISE_FREQ_REG  0xE0

#define VOICE1_VOL_REG  0x90         /* Top nibbles for byte2 for all voices: */
#define VOICE2_VOL_REG  0xB0
#define VOICE3_VOL_REG  0xD0
#define NOISE_VOL_REG   0xF0

#define MIN_VOICE       1             /* Legal ranges for parms from user: */
#define MAX_VOICE       3
#define MIN_PITCH       0
#define MAX_PITCH       1023
#define MIN_DURATION    0
#define MAX_DURATION    255
#define MIN_VOLUME      0
#define MAX_VOLUME      15
#define MIN_TYPE        0
#define MAX_TYPE        1
#define MIN_RATE        0
#define MAX_RATE        3

extern int beeper_fd;

/*********************************************************************
* PUBLIC FUNCTIONS:                                                  *
*********************************************************************/

/*****************************************************************************
*                                                                            *
* Beep using specified voice:                                                *
*                                                                            *
*   TAKES:                                                                   *
*                                                                            *
*   VOICE    : from 1 to 3                                                   *
*   PITCH    : from 0 to 1023 (incl)                                         *
*   VOLUME   : from 0 to 15   (incl). Zero turns voice off.                  *
*   DURATION : from 0 to 255  (incl). Zero turns voice on continuously.      *
*                                                                            *
*   RETURNS:                                                                 *
*                                                                            *
*   0        : All ok                                                        *
*   -1       : Cannot access beeper device file                              *
*   -2       : Parameter out of range                                        *
*                                                                            *
******************************************************************************/


beep(voice,pitch,volume,duration)
    int voice,pitch,volume,duration;
    {
    unsigned char buffer[4];
    char vol ;

#if defined(__apollo)
#endif /* __apollo */

    /* Check whether beeper device has already been opened: */

#if defined(__hpux) || defined(__hp_osf) || defined(hp9000)
    if (beeper_fd < 0)
	return(ACCESS_PROBLEM);

#if defined(__hp9000s300) || defined(__hp9000s700) || defined(__hp_osf) || defined(hp300)

    /* Check ranges of parameters: */
    if (
       (voice < MIN_VOICE)       ||
       (voice > MAX_VOICE)       ||
       (pitch < MIN_PITCH)       ||
       (pitch > MAX_PITCH)       ||
       (volume < MIN_VOLUME)     ||
       (volume > MAX_VOLUME)     ||
       (duration < MIN_DURATION) ||
       (duration > MAX_DURATION)
       )
	return(BAD_RANGE);

  /* Init the voice dependent data bytes. Note the inversion of user's
     volume and pitch specs to attenuation:
  */

    volume = MAX_VOLUME - volume;
    pitch  = MAX_PITCH  - pitch;
    switch (voice)
	{
	case 1: buffer[0] = VOICE1_FREQ_REG | (pitch & 0x0000000f);
            buffer[2] = VOICE1_VOL_REG  | (volume & 0x0000000f);
            break;
	case 2: buffer[0] = VOICE2_FREQ_REG | (pitch & 0x0000000f);
            buffer[2] = VOICE2_VOL_REG  | (volume & 0x0000000f);
            break;
	case 3: buffer[0] = VOICE3_FREQ_REG | (pitch & 0x0000000f);
            buffer[2] = VOICE3_VOL_REG  | (volume & 0x0000000f);
            break;
	}

    /* The high 6 bits of the pitch go into byte 1: */

    buffer[1] = 0x0000003f & (pitch >> 4);
    buffer[3] = duration; 			/* Duration: */

    if (ioctl(beeper_fd,EFTSBP,buffer) < 0)
	return(ACCESS_PROBLEM);
    return(ALL_OK);


#else		/* building for s800 */

   /*
    * map input range of 0 - 15 to driver range range of 0 - 255
    */

    if ( !volume ) 
	return;

    if ( voice == 2 )  /*  2 == CLICK_VOICE  which means key click */
	{
	/* doesn't appear there's any way to set the volume or duration 
         on 800 so I'll just pinch the volume for key clicks.  t.houser
	*/
	vol = (char) volume * (KBD_MAXVOLUME/45);
	}
    else                         /* beeper */
	{
	vol = (char) volume * (KBD_MAXVOLUME/15);
	}
    ioctl(beeper_fd, KBD_BEEP, &vol);
#endif /* __hp9000s300 */
#endif /* __hpux || __hp_osf */
 }


void
SetBellAttributes(pDevice, pCtrl)
register HPInputDevice *pDevice;
register KeybdCtrl *pCtrl;
{

#if defined(__apollo)

    /* The duration passed in in the pCtrl struct is a 16-bit value.
       The units is milliseconds, but the tone_$time function has
       units of 4 microseconds.
    */

    int *duration = (int *) pDevice->bell1;

    *duration = pCtrl->bell_duration * 250;	/* save in tone_$time units */
#endif /* __apollo */

#if defined(__hp9000s300) || defined(__hp9000s700) || defined(__hp_osf) || defined(hp300)
    register unsigned char duration;
    register int bellPitch;

    duration = (unsigned char) (pCtrl->bell_duration / 10);
    bellPitch = pCtrl->bell_pitch == 0 ? 1023 : (83333 / pCtrl->bell_pitch);
    if (bellPitch < 0)
	bellPitch = 0;
    if (bellPitch > 1023)
	bellPitch = 1023;
    pDevice->bell1[0] = VOICE1_FREQ_REG | (bellPitch & 0x0f);
    pDevice->bell2[0] = VOICE2_FREQ_REG | (bellPitch & 0x0f);
    pDevice->bell3[0] = VOICE3_FREQ_REG | (bellPitch & 0x0f);

    pDevice->bell1[1] = 0x03f & (bellPitch >> 4);
    pDevice->bell2[1] = 0x03f & (bellPitch >> 4);
    pDevice->bell3[1] = 0x03f & (bellPitch >> 4);


    if(duration) 
	{
	pDevice->bell1[3] = pDevice->bell2[3] = pDevice->bell3[3] = duration;
	}
    else 
	{
	pDevice->bell1[3] = pDevice->bell2[3] = pDevice->bell3[3] = 1;
	}
#endif /* building on __hp9000s300  or for s700 */
}


void
hpBell(volume, pDevice)
register int volume;
register DevicePtr pDevice;
{

    register int attenuation, loud;
    register HPInputDevice *pHPDev = (HPInputDevice *)
	 (((DeviceIntPtr)pDevice)->public.devicePrivate);

#if defined(__apollo)
    if (volume)
	{
	int	*duration = (int *) pHPDev->bell1;
	time_$clock_t beepTime;

	beepTime.high16 = 0;
	beepTime.low32 = *duration;

	tone_$time (&beepTime);
	}
#endif /* __apollo */

#if defined(__hpux) || defined(__hp_osf) || defined(hp9000)
    if (beeper_fd < 0) 
	return;

    /*
     * device independant code has already range checked the volume
     */
    if(volume) 
	{

#if defined(__hp9000s300) || defined(__hp9000s700) || defined(__hp_osf) || defined(hp300)
         /*
          * map input range of 0 - 100 to hardware attenuation range of 15 - 0
	  * we use a temp variable (loud) because of C's penchant for
	  * strange orders of evaluation ie to force the multiply before
	  * the divide
          */
	 loud = volume * MAX_VOLUME;
	 attenuation = (char) MAX_VOLUME - loud / 100;

         /*
          * we use all three voices to get a reasonably loud volume
          */
         pHPDev->bell1[2] = VOICE1_VOL_REG | attenuation;
         pHPDev->bell2[2] = VOICE2_VOL_REG | attenuation;
         pHPDev->bell3[2] = VOICE3_VOL_REG | attenuation;

         ioctl(beeper_fd, EFTSBP, pHPDev->bell1);
         ioctl(beeper_fd, EFTSBP, pHPDev->bell2);
         ioctl(beeper_fd, EFTSBP, pHPDev->bell3);
#else /* building for s800 */
         /*
          * map input range of 0 - 100 to driver range range of 0 - 255
          */
	 char vol;
	 vol = volume * KBD_MAXVOLUME / 100;

         ioctl(beeper_fd, KBD_BEEP, &vol);
#endif /* building on __hp9000s300 or for s700 */
	}
#endif /* __hpux || __hp_osf */

    return;
}