Audio on hp300 - News

In the late 1980s, with the expansion of the Internet (even though it was not open to commercial activities yet) and the slowly increasing capabilities of workstations, some people started to imagine the unthinkable: that, some day, you may use your computer to record voice messages, send them over the Internet, and the recipient could listen to these messages on hir own computer.

That was definitely science fiction... until workstation manufacturers started to add audio capabilities to their hardware.


You might remember the NeXT Computer among the first workstations with digital audio capabilities, in 1988. But, apart from NeXT, other companies were also testing the waters, first with phone-quality (8KHz mono, 8-bit u-law encoding) capabilities, then later on with CD-quality (44.1KHz stereo, 16-bit) when CD-ROM drives started to become commonplace.

From memory, here is an incomplete list of workstations with phone-quality audio devices:

Machines Architecture
Digital VAXstation 4000 pizza boxes (VLC, 60, 90, 90A, 96) VAX
Digital Personal DECstation 5000 series MIPS
Digital DEC series Alpha
HP 9000/705 and 9000/710 PA-RISC
Sun SPARCstation IPC, IPX, 1, 1+ and 2 SPARC (sun4c)
Sun Sun-4-600 SPARC (sun4m)

Interestingly, all these machines but those from HP are using the AMD 7930 chip as their audio codec.

Here is another incomplete list of workstations, this time with CD-quality audio devices:

You may notice that there are no mention of Motorola 68000-based hardware in these lists. Apart from the NeXT hardware mentioned earlier, none of the 68000-based workstations ever had audio capabilities.

Or had they?


Well, there is always an exception.

The HP 9000/425e system.

This particular machine is the last machine of the 9000/400 series; it was probably put together in order to get customers not yet ready to switch to PA-RISC processors as a cheap workstation with an easy upgrade plan, as it uses the same case and motherboard form factor as the - also cheap - 9000/705 and 9000/710 PA-RISC entry level workstations.

The 9000/400 series in itself is already an odd family. These systems were designed after HP had bought Apollo. Despite promising the most important Apollo customers that their investment in Apollo hardware were safe, HP quickly stabbed Apollo in the back and stomped over its corpse until it was no longer to be seen. The only concession HP made to Apollo customers was a set of workstations which could run both Domain/OS, the Apollo operating system, and HP-UX, the HP operating system: these were the 9000/400 series.

Because they are supposed to work in both worlds, the ROM is actually a dual-mode ROM with both HP-UX and Domain/OS personalities, and can switch back and forth between them; they have both an Apollo Domain keyboard connector and an HP-HIL connector (to which HP keyboards and mice can connect); and they have at least one ISA slot, in order to support the Domain Token Ring network controller (Apollo systems were using the ISA bus for all their expansion boards.)

When the ROM is configured in HP-UX mode, these systems can run the BSD ports to the hp300 hardware; model 425e, being a bit different than the other models in the 400 series - either 400 (50MHz 68030 processor), 425 (25MHz 68040 processor) or 433 (33MHz 68040 processor) with suffixes being either s (deskside, with a 4-slot ISA card cage on the side) or t (desktop, with a single ISA slot, or dl, a diskless variant ot the t with only four memory slots soldered on the motherboard and no on-board SCSI and HPIB interfaces) - got supported later, and first limited to serial console until I added SGC bus support to OpenBSD/hp300 in 2005; these changes got eventually carried over to NetBSD by Izumi Tsutsui.


I was lucky enough to find a 9000/425e system in october 2004, thanks to Guillaume Lachenal. Unfortunately the machine I got was not in working condition; it had no memory and no disk, and even after putting compatible memory in it, it would not complete its selftests. Moreover, the processor was running very hot, much more than it should have. I turned to fellow developer Todd Miller, who I knew also had a 425e, for help.

Date: Sun, 14 Nov 2004 21:21:32 +0000
From: Miod Vallat
To: Todd C. Miller
Subject: 425e no-go

I am stuck with the G led blinking very slowly (i.e. on for 10 seconds,
off for 10 seconds, etc...)

Any ideas?

Then the day after...

Date: Mon, 15 Nov 2004 15:42:16 +0000
From: Miod Vallat
To: Todd C. Miller
Subject: more 425e

If I consider the 425e led error codes match the early 715, as found on
http://www.openpa.net/led.html#scorpio, it means the cpu is toast. And
indeed it runs very hot for a 040@25.

If I am unlucky enough it is soldered )-:

Miod

But that story would not exist if I had not been able to fix that machine...

Date: Mon, 15 Nov 2004 21:44:17 +0000
From: Miod Vallat
To: Todd C. Miller
Subject: 425e happy

It is happy after a CPU transplant from that - useless anyway - 425dl.

Miod

When booting that machine under OpenBSD, the kernel messages reported...

OpenBSD 3.6-current (GENERIC) #37: Mon Jan 17 05:30:41 GMT 2005
    miod@epi.gentiane.org:/usr/src/sys/arch/hp300/compile/GENERIC
HP 9000/425e (25MHz MC68040 CPU+MMU+FPU, 4k on-chip physical I/D caches)
real mem  = 16764928 (16372K)
avail mem = 9674752 (9448K)
using 204 buffers containing 835584 bytes (816K) of memory
Parity detection enabled
mainbus0 (root)
intio0 at mainbus0
frodo0 at intio0 addr 0x41c000
dnkbd at frodo0 offset 0x0 not configured
apci0 at frodo0 offset 0x20: console, no fifo
apci1 at frodo0 offset 0x40: no fifo
apci2 at frodo0 offset 0x60: no fifo
hil0 at intio0 addr 0x428000
dio0 at mainbus0: 98620C, 2 channels, 32 bit DMA
digital audio at dio0 scode 8 not configured
internal parallel at dio0 scode 12 not configured
spc0 at dio0 scode 14 ipl 4: 98265A SCSI, 32-bit DMA, SCSI ID 7
scsibus0 at spc0: 8 targets
sd0 at scsibus0 targ 4 lun 0: <DEC, RZ26L (C) DEC, 442D> SCSI2 0/direct fixed
sd0: 1001MB, 3117 cyl, 8 head, 82 sec, 512 bytes/sec, 2050860 sec total
le0 at dio0 scode 21 ipl 5: address 08:00:09:15:35:44
le0: 8 receive buffers, 2 transmit buffers
sgc0 at mainbus0
sti0 at sgc0 slot 0: EVRX rev 8.02;75, ID 0xC599000000000000
sti0: 2048x1024 frame buffer, 1280x1024x8 display, offset 2048x1024
sti0: 10x20 font type 1, 40 bpc, charset 0-255
interrupt levels: bio = 4, net = 5, tty = 5
boot device: sd0
root on sd0a swap on sd0b
hil0: no devices
wsdisplay0 at sti0
wsdisplay0: screen 0 added (default, vt100 emulation)
rootdev=0x400 rrootdev=0x800 rawdev=0x802

Note the mention of digital audio... not configured. The 425e case indeed has audio input and output jack connectors on the back.

So it would be a nice challenge to get audio support on that machine. But, after I did the necessary work to get its frame buffer supported, that machine, after being upgraded to 48MB of memory (the maximum it could support, with six 8MB SIMMs), that machine quickly became a build machine, building OpenBSD/hp300 snapshots and releases in a loop, thanks to its lower power consumption than the 425t I was previously using for that purpose, which gave no more room for tinkering.

And since none of the rare 425e owners did ask for audio support, nothing happened.


However, on 2010, developer Alexandre Ratchov commited the forgotten part of a larger change, which had broken the OpenBSD builds after said incomplete change had been commited. I don't know what crazy idea hit him, but he decided to add a joke at the expense of two OpenBSD supported platforms without audio capabilities, in the commit message:

Date: Wed, 13 Jan 2010 10:02:52 +0000
From: Alexandre Ratchov
To: source-changes
Subject: CVS: cvs.openbsd.org: src

[...]
Log message:
Rename s/safile/siofile/g, missed when libsa was renamed to
libsndio. Fixes crashes in full-duplex mode on vax and hp300.

Now, as mentioned earlier, some VAX actually have audio capabilities, but the OpenBSD/vax port didn't support them at that time, so nobody's pride was harmed by this joke.

But seeing this commit message, I moved the "audio on vax" item of my todolist to a higher position, and almost two years later, in september 2011, I ported the NetBSD/vax unfinished yet almost complete audio driver glue for the VAXstation 4000/VLC and VAXstation 4000/60, which had been submitted by Blaz Antonic in 2004 but had never been taken care of, to OpenBSD/vax, and added support for the VAXstation 4000/90 in the process.

(There was support for the AMD 7930 chip already since many years in both NetBSD and OpenBSD, where it originated from Chris Torek's SPARC 4.4BSD port; the missing bits were simply VAX-specific attachment and configuration code.)

In order to make sure the driver was working correctly, I had to compile mpg123 and play a mp3 song (using mpg123 --8bit -m -r 8000 to match the audio output capabilities of the hardware) on the VAXstation 4000/90's internal speaker, and this is probably the first time a mp3 file has ever been played on a VAX (on september 4th.)

There were a few other OpenBSD developers gathering at my house later in october that year, and they would not believe that one could play audio on a VAX, so I had to play a mp3 file a second time on october 22nd, and this is probably the last time a mp3 file has ever been played on a VAX.

After the VAX audio situation was taken care of, I had no excuse not to give hp300 audio a try.


Looking for clues about which audio chip could be present on the 425e motherboard, I turned to the HP-UX system headers. Fortunately, although I had never been close to an hp300 system running HP-UX, I had a backup from an HP-UX 9.05 installation on my PA-RISC model 9000/720 (which was since then running OpenBSD/hppa, of course), and could peek at its /usr/include/sys/audio.h header file.

You can find a slightly more recent version of that file here; this file is dated 93/12/06 as it comes from HP-UX 9.10, while mine was dated 92/08/04. Their contents are however almost identical.

Simply glancing at that file yielded interesting discoveries.

To begin with, there are two kind of audio hardware supported in that version of HP-UX:

/* Valid audio id's returned by AUDIO_DESCRIBE */

#define AUDIO_ID_PSB2160   1
#define AUDIO_ID_CS4215    2

The second one in this list is obviously the Crystal Semiconductors CS4215 sound chip, used on many PA-RISC systems and supported by the harmony driver in OpenBSD/hppa.

Then, late in the file, there are two register layout declarations, one for audio1_reg and the other for audio2_reg. The audio2_reg layout looks like this:

struct audio2_reg {
    volatile unsigned long id;         /* Sound ID (read only) */
    volatile unsigned long reset;      /* Reset Register (r/w) */
    volatile unsigned long cntl;       /* Control Register (r/w) */
    volatile unsigned long gainctl;    /* Gain Control (r/w) */
    volatile unsigned long pnxtadd;    /* Playback Next Address (r/w) */
    volatile unsigned long pcuradd;    /* Playback Current Address (read only) */
    volatile unsigned long rnxtadd;    /* Record Next Address (r/w) */
    volatile unsigned long rcuradd;    /* Record Current Address (read only) */
    volatile unsigned long dstatus;    /* DMA status (read only) */
    volatile unsigned long ovrange;    /* Over Range (r/w) */
    volatile unsigned long pio;        /* PIO register (r/w) */
    unsigned long filler[4];
    volatile unsigned long diag;        /* DIAG clock register (read only) */
};

Compare this to the register offsets listed in OpenBSD's harmony driver (in sys/arch/hppa/gsc/harmonyreg.h):

#define HARMONY_NREGS   0x40

#define HARMONY_ID              0x00            /* identification */
#define HARMONY_RESET           0x04            /* reset */
#define HARMONY_CNTL            0x08            /* control */
#define HARMONY_GAINCTL         0x0c            /* gain control */
#define HARMONY_PNXTADD         0x10            /* play next address */
#define HARMONY_PCURADD         0x14            /* play current address */
#define HARMONY_RNXTADD         0x18            /* record next address */
#define HARMONY_RCURADD         0x1c            /* record current address */
#define HARMONY_DSTATUS         0x20            /* device status */
#define HARMONY_OV              0x24            /* overrange input */
#define HARMONY_PIO             0x28            /* general purpose i/o */
#define HARMONY_DIAG            0x3c            /* chi diagnostic */

They are obviously describing the same register set. So, if audio2_reg matches the harmony driver, audio1_reg would be a possible candidate for the 425e sound hardware, and look no further:

#ifdef __hp9000s300
struct audio1_reg {
    volatile unsigned char filler1;
    volatile unsigned char idreset;
    volatile unsigned char filler2;
    volatile unsigned char interrupt;
    volatile unsigned char filler3;
    volatile unsigned char fifointctl;
    volatile unsigned char filler4;
    volatile unsigned char fifostatus;
    volatile unsigned char filler5;
    volatile unsigned char inoutfifo;
    volatile unsigned char filler6;
    volatile unsigned char ctlfifo;
};
#else
struct audio1_reg {
    volatile unsigned char filler0;
    volatile unsigned char idreset;    /* Sound ID and Reset Register (r/w)    */
    volatile unsigned char filler1;
    volatile unsigned char filler2;
    volatile unsigned char filler3;
    volatile unsigned char interrupt;  /* Status and Control Register (r/w)    */
    volatile unsigned char filler4;
    volatile unsigned char filler5;
    volatile unsigned char filler6;
    volatile unsigned char fifointctl; /* FIFO Interrupt Register (r/w)        */
    volatile unsigned char filler7;
    volatile unsigned char filler8;
    volatile unsigned char filler9;
    volatile unsigned char fifostatus; /* FIFO Status Register (read only)     */
    volatile unsigned char fillera;
    volatile unsigned char fillerb;
    volatile unsigned char fillerc;
    volatile unsigned char inoutfifo;  /* Sound Data Port (r/w)                */
    volatile unsigned char fillerd;
    volatile unsigned char fillere;
    volatile unsigned char fillerf;
    volatile unsigned char ctlfifo;    /* Sound Control Port (write only)      */
};
#endif

The __hp9000s300 preprocessor ifdef construct declares a different hardware layout on series 300 than on series 700 (the PA-RISC systems.)

If you're a bit familiar with expansion board designs and skip the filler declarations, you will quickly notice that the registers are located on the second byte of every 16-bit word on series 300, and on the second byte of every 32-bit word, on series 700. This also hints that this is the same hardware setup, connected to a 16-bit wide bus on series 300 and to a 32-bit wide bus on series 700, which totally makes sense as the expansion bus on series 300 is HP's DIO bus, a 16-bit bus, while the PA-RISC designs use 32-bit busses everywhere.

Near the end of the file, I also found these defines:

#ifdef __hp9000s700
#define AUDIO_INT_LEVEL   5
#define AUDIO_1_CARD_ID           0x7a
#define AUDIO_1_NOBEEPER_CARD_ID  0x7e
#define AUDIO_2_CARD_ID           0x7f
#define AUDIO_2_NOBEEPER_CARD_ID  0x7b
#define AUDIO_2_HPA               0xf1000000

#define IRQ_SGC_SLOT1     20
#define IRQ_SGC_SLOT2     19
#define IRQ_AUDIO_CORE    18
#else
#define AUDIO_INT_LEVEL   6
#define AUDIO_1_CARD_ID   0x13
#define AUDIO_2_CARD_ID   0x14
#define AUDIO_1_NOBEEPER_CARD_ID  0x13
#define AUDIO_2_NOBEEPER_CARD_ID  0x14
#endif

What do they tell us? The __hp9000s700 part matches the known board ids for PA-RISC hardware, with the AUDIO_1_CARD_ID being known as Core audio and AUDIO_2_CARD_ID as Advanced audio. And on the series 300 side, the AUDIO_1_CARD_ID matches the known DIO board id reported as digital audio in the kernel messages.

(A list of known board ids for many PA-RISC hardware can be found in the OpenBSD source tree as sys/arch/hppa/dev/cpudevs, and a list of known DIO board ids can be found in the NetBSD source tree as sys/arch/hp300/dev/diodevs.)

I doubt the DIO id for an "audio2" board has ever been used. There might have been plans to produce new series 400 hardware with a CS4215, or a standalone DIO expansion board, but I couldn't find any evidence such hardware ever existed. Also, if it had, I would expect a different declaration of the audio2_reg struct on series 300, especially because the DIO bus specification requires the board id (on reads) and board reset (on writes) register to appear at offset 1 from the board's address space, which is not the case with the audio2_reg declaration. But I digress.

At this point, all I had were very strong clues hinting at the presence of a PSB2160 chip on the 425e. But was this true? And what was this chip capable of?


Looking carefully at the 425e motherboard, it did not take long to find the PSB2160 chip:

This gave me two pieces of vital information. First, the chip had been produced by Siemens; second, that it was labeled ARCOFI. Searching for both PSB2160 and ARCOFI on Internet returned pointers to its datasheet, as well as the meaning of that name: it stands for Audio Receive COdec FIlter.

Nowadays I can't seem to be able to find that datasheet anywhere, so I've put a copy here for people interested. That file is slightly smaller than 2MB, and contains 63 pages of scanned typewritten text, sometimes badly cropped, but this is much better than nothing.

After reading the datasheet and confirming its registers description and various programming values were matching the defines in HP-UX's sys/audio.h, I could start working on a driver.

The Siemens chip itself is very simple, and designed to be used on telephony equipment, although it can also be run in 16-bit mode, giving a much more pleasant sound. It has no buffering capabilities, and would interrupt after every sound sample played or recorded, which means 8000 interrupts per second - something that would put a 68040 to its knees.

Because of this, the HP engineers have put the PSB2160 behind a 128-byte FIFO logic, reducing the interrupt frequency to a more reasonable 62.5 interrupts per second. But in order to get a smooth sound processing, it was better to have it interrupt when the FIFO is half-empty (during playback) or half-full (during recording), therefore every 64 bytes processed: 125 interrupts per second (250 when using 16-bit samples), which was still a huge improvement. Thankfully, the defines in HP-UX's sys/audio.h were complete and descriptive enough to let me understand the operation of the FIFO logic and control it.

Working on my driver code, I eventually reached a point where I had something to test. I booted my experimental kernel. The driver appeared to attach correctly.

arcofi0 at dio0 scode 8 ipl 6
audio0 at arcofi0

Now all I had to do to test playback, was to cat a file to /dev/audio. Of course, this needed to be an audio file with 8KHz, 8-bit data in uLaw format.

It turns out that I have a thank-you.au file lying in my home directory since (almost) the dawn of time. It's a few seconds long recording of a man saying ``Thank you for your cooperation'' which I had downloaded somewhere on the Internet so long ago that I don't remember why and what for, but since then turned out to be a perfect test file for a phone-quality audio driver.

$ file thank-you.au                  
thank-you.au: Sun/NeXT audio data: 8-bit ISDN mu-law, mono, 8000 Hz

So I issued the cat command, and the machine immediately locked up. Of all the possible outcomes (no sound, garbled sound, kernel panic, spurious interrupt messages...) this was not what I had expected.


The number one cause of hard lockups is a high-priority interrupt not being acknowledged correctly, and thus remaining pending. And indeed, the audio device had a high priority (ipl 6 in the kernel messages, as opposed to the other various drivers using interrupt privilege levels values ranging from 2 to 5. In fact, that 6 looked a bit odd (despite being an even number), because I had never seen values over 5 before on hp300.

On Motorola 68000-based systems, the processor has seven device interrupt pins, to receive external interrupt notifications on up to seven levels, with the highest levels having priority over the lowest. The kernel needs to manage lists of registered interrupt handlers at a given level, in order to have all of them check, when an interrupt for their level occurs, if the interrupt is caused by the device they work for. In the hp300 port, this is handled by a specific routine called intr_dispatch, which gets invoked from the assembly interrupt handlers found in sys/arch/hp300/hp300/locore.s:

ENTRY_NOPROFILE(intrhand)       /* levels 2 through 5 */
        INTERRUPT_SAVEREG
        movw    sp@(22),sp@-            | push exception vector info
        clrw    sp@-
        jbsr    _C_LABEL(intr_dispatch) | call dispatch routine
        addql   #4,sp
        INTERRUPT_RESTOREREG
        jra     _ASM_LABEL(rei)         | all done

Wait, the comment says "levels 2 through 5". What happens for level 6? The answer lies a few lines further down:

ENTRY_NOPROFILE(lev6intr)       /* level 6: clock */
        INTERRUPT_SAVEREG
        CLKADDR(a0)
        movb    a0@(CLKSR),d0           | read clock status
Lclkagain:
        btst    #0,d0                   | clear timer1 int immediately to
        jeq     Lnotim1                 |  minimize chance of losing another
        movpw   a0@(CLKMSB1),d1         |  due to statintr processing delay
Lnotim1:
        btst    #2,d0                   | timer3 interrupt?
        jeq     Lnotim3                 | no, skip statclock
        movpw   a0@(CLKMSB3),d1         | clear timer3 interrupt
        lea     sp@(16),a1              | a1 = &clockframe
        movl    d0,sp@-                 | save status
        movl    a1,sp@-
        jbsr    _C_LABEL(statintr)      | statintr(&frame)
        addql   #4,sp
        movl    sp@+,d0                 | restore pre-statintr status
        CLKADDR(a0)
Lnotim3:
        btst    #0,d0                   | timer1 interrupt?
        jeq     Lrecheck                | no, skip hardclock
        lea     sp@(16),a1              | a1 = &clockframe
        movl    a1,sp@-
        jbsr    _C_LABEL(clockintr)     | clockintr(&frame)
        addql   #4,sp
        CLKADDR(a0)
Lrecheck:
        addql   #1,_C_LABEL(uvmexp)+UVMEXP_INTRS | chalk up another interrupt
        movb    a0@(CLKSR),d0           | see if anything happened
        jmi     Lclkagain               |  while we were in clockintr/statintr
        INTERRUPT_RESTOREREG
        jra     _ASM_LABEL(rei)         | all done

Where is intr_dispatch being called here? Well, nowhere. Because, until I started my experiments with the audio device, all the interrupts occuring at level 6 were clock interrupts, and they were being serviced as fast as possible. Thus, the interrupts produced by the audio hardware remained unprocessed and, never being acknowledged, the kernel became stuck in a loop entering its level 6 handler, not having anything clock-related to do and not being to help on the audio side.

The fix was trivial, and consisted of adding the four lines of the levels 2 to 5 handler, between the INTERRUPT_SAVEREG and INTERRUPT_RESTOREREG macros, which invoke the C intr_dispatch routine, immediately before the INTERRUPT_RESTOREREG macro of the level 6 handler. And in order to make this extra intr_dispatch call at every clock interrupts (thus 100 times per second) not uselessly costly, I put these new lines within #if NAUDIO > 0 preprocessor guards, to not compile them on kernel lacking audio drivers.

(I probably should have been more aggressive and patch this at runtime to skip the call, after all device had been probed and their interrupt handlers registered, if it turned out there was no interrupt handler registered for level 6.)

Once the interrupt handling code had been fixed, I had the pleasure to get correctly recognizable audio while playing that thank-you.au file, with the interrupt counters correctly reporting the expected 125 interrupts per second. I could start teasing Todd Miller with an updated dmesg:

Date: Tue, 20 Dec 2011 22:05:55 +0000
From: Miod Vallat
To: Todd C. Miller
Subject: fancy 425e dmesg
  
You still have your 425e, do you? Can you spot something unusual in this
dmesg?

OpenBSD 5.0-current (GENERIC) #150: Tue Dec 20 21:58:58 GMT 2011
    miod@seuge.gentiane.org:/usr/src/sys/arch/hp300/compile/GENERIC
HP 9000/425e (25MHz MC68040 CPU+MMU+FPU, 4k on-chip physical I/D caches)
real mem = 50331648 (48MB)
avail mem = 45219840 (43MB)
Parity detection enabled
mainbus0 at root
intio0 at mainbus0
frodo0 at intio0 addr 0x41c000 ipl 5: service mode enabled
dnkbd0 at frodo0 offset 0x0: no keyboard
apci0 at frodo0 offset 0x20: console, no fifo
apci1 at frodo0 offset 0x40: no fifo
apci2 at frodo0 offset 0x60: no fifo
hil0 at intio0 addr 0x428000 ipl 1
dio0 at mainbus0: 98620C, 2 channels, 32 bit DMA
arcofi0 at dio0 scode 8 ipl 6
audio0 at arcofi0
internal parallel at dio0 scode 12 not configured
spc0 at dio0 scode 14 ipl 4: 98265A SCSI, 32-bit DMA
scsibus0 at spc0: 8 targets, initiator 7
sd0 at scsibus0 targ 3 lun 0: <IBM, DCAS-34330, S65A> SCSI2 0/direct fixed serial.IBM_DCAS-34330_B3GV3534_
sd0: 4134MB, 512 bytes/sector, 8467200 sectors
le0 at dio0 scode 21 ipl 5: address 08:00:09:15:35:44
le0: 8 receive buffers, 2 transmit buffers
sgc0 at mainbus0
sti0 at sgc0 slot 0: rev 8.02;75, ID 0x27134C9F40A00499
sti0: EVRX, 2048x1024 frame buffer, 1280x1024x8 display
sti0: 10x20 font type 1, 40 bpc, charset 0-255
wsdisplay0 at sti0 mux 1
wsdisplay0: screen 0 added (std, vt100 emulation)
interrupt levels: bio = 4, net = 5, tty = 5
boot device: sd0
hil0: no devices
vscsi0 at root
scsibus1 at vscsi0: 256 targets
softraid0 at root
scsibus2 at softraid0: 256 targets
root on sd0a swap on sd0b dump on sd0b

After checking that the driver would correctly be able to output on either the internal speaker or the external line output, controlled with mixerctl. The only missing part was the volume control (and recording, which I would add a bit later.)

The OpenBSD audio subsystem exposes a linear volume scale to the user. But the PSB2160 chip needs to be programmed with a logarithmic gain, using fixed-point values. And I had no idea how to compute a proper volume table, apart from the minimum and maximum values which were documented in the datasheet.

Searching for more information on that topic, I stumbled upon a ready-to-use gain table for a close relative of that chip, the Siemens PSB2163. Unfortunately for me, these values turned out to be completely wrong for the PSB2160.

I was considering giving up, putting a crude table and documenting it as horrible in the manual page, when I realized that the audio driver in HP-UX probably contained a similar table, which I could reuse the values of.

I then searched for a freely available HP-UX binary patch which would replace the audio driver, and eventually found one for HP-UX 10.20. Checking the file symbols, it did not take me long to find a private_audio_gain_tab data block in audio_shared.o. The values in this table had miminal and maximal values compatible with the PSB2160 documentation. After picking these values, the volume control on the 425e did behave as expected, with small but noticeable attenuation as the volume goes down.

The source code of my driver acknowledges this.

/*
 * Gain programming formulae are a complete mystery to me, and of course
 * no two chips are compatible - not even the PSB 2163 and PSB 2165
 * later ARCOFI chips, from the same manufacturer as the PSB 2160!
 *
 * Of course, the PSB 2160 datasheet does not give any set of values.
 * The following table is taken from the HP-UX audio driver (audio_shared.o
 * private_audio_gain_tab).
 */

#define NEGATIVE_GAINS  60
#define POSITIVE_GAINS  14
static const uint16_t arcofi_gains[1 + NEGATIVE_GAINS + 1 + POSITIVE_GAINS] = {
        /* minus infinity */
        0x0988,

        0xf8b8, 0xf8b8, 0xf8b8, 0xf8b8, 0x099f, 0x099f, 0x099f, 0x099f,
        0x09af, 0x09af, 0x09af, 0x09cf, 0x09cf, 0x09cf, 0xf8a9, 0xf83a,
        0xf83a, 0xf82b, 0xf82d, 0xf8a3, 0xf8b2, 0xf8a1, 0xe8aa, 0xe84b,
        0xe89e, 0xe8d3, 0xe891, 0xe8b1, 0xd8aa, 0xd8cb, 0xd8a6, 0xd8b3,
        0xd842, 0xd8b1, 0xc8aa, 0xc8bb, 0xc888, 0xc853, 0xc852, 0xc8b1,
        0xb8aa, 0xb8ab, 0xb896, 0xb892, 0xb842, 0xb8b1, 0xa8aa, 0xa8bb,
        0x199f, 0x195b, 0x29c1, 0x2923, 0x29aa, 0x392b, 0xf998, 0xb988,
        0x1aac, 0x3aa1, 0xbaa1, 0xbb88,

        /* 0 */
        0x8888,

        0xd388, 0x5288, 0xb1a1, 0x31a1, 0x1192, 0x11d0, 0x30c0, 0x2050,
        0x1021, 0x1020, 0x1000, 0x0001, 0x0010, 0x0000
};

I now had a working arcofi driver for hp300. But the information obtained from HP-UX hinted that this device had also been used on some PA-RISC systems. Therefore it made sense to split the device into a machine-independent part, and small attachment glue code, one for the hp300 port, and another for the hppa port.

But I needed to confirm that models 9000/705 and 9000/710 indeed were using a PSB2160 chip. And I didn't have such a machine. Fortunately, developer Tobias Ulmer had a 705, so, while working on the driver on my 425e, I asked him to look at the motherboard of his machine, attaching the picture of the Siemens chip seen above.

Date: Sat, 17 Dec 2011 14:12:53 +0000
From: Miod Vallat
To: Tobias Ulmer
Subject: Re: HP apollo 705
  
Also, could you open the machine and have a look at the audio chip it
uses? Since the harmony(4) driver did not attach, I'd expect the chip
not to be the Crystal Semi CS4215 (square-shaped), but instead the
Siemens PSB2160 (see attached picture for an example of this chip being
used in another HP board). Could you confirm this? And if so, would you
be interested in testing a driver for it if I ever get it to work?
   
Miod

And I had been correct! There was indeed such a chip in his machine. I then worked on the driver split and wrote the hppa glue. And since the code did work on the 425e, I decided it was time to commit the complete driver to the OpenBSD source tree, but keep it disabled on hppa, until people could test and report success.

From: Miod Vallat
Date: Wed, 21 Dec 2011 23:12:03 +0000
To: source-changes
Subject: CVS: cvs.openbsd.org: src

CVSROOT:        /cvs
Module name:    src
Changes by:     miod@cvs.openbsd.org    2011/12/21 16:12:03

Modified files:
        share/man/man4 : Makefile
        share/man/man4/man4.hp300: dio.4 intro.4
        share/man/man4/man4.hppa: gsc.4
        sys/arch/hp300/conf: GENERIC files.hp300
        sys/arch/hp300/hp300: conf.c
        sys/arch/hppa/conf: GENERIC files.hppa
        sys/conf       : files
Added files:
        share/man/man4 : arcofi.4
        sys/arch/hp300/dev: arcofi_dio.c
        sys/arch/hppa/gsc: arcofi_gsc.c
        sys/dev/ic     : arcofi.c arcofivar.h

Log message:
Work-in-progress driver for the HP ``Audio1'' device found on the HP 9000/425e
(hp300) and the HP9000/705 and 9000/710 (hppa). 8-bit mono, 8KHz, no surprise
since it is based upon a digital phone chip.
Tested on 425e only so far, and playback only; configured in, but disabled,
on hppa kernels until there are positive test reports (I am not sure the
interrupt assignment on hppa is correct).
And now people no longer can joke about audio on hp300.

The next day, I found out why I had not been able to get 16-bit audio to work and enabled 16-bit audio.

From: Miod Vallat
Date: Thu, 22 Dec 2011 23:20:48 +0000
To: source-changes
Subject: CVS: cvs.openbsd.org: src

CVSROOT:        /cvs
Module name:    src
Changes by:     miod@cvs.openbsd.org    2011/12/22 16:20:48

Modified files:
        sys/conf       : files 
        sys/dev/ic     : arcofi.c 

Log message:
Add 16-bit modes to arcofi(4). Big-endian signed is native, others require some
bit or byte flipping. Trivial, and would have been part of the initial commit,
had I not made the mistake of testing a big-endian chip with 16-bit audio
data in little-endian format (``oops'').

The hppa driver was eventually successfully tested by Tobias Ulmer, and it got enabled on january 2nd.

After the OpenBSD/hp300 got removed in 2014, I asked Izumi Tsutsui if he would be interested to get my 425e, so that he could improve the NetBSD support for that machine (and port the audio driver).

He gladly accepted, and the arcofi driver eventually was ported to NetBSD on august 2014 (although the hppa attachment hasn't, but this does not really matter, as NetBSD currently doesn't run on the PCXS flavour of PA-RISC processors used on the 9000/705 and 9000/710.)

While doing this porting work, he even noticed and reported a locking bug in the OpenBSD driver, introduced by mistake in 2013 during a rework of the OpenBSD audio subsystem.


The moral of this story? Be careful when you put jokes in commit messages, for they might have unforeseen consequences.