The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/pcaudio.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1994 Søren Schmidt
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer
   10  *    in this position and unchanged.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. The name of the author may not be used to endorse or promote products
   15  *    derived from this software withough specific prior written permission
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27  *
   28  * $FreeBSD: src/sys/i386/isa/pcaudio.c,v 1.32.2.1 1999/09/05 08:13:16 peter Exp $
   29  */
   30 
   31 #include "pca.h"
   32 #if NPCA > 0
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/conf.h>
   37 #include <sys/uio.h>
   38 #include <sys/fcntl.h>
   39 #include <sys/proc.h>
   40 #include <sys/kernel.h>
   41 
   42 #include <machine/clock.h>
   43 #include <machine/pcaudioio.h>
   44 
   45 #include <i386/isa/isa.h>
   46 #include <i386/isa/isa_device.h>
   47 #include <i386/isa/timerreg.h>
   48 
   49 #define DSP_ULAW_NOT_WANTED
   50 #include <i386/isa/sound/ulaw.h>
   51 
   52 #ifdef DEVFS
   53 #include <sys/devfsext.h>
   54 #endif /* DEVFS */
   55 
   56 #define BUF_SIZE        8192
   57 #define SAMPLE_RATE     8000
   58 #define INTERRUPT_RATE  16000
   59 
   60 static struct pca_status {
   61         char            open;           /* device open */
   62         char            queries;        /* did others try opening */
   63         unsigned char   *buf[2];        /* double buffering */
   64         unsigned char   *buffer;        /* current buffer ptr */
   65         unsigned        in_use[2];      /* buffers fill */
   66         unsigned        index;          /* index in current buffer */
   67         unsigned        counter;        /* sample counter */
   68         unsigned        scale;          /* sample counter scale */
   69         unsigned        sample_rate;    /* sample rate */
   70         unsigned        processed;      /* samples processed */
   71         unsigned        volume;         /* volume for pc-speaker */
   72         char            encoding;       /* Ulaw, Alaw or linear */
   73         char            current;        /* current buffer */
   74         unsigned char   oldval;         /* old timer port value */
   75         char            timer_on;       /* is playback running */
   76         struct selinfo  wsel;           /* select status */
   77 } pca_status;
   78 
   79 static char buffer1[BUF_SIZE];
   80 static char buffer2[BUF_SIZE];
   81 static char volume_table[256];
   82 
   83 #ifdef DEVFS
   84 static  void    *pca_devfs_token;
   85 static  void    *pcac_devfs_token;
   86 #endif
   87 
   88 static int pca_sleep = 0;
   89 static int pca_initialized = 0;
   90 
   91 static void pcaintr(struct clockframe *frame);
   92 static int pcaprobe(struct isa_device *dvp);
   93 static int pcaattach(struct isa_device *dvp);
   94 
   95 struct  isa_driver pcadriver = {
   96         pcaprobe, pcaattach, "pca",
   97 };
   98 
   99 static  d_open_t        pcaopen;
  100 static  d_close_t       pcaclose;
  101 static  d_write_t       pcawrite;
  102 static  d_ioctl_t       pcaioctl;
  103 static  d_select_t      pcaselect;
  104 
  105 #define CDEV_MAJOR 24
  106 static struct cdevsw pca_cdevsw = 
  107         { pcaopen,      pcaclose,       noread,         pcawrite,       /*24*/
  108           pcaioctl,     nostop,         nullreset,      nodevtotty,/* pcaudio */
  109           pcaselect,    nommap,         NULL,   "pca",  NULL,   -1 };
  110 
  111 static void pca_continue __P((void));
  112 static void pca_init __P((void));
  113 static void pca_pause __P((void));
  114 
  115 static inline void
  116 conv(const void *table, void *buff, unsigned long n)
  117 {
  118   __asm__("1:\tmovb (%2), %3\n"
  119           "\txlatb\n"
  120           "\tmovb %3, (%2)\n"
  121           "\tinc %2\n"
  122           "\tdec %1\n"
  123           "\tjnz 1b\n"
  124           :
  125           :"b" ((long)table), "c" (n), "D" ((long)buff), "a" ((char)n)
  126           :"bx","cx","di","ax");
  127 }
  128 
  129 
  130 static void
  131 pca_volume(int volume)
  132 {
  133         int i, j;
  134 
  135         for (i=0; i<256; i++) {
  136                 j = ((i-128)*volume)/100;
  137                 if (j<-128)
  138                         j = -128;
  139                 if (j>127)
  140                         j = 127;
  141                 volume_table[i] = (((255-(j + 128))/4)+1);
  142         }
  143 }
  144 
  145 
  146 static void
  147 pca_init(void)
  148 {
  149         pca_status.open = 0;
  150         pca_status.queries = 0;
  151         pca_status.timer_on = 0;
  152         pca_status.buf[0] = (unsigned char *)&buffer1[0];
  153         pca_status.buf[1] = (unsigned char *)&buffer2[0];
  154         pca_status.buffer = pca_status.buf[0];
  155         pca_status.in_use[0] = pca_status.in_use[1] = 0;
  156         pca_status.current = 0;
  157         pca_status.sample_rate = SAMPLE_RATE;
  158         pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
  159         pca_status.encoding = AUDIO_ENCODING_ULAW;
  160         pca_status.volume = 100;
  161 
  162         pca_volume(pca_status.volume);
  163 }
  164 
  165 
  166 static int
  167 pca_start(void)
  168 {
  169         int x = splhigh();
  170         int rv = 0;
  171 
  172         /* use the first buffer */
  173         pca_status.current  = 0;
  174         pca_status.index = 0;
  175         pca_status.counter = 0;
  176         pca_status.buffer  = pca_status.buf[pca_status.current];
  177         pca_status.oldval = inb(IO_PPI) | 0x03;
  178         /* acquire the timers */
  179         if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
  180                 rv = -1;
  181         else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
  182                 release_timer2();
  183                 rv =  -1;
  184         } else
  185                 pca_status.timer_on = 1;
  186 
  187         splx(x);
  188         return rv;
  189 }
  190 
  191 
  192 static void
  193 pca_stop(void)
  194 {
  195         int x = splhigh();
  196 
  197         /* release the timers */
  198         release_timer0();
  199         release_timer2();
  200         /* reset the buffer */
  201         pca_status.in_use[0] = pca_status.in_use[1] = 0;
  202         pca_status.index = 0;
  203         pca_status.counter = 0;
  204         pca_status.current = 0;
  205         pca_status.buffer = pca_status.buf[pca_status.current];
  206         pca_status.timer_on = 0;
  207         splx(x);
  208 }
  209 
  210 
  211 static void
  212 pca_pause(void)
  213 {
  214         int x = splhigh();
  215 
  216         release_timer0();
  217         release_timer2();
  218         pca_status.timer_on = 0;
  219         splx(x);
  220 }
  221 
  222 
  223 static void
  224 pca_continue(void)
  225 {
  226         int x = splhigh();
  227 
  228         pca_status.oldval = inb(IO_PPI) | 0x03;
  229         acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
  230         acquire_timer0(INTERRUPT_RATE, pcaintr);
  231         pca_status.timer_on = 1;
  232         splx(x);
  233 }
  234 
  235 
  236 static int
  237 pca_wait(void)
  238 {
  239         int error, x;
  240 
  241         if (!pca_status.timer_on)
  242                 return 0;
  243 
  244         while (pca_status.in_use[0] || pca_status.in_use[1]) {
  245                 x = spltty();
  246                 pca_sleep = 1;
  247                 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
  248                 pca_sleep = 0;
  249                 splx(x);
  250                 if (error != 0 && error != ERESTART) {
  251                         pca_stop();
  252                         return error;
  253                 }
  254         }
  255         return 0;
  256 }
  257 
  258 
  259 static int
  260 pcaprobe(struct isa_device *dvp)
  261 {
  262         return(-1);
  263 }
  264 
  265 
  266 static int
  267 pcaattach(struct isa_device *dvp)
  268 {
  269         printf("pca%d: PC speaker audio driver\n", dvp->id_unit);
  270         pca_init();
  271 #ifdef DEVFS
  272         pca_devfs_token = 
  273                 devfs_add_devswf(&pca_cdevsw, 0, DV_CHR, 0, 0, 0600, "pcaudio");
  274         pcac_devfs_token = 
  275                 devfs_add_devswf(&pca_cdevsw, 128, DV_CHR, 0, 0, 0600, 
  276                                  "pcaudioctl");
  277 #endif /*DEVFS*/
  278 
  279         return 1;
  280 }
  281 
  282 
  283 static int
  284 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
  285 {
  286         /* audioctl device can always be opened */
  287         if (minor(dev) == 128)
  288                 return 0;
  289         if (minor(dev) > 0)
  290                 return ENXIO;
  291 
  292         if (!pca_initialized) {
  293                 pca_init();
  294                 pca_initialized = 1;
  295         }
  296 
  297         /* audio device can only be open by one process */
  298         if (pca_status.open) {
  299                 pca_status.queries = 1;
  300                 return EBUSY;
  301         }
  302         pca_status.buffer = pca_status.buf[0];
  303         pca_status.in_use[0] = pca_status.in_use[1] = 0;
  304         pca_status.timer_on = 0;
  305         pca_status.open = 1;
  306         pca_status.processed = 0;
  307         return 0;
  308 }
  309 
  310 
  311 static int
  312 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
  313 {
  314         /* audioctl device can always be closed */
  315         if (minor(dev) == 128)
  316                 return 0;
  317         if (minor(dev) > 0)
  318                 return ENXIO;
  319         /* audio device close drains all output and restores timers */
  320         pca_wait();
  321         pca_stop();
  322         pca_status.open = 0;
  323         return 0;
  324 }
  325 
  326 
  327 static int
  328 pcawrite(dev_t dev, struct uio *uio, int flag)
  329 {
  330         int count, error, which, x;
  331 
  332         /* only audio device can be written */
  333         if (minor(dev) > 0)
  334                 return ENXIO;
  335 
  336         while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
  337                 if (pca_status.in_use[0] && pca_status.in_use[1]) {
  338                         x = spltty();
  339                         pca_sleep = 1;
  340                         error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
  341                         pca_sleep = 0;
  342                         splx(x);
  343                         if (error != 0 && error != ERESTART) {
  344                                 pca_stop();
  345                                 return error;
  346                         }
  347                 }
  348                 which = pca_status.in_use[0] ? 1 : 0;
  349                 if (count && !pca_status.in_use[which]) {
  350                         uiomove(pca_status.buf[which], count, uio);
  351                         pca_status.processed += count;
  352                         switch (pca_status.encoding) {
  353                         case AUDIO_ENCODING_ULAW:
  354                                 conv(ulaw_dsp, pca_status.buf[which], count);
  355                                 break;
  356 
  357                         case AUDIO_ENCODING_ALAW:
  358                                 break;
  359 
  360                         case AUDIO_ENCODING_RAW:
  361                                 break;
  362                         }
  363                         pca_status.in_use[which] = count;
  364                         if (!pca_status.timer_on)
  365                                 if (pca_start())
  366                                         return EBUSY;
  367                 }
  368         }
  369         return 0;
  370 }
  371 
  372 
  373 static int
  374 pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)
  375 {
  376         audio_info_t *auptr;
  377 
  378         switch(cmd) {
  379 
  380         case AUDIO_GETINFO:
  381                 auptr = (audio_info_t *)data;
  382                 auptr->play.sample_rate = pca_status.sample_rate;
  383                 auptr->play.channels = 1;
  384                 auptr->play.precision = 8;
  385                 auptr->play.encoding = pca_status.encoding;
  386 
  387                 auptr->play.gain = pca_status.volume;
  388                 auptr->play.port = 0;
  389 
  390                 auptr->play.samples = pca_status.processed;
  391                 auptr->play.eof = 0;
  392                 auptr->play.pause = !pca_status.timer_on;
  393                 auptr->play.error = 0;
  394                 auptr->play.waiting = pca_status.queries;
  395 
  396                 auptr->play.open = pca_status.open;
  397                 auptr->play.active = pca_status.timer_on;
  398                 return 0;
  399 
  400         case AUDIO_SETINFO:
  401                 auptr = (audio_info_t *)data;
  402                 if (auptr->play.sample_rate != (unsigned int)~0) {
  403                         pca_status.sample_rate = auptr->play.sample_rate;
  404                         pca_status.scale =
  405                                 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
  406                 }
  407                 if (auptr->play.encoding != (unsigned int)~0) {
  408                         pca_status.encoding = auptr->play.encoding;
  409                 }
  410                 if (auptr->play.gain != (unsigned int)~0) {
  411                         pca_status.volume = auptr->play.gain;
  412                         pca_volume(pca_status.volume);
  413                 }
  414                 if (auptr->play.pause != (unsigned char)~0) {
  415                         if (auptr->play.pause)
  416                                 pca_pause();
  417                         else
  418                                 pca_continue();
  419                 }
  420 
  421                 return 0;
  422 
  423         case AUDIO_DRAIN:
  424         case AUDIO_COMPAT_DRAIN:
  425                 return pca_wait();
  426 
  427         case AUDIO_FLUSH:
  428         case AUDIO_COMPAT_FLUSH:
  429                 pca_stop();
  430                 return 0;
  431 
  432         }
  433         return ENXIO;
  434 }
  435 
  436 
  437 static void
  438 pcaintr(struct clockframe *frame)
  439 {
  440         if (pca_status.index < pca_status.in_use[pca_status.current]) {
  441                 disable_intr();
  442                 __asm__("outb %0,$0x61\n"
  443                         "andb $0xFE,%0\n"
  444                         "outb %0,$0x61"
  445                         : : "a" ((char)pca_status.oldval) );
  446                 __asm__("xlatb\n"
  447                         "outb %0,$0x42"
  448                         : : "a" ((char)pca_status.buffer[pca_status.index]),
  449                             "b" ((long)volume_table) );
  450                 enable_intr();
  451                 pca_status.counter += pca_status.scale;
  452                 pca_status.index = (pca_status.counter >> 8);
  453         }
  454         if (pca_status.index >= pca_status.in_use[pca_status.current]) {
  455                 pca_status.index = pca_status.counter = 0;
  456                 pca_status.in_use[pca_status.current] = 0;
  457                 pca_status.current ^= 1;
  458                 pca_status.buffer = pca_status.buf[pca_status.current];
  459                 if (pca_sleep)
  460                         wakeup(&pca_sleep);
  461                 if (pca_status.wsel.si_pid) {
  462                         selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
  463                         pca_status.wsel.si_pid = 0;
  464                         pca_status.wsel.si_flags = 0;
  465                 }
  466         }
  467 }
  468 
  469 
  470 static int
  471 pcaselect(dev_t dev, int rw, struct proc *p)
  472 {
  473         int s = spltty();
  474         struct proc *p1;
  475 
  476         switch (rw) {
  477 
  478         case FWRITE:
  479                 if (!pca_status.in_use[0] || !pca_status.in_use[1]) {
  480                         splx(s);
  481                         return(1);
  482                 }
  483                 if (pca_status.wsel.si_pid && (p1=pfind(pca_status.wsel.si_pid))
  484                     && p1->p_wchan == (caddr_t)&selwait)
  485                         pca_status.wsel.si_flags = SI_COLL;
  486                 else
  487                         pca_status.wsel.si_pid = p->p_pid;
  488                 splx(s);
  489                 return 0;
  490         default:
  491                 splx(s);
  492                 return(0);
  493         }
  494 }
  495 
  496 static pca_devsw_installed = 0;
  497 
  498 static void     pca_drvinit(void *unused)
  499 {
  500         dev_t dev;
  501 
  502         if( ! pca_devsw_installed ) {
  503                 dev = makedev(CDEV_MAJOR, 0);
  504                 cdevsw_add(&dev,&pca_cdevsw, NULL);
  505                 pca_devsw_installed = 1;
  506         }
  507 }
  508 
  509 SYSINIT(pcadev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pca_drvinit,NULL)
  510 
  511 
  512 #endif

Cache object: 13e101233ebcd0b42232e1e3e527736a


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.