mdg@uport.UUCP (10/23/87)
This is a loudspeaker device driver for Microport System V/AT UNIX. The device has an interface that lets you play simple tunes by writing character strings to the device. There are several files concatenated, including documentation on installation. I would appreciate any and all comments, questions, etc. that you might have, especially mods for the driver or "song files" you may write. Thanks and enjoy! Marc de Groot (KG6KF) | UUCP: {hplabs, sun, ucbvax}!amdcad!uport!mdg Microport Systems, Inc. | AMATEUR PACKET RADIO: KG6KF @ N6IYA 10 Victor Square | DISCLAIMER: "..full of sound and fury, not Scotts Valley, CA 95066 | necessarily agreeing with anyone.." 408 438 8649 Ext. 31 | MOTTO: "The future is almost over." ------------------------------CUT HERE--------------------------------------- spkr.doc follows ------------------------------CUT HERE--------------------------------------- Directions for installing the loudspeaker driver under Microport System V/AT - Make sure you have installed the link kit. If the directory /usr/linkkit doesn't exist, you haven't installed it. Put the link kit disk in the floppy drive and type 'installit' <Enter>. - Put the files spkr.c and spkr.h in the directory /usr/linkkit/io . Then type: cd /usr/linkkit/io cc -c -Ml spkr.c ar r ../lib2 spkr.o - Edit the file /usr/linkkit/cf/master . Find the following piece of text: *sxt 0 ocrwi co sxt 0 6 32 lp 37,39 ocwis c lp 0 7 3 *prf 0 rwi co prf 0 10 1 cm0 0 rw co cm0 0 8 1 Add the following line after the above: spkr 0 ocrwi c sp 0 12 1 - Edit the file /usr/linkkit/cf/dfile.wini . Find the following piece of text: *sxt 0 8 lp 0 3 *prf 0 1 cm0 0 1 Add the following line after the above: spkr 0 1 - The following steps should be performed as super-user. - To create the device, type: mknod /dev/spkr c 12 0 chmod 666 /dev/spkr - Now, to link the kernel, type: cd /usr/linkkit/cf make wini - To copy the new kernel to the root directory, type: cp ../system5 / You may want to back up the old kernel first, by typing: cp /system5 /system.old - At this point, bring down the system and reboot in the usual way. If all has gone well, the system will boot and run with the new device driver incorporated into the UNIX kernel. You may now write to this device like any other. The loudspeaker may be gotten to play simple musical tunes by writing ASCII characters in a particular format to the loudspeaker. The format is described in detail in the file spkr.c . There is a sample "song file" called bugle; it may be played by typing: cat bugle > /dev/spkr Bugle was written by a colleague of mine at Microport, Rex Core. It's possible that the system will NOT boot and run the new kernel. If you have the new boot code that lets you specify the kernel name, *and* you backed up the old kernel as described above, you may type system5.old in response to the boot prompt, and the old kernel will boot. If you have the old boot code, which boots /system5 without asking, you have to boot your system from floppy and copy /system5 on the floppy to /system5 on the hard disk. Ask your system administrator; it's beyond the scope of this doc. I would appreciate any comments on the device driver, especially song files, additions, enhancements, bug fixes, etc. If the demand is great enough I'll summarize and post to the news. ------------------------------CUT HERE--------------------------------------- bugle follows ------------------------------CUT HERE--------------------------------------- 2qg 9qg 3qc 9qg 3ee 9eg 3ec 9eg 2qg 9qg 2qg 9qg 3qc 9qg 3ee 9eg 3ec 9eg 2qg 9qg 2qg 9qg 3qc 9qg 3ee 9eg 3ec 9eg 2qg 9qg 3qc 9qg 3he 9hg 3hc 9hg ------------------------------CUT HERE--------------------------------------- spkr.h follows ------------------------------CUT HERE--------------------------------------- /* * Header file for the speaker I/O driver - Marc de Groot */ /************************** COPYRIGHT NOTICE ************************** * This device driver is copyright (c) 1987 by Marc de Groot. Further * * distribution of all or part of this device driver must include this * * copyright notice. Distribution of compiled object code derived from * * this device driver must be accompanied by the source code with this * * copyright notice included. * ***********************************************************************/ /* * Port addresses */ #define SP_TIMER 0x40 /* speaker timer port base addr */ #define SP_PORTB 0x61 /* speaker gate port addr */ /* * ioctl function codes */ #define SP_SET_DIVISOR (('S' << 8) | 0) #define SP_TURN_ON (('S' << 8) | 1) #define SP_SET_TICKS (('S' << 8) | 2) #define SP_SET_FREQUENCY (('S' << 8) | 3) #define SP_SET_TEMPO (('S' << 8) | 4) /* * constants for the state machine */ #define SP_OCTAVE 20 #define SP_NOTE_LEN 21 #define SP_NOTENM1 22 #define SP_NOTENM2 23 /* * Character macros */ #define iswhite(x) ((x) == '\n' || (x) == '\t' || (x) == ' ') #define toupper(x) ((x) >= 'a' && (x) <= 'z' ? (x) & 0xdf : (x)) #define ctoi(x) ((x) - 0x30) /* * Debugging macro */ #define DBG(p) { if (sp_debug) {p} } ------------------------------CUT HERE--------------------------------------- spkr.c follows ------------------------------CUT HERE--------------------------------------- /* * Loudspeaker device driver for Microport System V/AT */ /************************** COPYRIGHT NOTICE ************************** * This device driver is copyright (c) 1987 by Marc de Groot. Further * * distribution of all or part of this device driver must include this * * copyright notice. Distribution of compiled object code derived from * * this device driver must be accompanied by the source code with this * * copyright notice included. * ***********************************************************************/ /* * Include files */ #include <sys/types.h> #include <sys/signal.h> #include <sys/dir.h> #include <sys/param.h> #include <sys/errno.h> #include <sys/user.h> #include <sys/ioctl.h> #include "spkr.h" /* * Global variables */ int spkr_open = 0; /* spkr_open != 0 means dev is open */ int sp_divisor; /* Freq divisor. Loaded into timer. */ int sp_note_name; /* ASCII name of the current note. */ int sp_ticks; /* # of ticks that the tone lasts. */ int sp_tempo; /* # of ticks in a whole note. */ int sp_octave; /* How many times we shift divisor. */ int sp_symbol_type; /* Next sym. state mach. will parse */ int sp_debug = 0; /* sp_debug != 0 means print diags */ /* sp_debug may be changed with */ /* /etc/patch */ long sp_divisors[] = /* The chromatic scale. */ { 61856, /* A */ 58384, /* A# */ 58384, /* Bb */ 55107, /* B */ 52014, /* C */ 49095, /* C# */ 49095, /* Db */ 46340, /* D */ 43739, /* D# */ 43739, /* Eb */ 41284, /* E */ 38966, /* F */ 36779, /* F# */ 36779, /* Gb */ 34715, /* G */ 32767, /* G# */ 32767 /* Ab */ }; int sp_note_names[] = { 'A' << 8, 'A' << 8 | '#', 'B' << 8 | 'b', 'B' << 8 , 'C' << 8, 'C' << 8 | '#', 'D' << 8 | 'b', 'D' << 8, 'D' << 8 | '#', 'E' << 8 | 'b', 'E' << 8, 'F' << 8, 'F' << 8 | '#', 'G' << 8 | 'b', 'G' << 8, 'G' << 8 | '#', 'A' << 8 | 'b' }; /* * spopen - Open the speaker device */ spopen(dev, flag) int dev, flag; { if(spkr_open) /* If spkr already open */ { u.u_error = ENXIO; /* Report error */ return; } spkr_open = 1; /* Others can't use spkr */ sp_tempo = 20; /* Whole note = 1/3 second */ sp_symbol_type = SP_OCTAVE; /* Initialize state machine */ sp_octave = 4; /* Sane value for the octave */ sp_note_name = 'A' << 8; /* Sane note name */ sp_ticks = 10; /* Sane # of ticks ( 1/6 sec ) */ } /* * spclose - close the speaker device */ spclose(dev) int dev; { if(!spkr_open) { u.u_error = ENXIO; return; } spkr_open = 0; } /* * spread - speaker read routine */ spread(dev) int dev; { } /* * spwrite - speaker write routine * * Description of character output: * The spwrite routine incorporates a simple state machine which * interprets a representation of musical notes. A whitespace * character causes a reset of the state machine to its initial * state, and plays the note set by the previous non-whitespace * character string. That character string is expected to consist * of: odnn * where: o is the octave number * d is the duration of the note; one of * w for whole note * h for half note * q for quarter note * e for eighth note * s for sixteenth note * t for thirty-secondth note * (Upper or lower case) * nn is a one- or two-character string giving the note * name. The first character is one of * A B C D E F G (Upper or lower case) * The second character, if present, is one of * b # (To indicate flat or sharp, respectively) * * Example: * The string "3qbb 5wf " would play a B-flat quarter note in the third * octave, and then an F-natural whole note in the fifth octave. * * Bugs: * There is no detection of erroneous note representations. Unexpected * characters will produce unpredictable results. */ spwrite(dev) int dev; { char c; unsigned int i; for(i = u.u_count; i--; u.u_base++) { copyin(u.u_base, &c, 1); state_machine(c); } u.u_count = 0; } /* * spioctl - speaker ioctl routine * * Functions supported: * * SP_SET_DIVISOR - Set the frequency divisor. The speaker output * frequency will be 1193180 / iarg Hertz. * * SP_TURN_ON - Turn on the loudspeaker for a certain number of * ticks of the system clock. * * SP_SET_FREQUENCY - The frequency divisor is set such that the * speaker output will be iarg Hertz the next time it is turned on. * This frequency is independent of the processor frequency. * * SP_SET_TICKS - Set the number of ticks of the system clock. The * speaker will be turned off after this number of ticks the next time * it is turned on. * * SP_SET_TEMPO - Set the length of a whole note to iarg ticks of the * system clock. */ spioctl(dev, cmd, arg) int dev, cmd; union ioctl_arg arg; { switch(cmd) { case SP_SET_DIVISOR: { sp_divisor = arg.iarg; break; } case SP_TURN_ON: { sp_setparms(); sp_turn_on(); break; } case SP_SET_FREQUENCY: { sp_divisor = 1193180L / (long)arg.iarg; break; } case SP_SET_TICKS: { sp_ticks = arg.iarg; break; } case SP_SET_TEMPO: { sp_tempo = arg.iarg; break; } } } /* * Supporting subroutines follow */ /* * sp_setparms - Set up the 8253 timer for sound */ sp_setparms() { outb(SP_TIMER + 3, 0xb6); /* Timer 2, lsb, msb, binary */ outb(SP_TIMER + 2, sp_divisor & 0xff); /* Output lo byte */ outb(SP_TIMER + 2, sp_divisor >> 8 & 0xff); /* Output hi byte */ } /* * sp_turn_on() - Turn on the loudspeaker gate, delay for a number of * system ticks, and turn the loudspeaker back off. */ sp_turn_on() { outb(SP_PORTB, inb(SP_PORTB) | 3); /* Gate timer 2, turn sp on */ delay(sp_ticks); /* Wait sp_ticks/HZ seconds */ outb(SP_PORTB, inb(SP_PORTB) & ~3); /* Gate timer 2, turn sp off*/ } /* * state_machine - The note-playing state machine */ state_machine(c) char c; { DBG(printf("state_machine(0x%x) ", c);) if(iswhite(c)) /* Whitespace inits state machine */ { if(sp_symbol_type != SP_OCTAVE) { sp_cnvt_note(); /* Cnvrt sp_note_name to sp_divisor */ sp_setparms(); /* Load sp_divisor into timer hdwe. */ sp_turn_on(); /* Load timer and turn on speaker */ } sp_symbol_type = SP_OCTAVE; /* Get set up for next note */ DBG(printf("EXIT1 ");) return; } DBG(printf("sp_symbol_type=%d ", sp_symbol_type);) switch(sp_symbol_type) { case SP_OCTAVE: sp_octave = ctoi(c); sp_symbol_type = SP_NOTE_LEN; DBG(printf("sp_octave=%d ",sp_octave);) break; case SP_NOTE_LEN: sp_symbol_type = SP_NOTENM1; switch(c) { case 'W': case 'w': sp_ticks = sp_tempo; break; case 'H': case 'h': sp_ticks = sp_tempo / 2; break; case 'Q': case 'q': sp_ticks = sp_tempo / 4; break; case 'E': case 'e': sp_ticks = sp_tempo / 8; break; case 'S': case 's': sp_ticks = sp_tempo / 16; break; case 'T': case 't': sp_ticks = sp_tempo / 32; break; } DBG(printf("sp_ticks=%d ", sp_ticks);) break; case SP_NOTENM1: sp_note_name = toupper(c) << 8; sp_symbol_type = SP_NOTENM2; DBG(printf("1st:sp_note_name=0x%x ",sp_note_name);) break; case SP_NOTENM2: if(c == '#' || c == 'b') { sp_note_name |= c; } DBG(printf("2nd:sp_note_name=%d ",sp_note_name);) break; } DBG(printf("EXIT2 ");) } /* * sp_cnvt_note - Given a note designator, convert to a frequency * divisor and a number of ticks. */ sp_cnvt_note() { int i; DBG(printf("sp_cnvt_note() ");) for(i = 0; i < 17; i++) { if(sp_note_names[i] == sp_note_name) { sp_divisor = sp_divisors[i] >> sp_octave; DBG(printf("sp_divisor=%d, EXIT1 ", sp_divisor);) return; } } DBG(printf("EXIT2 ");) }