MFC: Bring /dev/speaker support to amd64.
This commit is contained in:
parent
b8108a4f96
commit
3b474f83c7
@ -56,6 +56,8 @@
|
||||
..
|
||||
smbus
|
||||
..
|
||||
speaker
|
||||
..
|
||||
usb
|
||||
..
|
||||
utopia
|
||||
|
@ -4,7 +4,7 @@
|
||||
PROG= morse
|
||||
MAN= morse.6
|
||||
|
||||
.if ${MACHINE_ARCH} == "i386"
|
||||
.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64"
|
||||
CFLAGS += -DSPEAKER=\"/dev/speaker\"
|
||||
.endif
|
||||
|
||||
|
@ -64,7 +64,7 @@ static const char rcsid[] =
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef SPEAKER
|
||||
#include <machine/speaker.h>
|
||||
#include <dev/speaker/speaker.h>
|
||||
#endif
|
||||
|
||||
struct morsetab {
|
||||
|
@ -38,7 +38,7 @@ LDIRS= bsm cam geom net net80211 netatalk netatm netgraph netinet netinet6 \
|
||||
LSUBDIRS= cam/scsi \
|
||||
dev/acpica dev/an dev/bktr dev/firewire dev/hwpmc \
|
||||
dev/ic dev/iicbus ${_dev_ieee488} dev/ofw \
|
||||
dev/pbio dev/ppbus dev/smbus dev/usb dev/wi dev/utopia \
|
||||
dev/pbio dev/ppbus dev/smbus dev/speaker dev/usb dev/wi dev/utopia \
|
||||
fs/devfs fs/fdescfs fs/fifofs fs/msdosfs fs/ntfs fs/nullfs \
|
||||
fs/nwfs fs/portalfs fs/procfs fs/smbfs fs/udf fs/umapfs \
|
||||
fs/unionfs \
|
||||
|
@ -300,6 +300,7 @@ MAN= aac.4 \
|
||||
snd_vibes.4 \
|
||||
snp.4 \
|
||||
spic.4 \
|
||||
${_spkr.4} \
|
||||
splash.4 \
|
||||
sppp.4 \
|
||||
ste.4 \
|
||||
@ -459,6 +460,7 @@ MLINKS+=sk.4 if_sk.4
|
||||
MLINKS+=sl.4 if_sl.4
|
||||
MLINKS+=smp.4 SMP.4
|
||||
MLINKS+=sn.4 if_sn.4
|
||||
MLINKS+=${_spkr.4} ${_speaker.4}
|
||||
MLINKS+=splash.4 screensaver.4
|
||||
MLINKS+=ste.4 if_ste.4
|
||||
MLINKS+=stf.4 if_stf.4
|
||||
@ -482,8 +484,10 @@ MLINKS+=xe.4 if_xe.4
|
||||
MLINKS+=xl.4 if_xl.4
|
||||
|
||||
.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
|
||||
_nve.4= nve.4
|
||||
_if_nve.4= if_nve.4
|
||||
_nve.4= nve.4
|
||||
_spkr.4= spkr.4
|
||||
_speaker.4= speaker.4
|
||||
.endif
|
||||
|
||||
.if exists(${.CURDIR}/man4.${MACHINE_ARCH})
|
||||
|
@ -44,7 +44,6 @@ MAN= acpi_asus.4 \
|
||||
scd.4 \
|
||||
smapi.4 \
|
||||
snc.4 \
|
||||
spkr.4 \
|
||||
sr.4 \
|
||||
streams.4 \
|
||||
svr4.4 \
|
||||
@ -58,7 +57,6 @@ MLINKS+=ndis.4 if_ndis.4
|
||||
MLINKS+=oltr.4 if_oltr.4
|
||||
MLINKS+=pae.4 PAE.4
|
||||
MLINKS+=sbni.4 if_sbni.4
|
||||
MLINKS+=spkr.4 speaker.4
|
||||
|
||||
MANSUBDIR=/i386
|
||||
|
||||
|
@ -1,249 +0,0 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd November 7, 1993
|
||||
.Dt SPKR 4 i386
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm speaker ,
|
||||
.Nm spkr
|
||||
.Nd console speaker device driver
|
||||
.Sh SYNOPSIS
|
||||
.Cd device speaker
|
||||
.In machine/speaker.h
|
||||
.Sh DESCRIPTION
|
||||
The speaker device driver allows applications to control the PC console
|
||||
speaker on an
|
||||
.Tn IBM-PC Ns --compatible
|
||||
machine running
|
||||
.Fx .
|
||||
.Pp
|
||||
Only one process may have this device open at any given time;
|
||||
.Xr open 2
|
||||
and
|
||||
.Xr close 2
|
||||
are used to lock and relinquish it.
|
||||
An attempt to open when
|
||||
another process has the device locked will return -1 with an
|
||||
.Er EBUSY
|
||||
error
|
||||
indication.
|
||||
Writes to the device are interpreted as `play strings' in a
|
||||
simple ASCII melody notation.
|
||||
An
|
||||
.Xr ioctl 2
|
||||
request
|
||||
for tone generation at arbitrary
|
||||
frequencies is also supported.
|
||||
.Pp
|
||||
Sound-generation does not monopolize the processor; in fact, the driver
|
||||
spends most of its time sleeping while the PC hardware is emitting
|
||||
tones.
|
||||
Other processes may emit beeps while the driver is running.
|
||||
.Pp
|
||||
Applications may call
|
||||
.Xr ioctl 2
|
||||
on a speaker file descriptor to control the
|
||||
speaker driver directly; definitions for the
|
||||
.Xr ioctl 2
|
||||
interface are in
|
||||
.In machine/speaker.h .
|
||||
The
|
||||
.Li tone_t
|
||||
structure used in these calls has two fields,
|
||||
specifying a frequency (in Hz) and a duration (in 1/100ths of a second).
|
||||
A frequency of zero is interpreted as a rest.
|
||||
.Pp
|
||||
At present there are two such
|
||||
.Xr ioctl 2
|
||||
calls.
|
||||
.Dv SPKRTONE
|
||||
accepts a pointer to a
|
||||
single tone structure as third argument and plays it.
|
||||
.Dv SPKRTUNE
|
||||
accepts a
|
||||
pointer to the first of an array of tone structures and plays them in
|
||||
continuous sequence; this array must be terminated by a final member with
|
||||
a zero duration.
|
||||
.Pp
|
||||
The play-string language is modeled on the PLAY statement conventions of
|
||||
.Tn IBM
|
||||
Advanced BASIC 2.0.
|
||||
The
|
||||
.Li MB ,
|
||||
.Li MF ,
|
||||
and
|
||||
.Li X
|
||||
primitives of PLAY are not
|
||||
useful in a timesharing environment and are omitted.
|
||||
The `octave-tracking'
|
||||
feature and the slur mark are new.
|
||||
.Pp
|
||||
There are 84 accessible notes numbered 1-84 in 7 octaves, each running from
|
||||
C to B, numbered 0-6; the scale is equal-tempered A440 and octave 3 starts
|
||||
with middle C.
|
||||
By default, the play function emits half-second notes with the
|
||||
last 1/16th second being `rest time'.
|
||||
.Pp
|
||||
Play strings are interpreted left to right as a series of play command groups;
|
||||
letter case is ignored.
|
||||
Play command groups are as follows:
|
||||
.Bl -tag -width CDEFGABxx
|
||||
.It Li CDEFGAB
|
||||
Letters A through G cause the corresponding note to be played in the
|
||||
current octave.
|
||||
A note letter may optionally be followed by an
|
||||
.Dq Em "accidental sign" ,
|
||||
one of # + or -; the first two of these cause it to be sharped one
|
||||
half-tone, the last causes it to be flatted one half-tone.
|
||||
It may
|
||||
also be followed by a time value number and by sustain dots (see
|
||||
below).
|
||||
Time values are interpreted as for the L command below.
|
||||
.It Ns Li O Sy n
|
||||
If
|
||||
.Sy n
|
||||
is numeric, this sets the current octave.
|
||||
.Sy n
|
||||
may also be one of
|
||||
.Li L
|
||||
or
|
||||
.Li N
|
||||
to enable or disable octave-tracking (it is disabled by default).
|
||||
When octave-tracking is on, interpretation of a pair of letter notes
|
||||
will change octaves if necessary in order to make the smallest
|
||||
possible jump between notes.
|
||||
Thus ``olbc'' will be played as
|
||||
``olb>c'', and ``olcb'' as ``olc<b''.
|
||||
Octave locking is disabled for
|
||||
one letter note following >, < and O[0123456].
|
||||
(The octave-locking
|
||||
feature is not supported in
|
||||
.Tn IBM
|
||||
BASIC.)
|
||||
.It Li >
|
||||
Bump the current octave up one.
|
||||
.It Li <
|
||||
Drop the current octave down one.
|
||||
.It Ns Li N Sy n
|
||||
Play note
|
||||
.Sy n ,
|
||||
.Sy n
|
||||
being 1 to 84 or 0 for a rest of current time value.
|
||||
May be followed by sustain dots.
|
||||
.It Ns Li L Sy n
|
||||
Sets the current time value for notes.
|
||||
The default is
|
||||
.Li L4 ,
|
||||
quarter or crotchet notes.
|
||||
The lowest possible value is 1; values up
|
||||
to 64 are accepted.
|
||||
.Li L1
|
||||
sets whole notes,
|
||||
.Li L2
|
||||
sets half notes,
|
||||
.Li L4
|
||||
sets quarter notes, etc.
|
||||
.It Ns Li P Sy n
|
||||
Pause (rest), with
|
||||
.Sy n
|
||||
interpreted as for
|
||||
.Li L Sy n .
|
||||
May be followed by
|
||||
sustain dots.
|
||||
May also be written
|
||||
.Li ~ .
|
||||
.It Ns Li T Sy n
|
||||
Sets the number of quarter notes per minute; default is 120.
|
||||
Musical
|
||||
names for common tempi are:
|
||||
.Bd -literal -offset indent
|
||||
Tempo Beats Per Minute
|
||||
very slow Larghissimo
|
||||
Largo 40-60
|
||||
Larghetto 60-66
|
||||
Grave
|
||||
Lento
|
||||
Adagio 66-76
|
||||
slow Adagietto
|
||||
Andante 76-108
|
||||
medium Andantino
|
||||
Moderato 108-120
|
||||
fast Allegretto
|
||||
Allegro 120-168
|
||||
Vivace
|
||||
Veloce
|
||||
Presto 168-208
|
||||
very fast Prestissimo
|
||||
.Ed
|
||||
.It Li M[LNS]
|
||||
Set articulation.
|
||||
.Li MN
|
||||
.Li ( N
|
||||
for normal) is the default; the last 1/8th of
|
||||
the note's value is rest time.
|
||||
You can set
|
||||
.Li ML
|
||||
for legato (no rest space) or
|
||||
.Li MS
|
||||
for staccato (1/4 rest space).
|
||||
.El
|
||||
.Pp
|
||||
Notes (that is,
|
||||
.Li CDEFGAB
|
||||
or
|
||||
.Li N
|
||||
command character groups) may be followed by
|
||||
sustain dots.
|
||||
Each dot causes the note's value to be lengthened by one-half
|
||||
for each one.
|
||||
Thus, a note dotted once is held for 3/2 of its undotted value;
|
||||
dotted twice, it is held 9/4, and three times would give 27/8.
|
||||
.Pp
|
||||
A note and its sustain dots may also be followed by a slur mark (underscore).
|
||||
This causes the normal micro-rest after the note to be filled in, slurring it
|
||||
to the next one.
|
||||
(The slur feature is not supported in
|
||||
.Tn IBM
|
||||
BASIC.)
|
||||
.Pp
|
||||
Whitespace in play strings is simply skipped and may be used to separate
|
||||
melody sections.
|
||||
.Sh FILES
|
||||
.Bl -tag -width /dev/speakerxx
|
||||
.It Pa /dev/speaker
|
||||
speaker device file
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr spkrtest 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
device appeared in
|
||||
.Fx 1.0 .
|
||||
.Sh AUTHORS
|
||||
.An Eric S. Raymond Aq esr@snark.thyrsus.com
|
||||
June 1990
|
||||
.Sh "PORTED BY"
|
||||
.An Andrew A. Chernov Aq ache@astral.msk.su
|
||||
.Sh BUGS
|
||||
Due to roundoff in the pitch tables and slop in the tone-generation and timer
|
||||
hardware (neither of which was designed for precision), neither pitch accuracy
|
||||
nor timings will be mathematically exact.
|
||||
There is no volume control.
|
||||
.Pp
|
||||
The action of two or more sustain dots does not reflect standard musical
|
||||
notation, in which each dot adds half the value of the previous dot
|
||||
modifier, not half the value of the note as modified.
|
||||
Thus, a note dotted
|
||||
once is held for 3/2 of its undotted value; dotted twice, it is held 7/4,
|
||||
and three times would give 15/8.
|
||||
The multiply-by-3/2 interpretation,
|
||||
however, is specified in the
|
||||
.Tn IBM
|
||||
BASIC manual and has been retained for
|
||||
compatibility.
|
||||
.Pp
|
||||
In play strings which are very long (longer than your system's physical I/O
|
||||
blocks) note suffixes or numbers may occasionally be parsed incorrectly due
|
||||
to crossing a block boundary.
|
@ -107,6 +107,9 @@ options CLK_USE_I8254_CALIBRATION
|
||||
#####################################################################
|
||||
# MISCELLANEOUS DEVICES AND OPTIONS
|
||||
|
||||
device speaker #Play IBM BASIC-style noises out your speaker
|
||||
hint.speaker.0.at="isa"
|
||||
hint.speaker.0.port="0x61"
|
||||
device gzip #Exec gzipped a.out's. REQUIRES COMPAT_AOUT!
|
||||
|
||||
|
||||
|
@ -173,6 +173,7 @@ dev/ppc/ppc.c optional ppc
|
||||
dev/ppc/ppc_puc.c optional ppc puc
|
||||
dev/sio/sio.c optional sio
|
||||
dev/sio/sio_isa.c optional sio isa
|
||||
dev/speaker/spkr.c optional speaker
|
||||
dev/syscons/apm/apm_saver.c optional apm_saver apm
|
||||
dev/syscons/schistory.c optional sc
|
||||
dev/syscons/scmouse.c optional sc
|
||||
|
@ -207,6 +207,7 @@ dev/sbni/if_sbni_isa.c optional sbni isa
|
||||
dev/sbni/if_sbni_pci.c optional sbni pci
|
||||
dev/sio/sio.c optional sio
|
||||
dev/sio/sio_isa.c optional sio isa
|
||||
dev/speaker/spkr.c optional speaker
|
||||
dev/sr/if_sr_isa.c optional sr isa
|
||||
dev/syscons/apm/apm_saver.c optional apm_saver apm
|
||||
dev/syscons/schistory.c optional sc
|
||||
@ -336,7 +337,6 @@ i386/isa/pcvt/pcvt_vtf.c optional vt
|
||||
i386/isa/pmtimer.c optional pmtimer
|
||||
i386/isa/prof_machdep.c optional profiling-routine
|
||||
i386/isa/spic.c optional spic
|
||||
i386/isa/spkr.c optional speaker
|
||||
i386/isa/vesa.c optional vga vesa
|
||||
i386/linux/imgact_linux.c optional compat_linux
|
||||
i386/linux/linux_dummy.c optional compat_linux
|
||||
|
@ -126,6 +126,7 @@ dev/snc/dp83932subr.c optional snc
|
||||
dev/snc/if_snc.c optional snc
|
||||
dev/snc/if_snc_cbus.c optional snc isa
|
||||
dev/snc/if_snc_pccard.c optional snc pccard
|
||||
dev/speaker/spkr.c optional speaker
|
||||
dev/syscons/apm/apm_saver.c optional apm_saver apm
|
||||
dev/syscons/schistory.c optional sc
|
||||
dev/syscons/scmouse.c optional sc
|
||||
@ -212,7 +213,6 @@ i386/isa/isa.c optional isa
|
||||
i386/isa/npx.c optional npx
|
||||
i386/isa/pmtimer.c optional pmtimer
|
||||
i386/isa/prof_machdep.c optional profiling-routine
|
||||
i386/isa/spkr.c optional speaker
|
||||
i386/linux/imgact_linux.c optional compat_linux
|
||||
i386/linux/linux_dummy.c optional compat_linux
|
||||
i386/linux/linux_locore.s optional compat_linux \
|
||||
|
@ -1,29 +1,16 @@
|
||||
/*
|
||||
* speaker.h -- interface definitions for speaker ioctl()
|
||||
*
|
||||
* v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
|
||||
* modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _MACHINE_SPEAKER_H_
|
||||
#define _MACHINE_SPEAKER_H_
|
||||
|
||||
#include <sys/ioccom.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define SPKRTONE _IOW('S', 1, tone_t) /* emit tone */
|
||||
#define SPKRTUNE _IO('S', 2) /* emit tone sequence*/
|
||||
#ifdef __CC_SUPPORTS_WARNING
|
||||
#warning "machine/speaker.h is deprecated. Include dev/speaker/speaker.h instead."
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int frequency; /* in hertz */
|
||||
int duration; /* in 1/100ths of a second */
|
||||
} tone_t;
|
||||
|
||||
/*
|
||||
* Strings written to the speaker device are interpreted as tunes and played;
|
||||
* see the spkr(4) man page for details.
|
||||
*/
|
||||
#include <dev/speaker/speaker.h>
|
||||
|
||||
#endif /* !_MACHINE_SPEAKER_H_ */
|
||||
|
@ -1,656 +0,0 @@
|
||||
/*-
|
||||
* spkr.c -- device driver for console speaker
|
||||
*
|
||||
* v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993
|
||||
* modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su>
|
||||
* modified for PC98 by Kakefuda
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/ctype.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <isa/isavar.h>
|
||||
#include <machine/clock.h>
|
||||
#include <machine/speaker.h>
|
||||
#include <machine/ppireg.h>
|
||||
#include <machine/timerreg.h>
|
||||
|
||||
static d_open_t spkropen;
|
||||
static d_close_t spkrclose;
|
||||
static d_write_t spkrwrite;
|
||||
static d_ioctl_t spkrioctl;
|
||||
|
||||
static struct cdevsw spkr_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_flags = D_NEEDGIANT,
|
||||
.d_open = spkropen,
|
||||
.d_close = spkrclose,
|
||||
.d_write = spkrwrite,
|
||||
.d_ioctl = spkrioctl,
|
||||
.d_name = "spkr",
|
||||
};
|
||||
|
||||
static MALLOC_DEFINE(M_SPKR, "spkr", "Speaker buffer");
|
||||
|
||||
/**************** MACHINE DEPENDENT PART STARTS HERE *************************
|
||||
*
|
||||
* This section defines a function tone() which causes a tone of given
|
||||
* frequency and duration from the ISA console speaker.
|
||||
* Another function endtone() is defined to force sound off, and there is
|
||||
* also a rest() entry point to do pauses.
|
||||
*
|
||||
* Audible sound is generated using the Programmable Interval Timer (PIT) and
|
||||
* Programmable Peripheral Interface (PPI) attached to the ISA speaker. The
|
||||
* PPI controls whether sound is passed through at all; the PIT's channel 2 is
|
||||
* used to generate clicks (a square wave) of whatever frequency is desired.
|
||||
*/
|
||||
|
||||
#ifdef PC98
|
||||
#define SPKR_DESC "PC98 speaker"
|
||||
#else
|
||||
#define SPKR_DESC "PC speaker"
|
||||
#endif
|
||||
|
||||
#define SPKRPRI PSOCK
|
||||
static char endtone, endrest;
|
||||
|
||||
static void tone(unsigned int thz, unsigned int ticks);
|
||||
static void rest(int ticks);
|
||||
static void playinit(void);
|
||||
static void playtone(int pitch, int value, int sustain);
|
||||
static void playstring(char *cp, size_t slen);
|
||||
|
||||
/* emit tone of frequency thz for given number of ticks */
|
||||
static void
|
||||
tone(thz, ticks)
|
||||
unsigned int thz, ticks;
|
||||
{
|
||||
unsigned int divisor;
|
||||
int sps;
|
||||
|
||||
if (thz <= 0)
|
||||
return;
|
||||
|
||||
divisor = timer_freq / thz;
|
||||
|
||||
#ifdef DEBUG
|
||||
(void) printf("tone: thz=%d ticks=%d\n", thz, ticks);
|
||||
#endif /* DEBUG */
|
||||
|
||||
/* set timer to generate clicks at given frequency in Hertz */
|
||||
sps = splclock();
|
||||
|
||||
if (timer_spkr_acquire()) {
|
||||
/* enter list of waiting procs ??? */
|
||||
splx(sps);
|
||||
return;
|
||||
}
|
||||
splx(sps);
|
||||
disable_intr();
|
||||
spkr_set_pitch(divisor);
|
||||
enable_intr();
|
||||
|
||||
/* turn the speaker on */
|
||||
ppi_spkr_on();
|
||||
|
||||
/*
|
||||
* Set timeout to endtone function, then give up the timeslice.
|
||||
* This is so other processes can execute while the tone is being
|
||||
* emitted.
|
||||
*/
|
||||
if (ticks > 0)
|
||||
tsleep(&endtone, SPKRPRI | PCATCH, "spkrtn", ticks);
|
||||
ppi_spkr_off();
|
||||
sps = splclock();
|
||||
timer_spkr_release();
|
||||
splx(sps);
|
||||
}
|
||||
|
||||
/* rest for given number of ticks */
|
||||
static void
|
||||
rest(ticks)
|
||||
int ticks;
|
||||
{
|
||||
/*
|
||||
* Set timeout to endrest function, then give up the timeslice.
|
||||
* This is so other processes can execute while the rest is being
|
||||
* waited out.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
(void) printf("rest: %d\n", ticks);
|
||||
#endif /* DEBUG */
|
||||
if (ticks > 0)
|
||||
tsleep(&endrest, SPKRPRI | PCATCH, "spkrrs", ticks);
|
||||
}
|
||||
|
||||
/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
|
||||
*
|
||||
* Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
|
||||
* M[LNS] are missing; the ~ synonym and the _ slur mark and the octave-
|
||||
* tracking facility are added.
|
||||
* Requires tone(), rest(), and endtone(). String play is not interruptible
|
||||
* except possibly at physical block boundaries.
|
||||
*/
|
||||
|
||||
typedef int bool;
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define dtoi(c) ((c) - '0')
|
||||
|
||||
static int octave; /* currently selected octave */
|
||||
static int whole; /* whole-note time at current tempo, in ticks */
|
||||
static int value; /* whole divisor for note time, quarter note = 1 */
|
||||
static int fill; /* controls spacing of notes */
|
||||
static bool octtrack; /* octave-tracking on? */
|
||||
static bool octprefix; /* override current octave-tracking state? */
|
||||
|
||||
/*
|
||||
* Magic number avoidance...
|
||||
*/
|
||||
#define SECS_PER_MIN 60 /* seconds per minute */
|
||||
#define WHOLE_NOTE 4 /* quarter notes per whole note */
|
||||
#define MIN_VALUE 64 /* the most we can divide a note by */
|
||||
#define DFLT_VALUE 4 /* default value (quarter-note) */
|
||||
#define FILLTIME 8 /* for articulation, break note in parts */
|
||||
#define STACCATO 6 /* 6/8 = 3/4 of note is filled */
|
||||
#define NORMAL 7 /* 7/8ths of note interval is filled */
|
||||
#define LEGATO 8 /* all of note interval is filled */
|
||||
#define DFLT_OCTAVE 4 /* default octave */
|
||||
#define MIN_TEMPO 32 /* minimum tempo */
|
||||
#define DFLT_TEMPO 120 /* default tempo */
|
||||
#define MAX_TEMPO 255 /* max tempo */
|
||||
#define NUM_MULT 3 /* numerator of dot multiplier */
|
||||
#define DENOM_MULT 2 /* denominator of dot multiplier */
|
||||
|
||||
/* letter to half-tone: A B C D E F G */
|
||||
static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
|
||||
|
||||
/*
|
||||
* This is the American Standard A440 Equal-Tempered scale with frequencies
|
||||
* rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
|
||||
* our octave 0 is standard octave 2.
|
||||
*/
|
||||
#define OCTAVE_NOTES 12 /* semitones per octave */
|
||||
static int pitchtab[] =
|
||||
{
|
||||
/* C C# D D# E F F# G G# A A# B*/
|
||||
/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
|
||||
/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
|
||||
/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
|
||||
/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
|
||||
/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
|
||||
/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
|
||||
/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
|
||||
};
|
||||
|
||||
static void
|
||||
playinit()
|
||||
{
|
||||
octave = DFLT_OCTAVE;
|
||||
whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
|
||||
fill = NORMAL;
|
||||
value = DFLT_VALUE;
|
||||
octtrack = FALSE;
|
||||
octprefix = TRUE; /* act as though there was an initial O(n) */
|
||||
}
|
||||
|
||||
/* play tone of proper duration for current rhythm signature */
|
||||
static void
|
||||
playtone(pitch, value, sustain)
|
||||
int pitch, value, sustain;
|
||||
{
|
||||
register int sound, silence, snum = 1, sdenom = 1;
|
||||
|
||||
/* this weirdness avoids floating-point arithmetic */
|
||||
for (; sustain; sustain--)
|
||||
{
|
||||
/* See the BUGS section in the man page for discussion */
|
||||
snum *= NUM_MULT;
|
||||
sdenom *= DENOM_MULT;
|
||||
}
|
||||
|
||||
if (value == 0 || sdenom == 0)
|
||||
return;
|
||||
|
||||
if (pitch == -1)
|
||||
rest(whole * snum / (value * sdenom));
|
||||
else
|
||||
{
|
||||
sound = (whole * snum) / (value * sdenom)
|
||||
- (whole * (FILLTIME - fill)) / (value * FILLTIME);
|
||||
silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
|
||||
|
||||
#ifdef DEBUG
|
||||
(void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
|
||||
pitch, sound, silence);
|
||||
#endif /* DEBUG */
|
||||
|
||||
tone(pitchtab[pitch], sound);
|
||||
if (fill != LEGATO)
|
||||
rest(silence);
|
||||
}
|
||||
}
|
||||
|
||||
/* interpret and play an item from a notation string */
|
||||
static void
|
||||
playstring(cp, slen)
|
||||
char *cp;
|
||||
size_t slen;
|
||||
{
|
||||
int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
|
||||
|
||||
#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
|
||||
{v = v * 10 + (*++cp - '0'); slen--;}
|
||||
for (; slen--; cp++)
|
||||
{
|
||||
int sustain, timeval, tempo;
|
||||
register char c = toupper(*cp);
|
||||
|
||||
#ifdef DEBUG
|
||||
(void) printf("playstring: %c (%x)\n", c, c);
|
||||
#endif /* DEBUG */
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
|
||||
|
||||
/* compute pitch */
|
||||
pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
|
||||
|
||||
/* this may be followed by an accidental sign */
|
||||
if (cp[1] == '#' || cp[1] == '+')
|
||||
{
|
||||
++pitch;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
else if (cp[1] == '-')
|
||||
{
|
||||
--pitch;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
|
||||
/*
|
||||
* If octave-tracking mode is on, and there has been no octave-
|
||||
* setting prefix, find the version of the current letter note
|
||||
* closest to the last regardless of octave.
|
||||
*/
|
||||
if (octtrack && !octprefix)
|
||||
{
|
||||
if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
|
||||
{
|
||||
++octave;
|
||||
pitch += OCTAVE_NOTES;
|
||||
}
|
||||
|
||||
if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
|
||||
{
|
||||
--octave;
|
||||
pitch -= OCTAVE_NOTES;
|
||||
}
|
||||
}
|
||||
octprefix = FALSE;
|
||||
lastpitch = pitch;
|
||||
|
||||
/* ...which may in turn be followed by an override time value */
|
||||
GETNUM(cp, timeval);
|
||||
if (timeval <= 0 || timeval > MIN_VALUE)
|
||||
timeval = value;
|
||||
|
||||
/* ...and/or sustain dots */
|
||||
for (sustain = 0; cp[1] == '.'; cp++)
|
||||
{
|
||||
slen--;
|
||||
sustain++;
|
||||
}
|
||||
|
||||
/* ...and/or a slur mark */
|
||||
oldfill = fill;
|
||||
if (cp[1] == '_')
|
||||
{
|
||||
fill = LEGATO;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
|
||||
/* time to emit the actual tone */
|
||||
playtone(pitch, timeval, sustain);
|
||||
|
||||
fill = oldfill;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if (cp[1] == 'N' || cp[1] == 'n')
|
||||
{
|
||||
octprefix = octtrack = FALSE;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
else if (cp[1] == 'L' || cp[1] == 'l')
|
||||
{
|
||||
octtrack = TRUE;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
else
|
||||
{
|
||||
GETNUM(cp, octave);
|
||||
if (octave >= sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES)
|
||||
octave = DFLT_OCTAVE;
|
||||
octprefix = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case '>':
|
||||
if (octave < sizeof(pitchtab) / sizeof(pitchtab[0]) / OCTAVE_NOTES - 1)
|
||||
octave++;
|
||||
octprefix = TRUE;
|
||||
break;
|
||||
|
||||
case '<':
|
||||
if (octave > 0)
|
||||
octave--;
|
||||
octprefix = TRUE;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
GETNUM(cp, pitch);
|
||||
for (sustain = 0; cp[1] == '.'; cp++)
|
||||
{
|
||||
slen--;
|
||||
sustain++;
|
||||
}
|
||||
oldfill = fill;
|
||||
if (cp[1] == '_')
|
||||
{
|
||||
fill = LEGATO;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
playtone(pitch - 1, value, sustain);
|
||||
fill = oldfill;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
GETNUM(cp, value);
|
||||
if (value <= 0 || value > MIN_VALUE)
|
||||
value = DFLT_VALUE;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
case '~':
|
||||
/* this may be followed by an override time value */
|
||||
GETNUM(cp, timeval);
|
||||
if (timeval <= 0 || timeval > MIN_VALUE)
|
||||
timeval = value;
|
||||
for (sustain = 0; cp[1] == '.'; cp++)
|
||||
{
|
||||
slen--;
|
||||
sustain++;
|
||||
}
|
||||
playtone(-1, timeval, sustain);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
GETNUM(cp, tempo);
|
||||
if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
|
||||
tempo = DFLT_TEMPO;
|
||||
whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (cp[1] == 'N' || cp[1] == 'n')
|
||||
{
|
||||
fill = NORMAL;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
else if (cp[1] == 'L' || cp[1] == 'l')
|
||||
{
|
||||
fill = LEGATO;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
else if (cp[1] == 'S' || cp[1] == 's')
|
||||
{
|
||||
fill = STACCATO;
|
||||
++cp;
|
||||
slen--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
|
||||
*
|
||||
* This section implements driver hooks to run playstring() and the tone(),
|
||||
* endtone(), and rest() functions defined above.
|
||||
*/
|
||||
|
||||
static int spkr_active = FALSE; /* exclusion flag */
|
||||
static char *spkr_inbuf; /* incoming buf */
|
||||
|
||||
static int
|
||||
spkropen(dev, flags, fmt, td)
|
||||
struct cdev *dev;
|
||||
int flags;
|
||||
int fmt;
|
||||
struct thread *td;
|
||||
{
|
||||
#ifdef DEBUG
|
||||
(void) printf("spkropen: entering with dev = %s\n", devtoname(dev));
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (minor(dev) != 0)
|
||||
return(ENXIO);
|
||||
else if (spkr_active)
|
||||
return(EBUSY);
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
(void) printf("spkropen: about to perform play initialization\n");
|
||||
#endif /* DEBUG */
|
||||
playinit();
|
||||
spkr_inbuf = malloc(DEV_BSIZE, M_SPKR, M_WAITOK);
|
||||
spkr_active = TRUE;
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
spkrwrite(dev, uio, ioflag)
|
||||
struct cdev *dev;
|
||||
struct uio *uio;
|
||||
int ioflag;
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("spkrwrite: entering with dev = %s, count = %d\n",
|
||||
devtoname(dev), uio->uio_resid);
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (minor(dev) != 0)
|
||||
return(ENXIO);
|
||||
else if (uio->uio_resid > (DEV_BSIZE - 1)) /* prevent system crashes */
|
||||
return(E2BIG);
|
||||
else
|
||||
{
|
||||
unsigned n;
|
||||
char *cp;
|
||||
int error;
|
||||
|
||||
n = uio->uio_resid;
|
||||
cp = spkr_inbuf;
|
||||
error = uiomove(cp, n, uio);
|
||||
if (!error) {
|
||||
cp[n] = '\0';
|
||||
playstring(cp, n);
|
||||
}
|
||||
return(error);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
spkrclose(dev, flags, fmt, td)
|
||||
struct cdev *dev;
|
||||
int flags;
|
||||
int fmt;
|
||||
struct thread *td;
|
||||
{
|
||||
#ifdef DEBUG
|
||||
(void) printf("spkrclose: entering with dev = %s\n", devtoname(dev));
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (minor(dev) != 0)
|
||||
return(ENXIO);
|
||||
else
|
||||
{
|
||||
wakeup(&endtone);
|
||||
wakeup(&endrest);
|
||||
free(spkr_inbuf, M_SPKR);
|
||||
spkr_active = FALSE;
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
spkrioctl(dev, cmd, cmdarg, flags, td)
|
||||
struct cdev *dev;
|
||||
unsigned long cmd;
|
||||
caddr_t cmdarg;
|
||||
int flags;
|
||||
struct thread *td;
|
||||
{
|
||||
#ifdef DEBUG
|
||||
(void) printf("spkrioctl: entering with dev = %s, cmd = %lx\n",
|
||||
devtoname(dev), cmd);
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (minor(dev) != 0)
|
||||
return(ENXIO);
|
||||
else if (cmd == SPKRTONE)
|
||||
{
|
||||
tone_t *tp = (tone_t *)cmdarg;
|
||||
|
||||
if (tp->frequency == 0)
|
||||
rest(tp->duration);
|
||||
else
|
||||
tone(tp->frequency, tp->duration);
|
||||
return 0;
|
||||
}
|
||||
else if (cmd == SPKRTUNE)
|
||||
{
|
||||
tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
|
||||
tone_t ttp;
|
||||
int error;
|
||||
|
||||
for (; ; tp++) {
|
||||
error = copyin(tp, &ttp, sizeof(tone_t));
|
||||
if (error)
|
||||
return(error);
|
||||
if (ttp.duration == 0)
|
||||
break;
|
||||
if (ttp.frequency == 0)
|
||||
rest(ttp.duration);
|
||||
else
|
||||
tone(ttp.frequency, ttp.duration);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
return(EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Install placeholder to claim the resources owned by the
|
||||
* AT tone generator.
|
||||
*/
|
||||
static struct isa_pnp_id speaker_ids[] = {
|
||||
#ifndef PC98
|
||||
{ 0x0008d041 /* PNP0800 */, SPKR_DESC },
|
||||
#endif
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct cdev *speaker_dev;
|
||||
|
||||
static int
|
||||
speaker_probe(device_t dev)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = ISA_PNP_PROBE(device_get_parent(dev), dev, speaker_ids);
|
||||
|
||||
/* PnP match */
|
||||
if (error == 0)
|
||||
return (0);
|
||||
|
||||
/* No match */
|
||||
if (error == ENXIO)
|
||||
return (ENXIO);
|
||||
|
||||
/* Not configured by hints. */
|
||||
if (strncmp(device_get_name(dev), "speaker", 9))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, SPKR_DESC);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
speaker_attach(device_t dev)
|
||||
{
|
||||
|
||||
if (speaker_dev) {
|
||||
device_printf(dev, "Already attached!\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
speaker_dev = make_dev(&spkr_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
|
||||
"speaker");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
speaker_detach(device_t dev)
|
||||
{
|
||||
destroy_dev(speaker_dev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t speaker_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, speaker_probe),
|
||||
DEVMETHOD(device_attach, speaker_attach),
|
||||
DEVMETHOD(device_detach, speaker_detach),
|
||||
DEVMETHOD(device_shutdown, bus_generic_shutdown),
|
||||
DEVMETHOD(device_suspend, bus_generic_suspend),
|
||||
DEVMETHOD(device_resume, bus_generic_resume),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static driver_t speaker_driver = {
|
||||
"speaker",
|
||||
speaker_methods,
|
||||
1, /* no softc */
|
||||
};
|
||||
|
||||
static devclass_t speaker_devclass;
|
||||
|
||||
DRIVER_MODULE(speaker, isa, speaker_driver, speaker_devclass, 0, 0);
|
||||
#ifndef PC98
|
||||
DRIVER_MODULE(speaker, acpi, speaker_driver, speaker_devclass, 0, 0);
|
||||
#endif
|
||||
|
||||
/* spkr.c ends here */
|
@ -450,6 +450,7 @@ _safe= safe
|
||||
_scsi_low= scsi_low
|
||||
_smbfs= smbfs
|
||||
_sound= sound
|
||||
_speaker= speaker
|
||||
_sppp= sppp
|
||||
_twa= twa
|
||||
.endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../i386/isa
|
||||
.PATH: ${.CURDIR}/../../dev/speaker
|
||||
|
||||
KMOD= speaker
|
||||
SRCS= spkr.c
|
||||
|
Loading…
Reference in New Issue
Block a user