home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!cbmvax!yoda!ag
- From: ag@yoda.omnicron.com (Keith Gabryelski)
- Newsgroups: comp.unix.amiga
- Subject: Working audio driver source.
- Message-ID: <1005@yoda.omnicron.com>
- Date: 23 Dec 92 00:39:33 GMT
- Organization: Omnicron Data Systems
- Lines: 843
-
- Here is a working audio driver I wrote about a month ago. It replaces
- the code in /usr/sys/amiga/driver/audio.[ch] and has only one known
- bug which is that small samples may have the first part of their
- sample cut off.
-
- I was away from home for the last month while this device driver was
- running and the sayclock (/usr/amiga/src/cmd/clksnd/sayclock) program
- was outputting a stream of data every ten minutes to it. So I believe
- this is a mostly functional audio driver.
-
- It currently supports stream audio data and repeated wave forms (via a
- repeat count ioctl) so this code can be used in the X server to
- produce console beeps. I may update the X11R5 code someday to handle
- this.
-
- I would also like this audio driver to be used for the console
- driver's ^G (beep). I haven't thought of a good way of interfacing
- kernel accesses to the audio driver yet but will send patches when
- this has been completed.
-
- I am currently in the midst of moving to Boston and working on a
- startup project so you will have to forgive me if I do not reply to
- mail reports very quickly.
-
- Merry Christmas.
-
- Pax, Keith
-
-
- # This is a shell archive. Remove anything before this line,
- # then unpack it by saving it in a file and typing "sh file".
- #
- # This archive contains:
- # audio.c audio.h
- #
-
- echo x - audio.c
- cat >audio.c <<'@EOF'
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/sysmacros.h>
- #include <sys/signal.h>
- #include <sys/file.h>
- #include <sys/user.h>
- #include <sys/proc.h>
- #include <sys/cmn_err.h>
- #include <sys/stream.h>
- #include <sys/stropts.h>
- #include <sys/errno.h>
- #include <sys/kmem.h>
- #include <sys/inline.h>
- #include <sys/mkdev.h>
- #include "memory.h"
- #include "amigahr.h"
- #include "audio.h"
-
- extern char *panicstr;
-
- static int audioopen(queue_t *, dev_t *, int, int, cred_t *);
- static int audioclose(queue_t *);
- static int audiowput(struct queue *, mblk_t *);
- static int audiowsrv(struct queue *);
- static void audioioctl(queue_t *, mblk_t *);
- static int audioinit(void);
- static int postaudio(struct audio_client *, mblk_t *);
- static struct audio_channel *grab_channel(struct audio_client *);
- static int fill_buffer(struct audio_buffer *, signed char *, int);
- static int allocate_channel_buffers(struct audio_channel *);
- static void loadup_dma(struct audio_channel *, struct audio_buffer *);
-
- static struct module_info audiomiinfo =
- {
- 0, "audio", 0, INFPSZ, 1024, 0,
- };
-
- static struct qinit audiorinit =
- {
- NULL, NULL, audioopen, audioclose, NULL, &audiomiinfo,
- };
-
- static struct module_info audiomoinfo =
- {
- 42, "audio", 0, INFPSZ, AUDIO_BUFFER_SIZE * 4, AUDIO_BUFFER_SIZE,
- };
-
- static struct qinit audiowinit =
- {
- audiowput, audiowsrv, audioopen, audioclose, NULL, &audiomoinfo,
- };
-
- struct streamtab audioinfo =
- {
- &audiorinit, &audiowinit, NULL, NULL,
- };
-
- #define blklen(bp) (bp->b_wptr - bp->b_rptr)
-
- static struct audio_buffer silent_buffer, handfeed_buffer;
-
- /*
- * audio_channel: one for each audio port we have. The standard
- * amiga has four channels, two dedicated to the left speaker, and
- * two to the right speaker.
- */
- static struct audio_channel audio_channel[MAXCHANNELS] =
- {
- { NULL, AIEAUD0, DMAAUD0, },
- { NULL, AIEAUD1, DMAAUD1, },
- { NULL, AIEAUD2, DMAAUD2, },
- { NULL, AIEAUD3, DMAAUD3, },
- };
-
- static struct audio_client *audio_clients;
-
- static int audioinit(void)
- {
- int i;
- static boolean_t initialized;
-
- if (initialized)
- return 0;
-
- silent_buffer.data = AllocMem(2, MEMF_CHIP);
- if (!silent_buffer.data)
- {
- printf("audioinit: Couldn't allocate 2 bytes of chip memory\n");
- return 1;
- }
- silent_buffer.data[0] = silent_buffer.data[1] = 0;
- silent_buffer.size = 2;
- silent_buffer.nbytes = 2;
- silent_buffer.period = 1;
- silent_buffer.volume = 0;
- silent_buffer.repeat = 0;
-
- handfeed_buffer.data = silent_buffer.data;
- handfeed_buffer.size = 2;
- handfeed_buffer.nbytes = 2;
- handfeed_buffer.period = 1;
- handfeed_buffer.volume = 0;
- handfeed_buffer.repeat = 0;
-
- for (i = 0; i < MAXCHANNELS; ++i)
- {
- /*
- * Clear and disable channel
- */
- AMIGA->intena = AINTCLR | audio_channel[i].dmadone;
- AMIGA->intreq = AINTCLR | audio_channel[i].dmadone;
- AMIGA->dmacon = DMACLR | audio_channel[i].enable;
-
- audio_channel[i].audio = &AMIGA->audio[i];
- audio_channel[i].audio->buf = silent_buffer.data;
- audio_channel[i].audio->size = 1;
- audio_channel[i].audio->period = 1;
- audio_channel[i].audio->loud = 0;
- audio_channel[i].audio->data = 0;
- if (allocate_channel_buffers(&audio_channel[i]))
- {
- printf("couldn't allocate channel buffer %d\n", i);
-
- /* XXX Need to free previously allocated audio buffers */
-
- return 1;
- }
-
- /* <IMPLEMENT> There should be a timeout here incase */
- /* <IMPLEMENT> the audio hardware is broken */
- while (!(AMIGA->intreqr & audio_channel[i].dmadone))
- ;
-
- AMIGA->intreq = AINTCLR | audio_channel[i].dmadone;
- }
-
- initialized = B_TRUE;
-
- return 0;
- }
-
- static int allocate_channel_buffers(struct audio_channel *ach)
- {
- ach->buffer[0].data = AllocMem(AUDIO_BUFFER_SIZE, MEMF_CHIP);
- if (!ach->buffer[0].data)
- return ENOMEM;
-
- ach->buffer[0].size = AUDIO_BUFFER_SIZE;
-
- ach->buffer[1].data = AllocMem(AUDIO_BUFFER_SIZE, MEMF_CHIP);
- if (!ach->buffer[1].data)
- {
- FreeMem(ach->buffer[0].data, AUDIO_BUFFER_SIZE);
- return ENOMEM;
- }
-
- ach->buffer[1].size = AUDIO_BUFFER_SIZE;
-
- ach->buffer[2].data = AllocMem(AUDIO_BUFFER_SIZE, MEMF_CHIP);
- if (!ach->buffer[2].data)
- {
- FreeMem(ach->buffer[0].data, AUDIO_BUFFER_SIZE);
- FreeMem(ach->buffer[1].data, AUDIO_BUFFER_SIZE);
- return ENOMEM;
- }
-
- ach->buffer[2].size = AUDIO_BUFFER_SIZE;
-
- ach->buffer[0].nbytes = ach->buffer[1].nbytes = ach->buffer[2].nbytes = 0;
- ach->playing = ach->loaded = ach->filling = 0;
-
- return 0;
- }
-
- static int audioopen(queue_t *q, dev_t *devp, int flag, int sflag,
- cred_t *credp)
- {
- struct audio_client *acl, *aclp;
- minor_t mindev;
- int s;
-
- if (q->q_ptr)
- return EBUSY;
-
- if (sflag == MODOPEN || getminor(*devp))
- return EINVAL;
-
- acl = kmem_alloc(sizeof *acl, 0);
- if (!acl)
- return ENOMEM;
-
- s = splaudio();
-
- if (audioinit())
- {
- splx(s);
- return ENOMEM;
- }
-
- for (mindev = 1; mindev <= MAXMIN; ++mindev)
- {
- for (aclp = audio_clients; aclp; aclp = aclp->next)
- {
- if (mindev == aclp->mindev)
- break;
- }
-
- if (!aclp)
- break;
- }
- if (mindev > MAXMIN)
- {
- splx(s);
- kmem_free(acl, sizeof *acl);
- return ENXIO;
- }
-
- acl->flags = 0;
- acl->port = 0;
- acl->volume = BLOUD;
- acl->period = BPER;
- acl->repeat = 0;
- acl->wq = WR(q);
- acl->mindev = mindev;
- *devp = makedevice(getmajor(*devp), mindev);
-
- q->q_ptr = acl;
- WR(q)->q_ptr = acl;
-
- acl->next = audio_clients;
- audio_clients = acl;
-
- splx(s);
-
- return 0;
- }
-
- static int audioclose(queue_t *q)
- {
- struct audio_client **aclpp, *acl = (struct audio_client *)q->q_ptr;
- int s = splaudio();
-
- while (WR(q)->q_first)
- {
- /*
- * There are still some requests pending; Wait for drain.
- */
-
- acl->flags |= AUD_CCLOSING;
- if (sleep(acl, PCATCH | PZERO))
- break;
- }
-
- if (audio_channel[acl->port].client == acl)
- audio_channel[acl->port].client = 0;
-
- for (aclpp = &audio_clients; *aclpp; aclpp = &(*aclpp)->next)
- {
- if (*aclpp == acl)
- {
- *aclpp = (*aclpp)->next;
- break;
- }
- }
-
- q->q_ptr = 0;
- WR(q)->q_ptr = 0;
-
- splx(s);
-
- kmem_free(acl, sizeof *acl);
-
- return 0;
- }
-
- static int audiowput(struct queue *q, mblk_t *mp)
- {
- int s = splaudio();
-
- switch (mp->b_datap->db_type)
- {
- case M_DATA:
- putq(q, mp);
- break;
-
- case M_IOCTL:
- case M_IOCDATA:
- audioioctl(q, mp);
- break;
-
- default:
- freemsg(mp);
- break;
- }
-
- splx(s);
-
- return 0;
- }
-
- static void audioioctl(queue_t *q, mblk_t *mp)
- {
- struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
- struct audio_client *audio_client = (struct audio_client *)q->q_ptr;
-
- if (mp->b_datap->db_type == M_IOCDATA)
- {
- /*
- * For copyin/copyout failures, just free message.
- */
- if (((struct copyresp *)mp->b_rptr)->cp_rval)
- {
- freemsg(mp);
- return;
- }
-
- if (!((struct copyresp *)mp->b_rptr)->cp_private)
- {
- mp->b_datap->db_type = M_IOCACK;
- freemsg(unlinkb(mp));
- iocbp->ioc_count = 0;
- iocbp->ioc_rval = 0;
- iocbp->ioc_error = 0;
- putnext(RD(q), mp);
- return;
- }
- }
-
- switch (iocbp->ioc_cmd)
- {
- case AUDIO_VOLUME:
- {
- int *volume;
-
- if (mp->b_datap->db_type == M_IOCTL &&
- iocbp->ioc_count == TRANSPARENT)
- {
- struct copyreq *creq = (struct copyreq *)mp->b_rptr;
- mp->b_datap->db_type = M_COPYIN;
- creq->cq_addr = *(caddr_t *)mp->b_cont->b_rptr;
- mp->b_wptr = mp->b_rptr + sizeof *creq;
- creq->cq_size = sizeof *volume;
- creq->cq_flag = 0;
- creq->cq_private = (mblk_t *)1;
- putnext(RD(q), mp);
- return;
- }
- else
- {
- mblk_t *bp1;
- int i;
-
- (void) pullupmsg(mp->b_cont, -1);
-
- if (!mp->b_cont || blklen(mp->b_cont) != sizeof *volume)
- {
- iocbp->ioc_error = EINVAL;
- mp->b_datap->db_type = M_IOCNAK;
- iocbp->ioc_count = 0;
- putnext(RD(q), mp);
- break;
- }
-
- volume = (int *)mp->b_cont->b_rptr;
-
- mp->b_datap->db_type = M_IOCACK;
-
- audio_client->volume = *volume;
-
- bp1 = unlinkb(mp);
- if (bp1)
- freeb(bp1);
- iocbp->ioc_count = 0;
- putnext(RD(q), mp);
-
- }
- break;
- }
-
- case AUDIO_PERIOD:
- {
- int *period;
-
- if (mp->b_datap->db_type == M_IOCTL &&
- iocbp->ioc_count == TRANSPARENT)
- {
- struct copyreq *creq = (struct copyreq *)mp->b_rptr;
- mp->b_datap->db_type = M_COPYIN;
- creq->cq_addr = *(caddr_t *)mp->b_cont->b_rptr;
- mp->b_wptr = mp->b_rptr + sizeof *creq;
- creq->cq_size = sizeof *period;
- creq->cq_flag = 0;
- creq->cq_private = (mblk_t *)1;
- putnext(RD(q), mp);
- return;
- }
- else
- {
- mblk_t *bp1;
- int i;
-
- (void) pullupmsg(mp->b_cont, -1);
-
- if (!mp->b_cont || blklen(mp->b_cont) != sizeof *period)
- {
- iocbp->ioc_error = EINVAL;
- mp->b_datap->db_type = M_IOCNAK;
- iocbp->ioc_count = 0;
- putnext(RD(q), mp);
- break;
- }
-
- period = (int *)mp->b_cont->b_rptr;
-
- mp->b_datap->db_type = M_IOCACK;
-
- audio_client->period = *period;
-
- bp1 = unlinkb(mp);
- if (bp1)
- freeb(bp1);
- iocbp->ioc_count = 0;
- putnext(RD(q), mp);
-
- }
- break;
- }
-
- case AUDIO_REPEAT:
- {
- int *repeat;
-
- if (mp->b_datap->db_type == M_IOCTL &&
- iocbp->ioc_count == TRANSPARENT)
- {
- struct copyreq *creq = (struct copyreq *)mp->b_rptr;
- mp->b_datap->db_type = M_COPYIN;
- creq->cq_addr = *(caddr_t *)mp->b_cont->b_rptr;
- mp->b_wptr = mp->b_rptr + sizeof *creq;
- creq->cq_size = sizeof *repeat;
- creq->cq_flag = 0;
- creq->cq_private = (mblk_t *)1;
- putnext(RD(q), mp);
- return;
- }
- else
- {
- mblk_t *bp1;
- int i;
-
- (void) pullupmsg(mp->b_cont, -1);
-
- if (!mp->b_cont || blklen(mp->b_cont) != sizeof *repeat)
- {
- iocbp->ioc_error = EINVAL;
- mp->b_datap->db_type = M_IOCNAK;
- iocbp->ioc_count = 0;
- putnext(RD(q), mp);
- break;
- }
-
- repeat = (int *)mp->b_cont->b_rptr;
-
- mp->b_datap->db_type = M_IOCACK;
-
- audio_client->repeat = *repeat;
-
- bp1 = unlinkb(mp);
- if (bp1)
- freeb(bp1);
- iocbp->ioc_count = 0;
- putnext(RD(q), mp);
-
- }
- break;
- }
-
- default:
- mp->b_datap->db_type = M_IOCNAK;
- freemsg(unlinkb(mp));
- iocbp->ioc_count = 0;
- iocbp->ioc_rval = 0;
- iocbp->ioc_error = EINVAL;
- putnext(RD(q), mp);
- return;
- }
-
- mp->b_datap->db_type = M_IOCACK;
- freemsg(unlinkb(mp));
- iocbp->ioc_count = 0;
- iocbp->ioc_rval = 0;
- iocbp->ioc_error = 0;
- putnext(RD(q), mp);
- }
-
- static int audiowsrv(queue_t *q)
- {
- mblk_t *mp;
- struct audio_client *acl = (struct audio_client *)q->q_ptr;
- int s = splaudio();
-
- /*
- * Service all requests possible.
- */
-
- while ((mp = getq(q, mp)))
- {
- if (postaudio(acl, mp))
- {
- /*
- * Couldn't service this request so places us on the
- * audio_clients list and we will be qenabled after
- * the next write interrupt.
- */
-
- putbq(q, mp);
- break;
- }
-
- freemsg(mp);
- }
-
- splx(s);
-
- return 0;
- }
-
- static int fill_buffer(struct audio_buffer *buffer, signed char *data, int len)
- {
- int size = min(len, buffer->size - buffer->nbytes);
-
- bcopy(data, buffer->data + buffer->nbytes, size);
- buffer->nbytes += size;
-
- return size;
- }
-
- static int postaudio(struct audio_client *acl, mblk_t *mp)
- {
- struct audio_channel *ach;
-
- ach = grab_channel(acl);
- if (!ach)
- return 1;
-
- if (!ach->filling)
- {
- /*
- * Find a buffer that we may use for filling
- */
-
- ach->filling =
- !ach->buffer[0].nbytes ? &ach->buffer[0] :
- !ach->buffer[1].nbytes ? &ach->buffer[1] :
- !ach->buffer[2].nbytes ? &ach->buffer[2] :
- (panic("no fillable audio buffers"),
- (struct audio_buffer *)0);
-
- ach->filling->volume = acl->volume;
- ach->filling->period = acl->period;
- ach->filling->repeat = acl->repeat;
- }
-
- mp->b_rptr +=
- fill_buffer(ach->filling, (signed char *)mp->b_rptr, blklen(mp));
-
- if (!ach->playing)
- {
- /*
- * Handfeed first byte and load up the audio hardware to grab
- * dma registers at next scan line. This is the only way to
- * garrantee that we will play the first dma'd audio sound to
- * a channel.
- */
-
- AMIGA->intena = AINTCLR | ach->dmadone; /* Disable interupts */
- AMIGA->dmacon = DMACLR | ach->enable; /* Disable DMA for now */
- AMIGA->intreq = AINTCLR | ach->dmadone; /* Clear interupt */
-
- if (!ach->loaded)
- ach->loaded = &handfeed_buffer;
-
- ach->audio->buf = silent_buffer.data;
- ach->audio->size = 1;
- ach->audio->period = 1;
- ach->audio->loud = 0;
- ach->audio->data = 0;
-
- /*
- * Busy-wait for hand fed word
- */
-
- while (!(AMIGA->intreqr & ach->dmadone))
- ;
-
- AMIGA->dmacon = DMASET | ach->enable; /* Enable DMA */
- AMIGA->intreq = AINTCLR | ach->dmadone; /* Clear interupt */
- AMIGA->intena = AINTSET | ach->dmadone; /* Enable interupts */
- }
-
- return mp->b_wptr - mp->b_rptr;
- }
-
- static struct audio_channel *grab_channel(struct audio_client *acl)
- {
- if (audio_channel[acl->port].client != acl)
- {
- int i;
-
- for (i = 0; i < MAXCHANNELS; ++i)
- {
- if (!audio_channel[i].client)
- {
- /*
- * This channel is not allocated--Use it.
- */
-
- acl->port = i;
- break;
- }
- }
-
- if (i >= MAXCHANNELS)
- return 0; /* No free channels */
-
- audio_channel[acl->port].client = acl;
- }
-
- return &audio_channel[acl->port];
- }
-
- static void loadup_dma(struct audio_channel *ach,
- struct audio_buffer *load_buffer)
- {
- ach->audio->buf = load_buffer->data;
- ach->audio->size = load_buffer->nbytes / 2;
-
- ach->loaded = load_buffer;
- }
-
- void audiointr(unsigned int reqbits)
- {
- int i;
-
- if (panicstr)
- {
- /*
- * The system has paniced; Turn off audio ports.
- */
-
- AMIGA->intena = AINTCLR | AIEAUD0 | AIEAUD1 | AIEAUD2 | AIEAUD3;
- AMIGA->dmacon = DMACLR | DMAAUD0 | DMAAUD1 | DMAAUD2 | DMAAUD3;
- return;
- }
-
- for (i = 0; i < MAXCHANNELS; ++i)
- {
- /*
- * If this channel is in use: Check to see if the DMA finished
- * our last request and post another audio if it has. If this
- * queue is empty, remove it from the audio_channel queue,
- * disable the audio channel and wake up the client if it is
- * sleeping in close.
- */
-
- if (reqbits & (1 << i))
- {
- struct audio_channel *ach = &audio_channel[i];
- struct audio_client *acl = ach->client;
-
- if (ach->playing && ach->playing != &silent_buffer)
- {
- ach->audio->period = ach->playing->period;
- ach->audio->loud = ach->playing->volume;
- if (ach->playing->repeat > 0)
- {
- --ach->playing->repeat;
- loadup_dma(ach, ach->playing);
- continue;
- }
- else
- ach->playing->nbytes = 0;
- }
-
- if (acl)
- {
- qenable(acl->wq);
-
- if (!acl->wq->q_first && acl->flags & AUD_CCLOSING)
- {
- acl->flags &= ~AUD_CCLOSING;
- wakeup(acl);
- ach->client = 0;
- }
- }
-
- ach->playing = ach->loaded;
-
- if (ach->filling)
- {
- loadup_dma(ach, ach->filling);
- ach->filling = 0;
- if (acl)
- qenable(acl->wq);
- }
- else
- {
- if (!ach->playing)
- {
- struct audio_client *aclp;
-
- AMIGA->intena = AINTCLR | ach->dmadone;
- AMIGA->dmacon = DMACLR | ach->enable;
-
- ach->client = 0;
-
- for (aclp = audio_clients; aclp; aclp = aclp->next)
- qenable(aclp->wq);
- }
- else if (ach->playing == &silent_buffer)
- ach->loaded = 0;
- else
- loadup_dma(ach, &silent_buffer);
- }
- }
- }
- }
-
- boolean_t play_audio(struct audio_client *client, struct audio_buffer *buffer,
- boolean_t force)
- {
- int s = splaudio();
-
- splx(s);
-
- return B_TRUE;
- }
- @EOF
-
- chmod 644 audio.c
-
- echo x - audio.h
- cat >audio.h <<'@EOF'
- #ifndef _AUDIO_H
- #define _AUDIO_H
-
- #define MAXCHANNELS (4)
-
- #ifdef _KERNEL
-
- #define AUD_CCLOSING (0x01) /* client in close */
-
- struct audio_client
- {
- int flags;
- int port;
- unsigned short volume;
- unsigned short period;
- int repeat;
- queue_t *wq;
- struct audio_client *next;
- minor_t mindev;
- };
-
- struct audio_buffer
- {
- signed char *data; /* chip memory */
- int size; /* size of buffer */
- int nbytes; /* number of bytes copied in buffer */
- unsigned short volume;
- unsigned short period;
- int repeat; /* number of interations for wave */
- };
-
- struct audio_channel
- {
- volatile struct amiga_audio *audio;
- int dmadone; /* AMIGA->intreqr; AUD block done */
- int enable; /* AMIGA->dmacon; AUD chan enable */
- struct audio_client *client; /* Client connected to this channel */
- struct audio_buffer buffer[3];
- struct audio_buffer *playing, *loaded, *filling;
- };
-
- #endif /* _KERNEL */
-
- #define AUDIO_BUFFER_SIZE 8192
-
- #define AUIOC ('U' << 8)
- #define AUDIO_VOLUME (AUIOC | 1)
- #define AUDIO_PERIOD (AUIOC | 2)
- #define AUDIO_REPEAT (AUIOC | 3)
-
-
- #define BLOUD 63
- #define BPER 400
-
- #define splaudio spl4
-
- #endif /* _AUDIO_H */
- @EOF
-
- chmod 644 audio.h
-
- exit 0
-