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-1998 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  *    without modification, immediately at the beginning of the file.
   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 without 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$
   29  */
   30 
   31 #include "pca.h"
   32 #if NPCA > 0
   33 #include "opt_devfs.h"
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/conf.h>
   38 #include <sys/proc.h>
   39 #include <sys/kernel.h>
   40 #include <sys/filio.h>
   41 #include <sys/poll.h>
   42 #include <sys/vnode.h>
   43 
   44 #include <machine/clock.h>
   45 #include <machine/pcaudioio.h>
   46 
   47 #include <i386/isa/isa.h>
   48 #include <i386/isa/isa_device.h>
   49 #include <i386/isa/timerreg.h>
   50 
   51 #ifdef DEVFS
   52 #include <sys/devfsext.h>
   53 #endif /* DEVFS */
   54 
   55 #define BUF_SIZE        8192
   56 #define SAMPLE_RATE     8000
   57 #define INTERRUPT_RATE  16000
   58 
   59 static struct pca_status {
   60         char            open;           /* device open */
   61         char            queries;        /* did others try opening */
   62         unsigned char   *buf[3];        /* triple buffering */
   63         unsigned char   *buffer;        /* current buffer ptr */
   64         unsigned        in_use[3];      /* buffers fill */
   65         unsigned        index;          /* index in current buffer */
   66         unsigned        counter;        /* sample counter */
   67         unsigned        scale;          /* sample counter scale */
   68         unsigned        sample_rate;    /* sample rate */
   69         unsigned        processed;      /* samples processed */
   70         unsigned        volume;         /* volume for pc-speaker */
   71         char            encoding;       /* Ulaw, Alaw or linear */
   72         char            current;        /* current buffer */
   73         unsigned char   oldval;         /* old timer port value */
   74         char            timer_on;       /* is playback running */
   75         struct selinfo  wsel;           /* select/poll status */
   76 } pca_status;
   77 
   78 static char buffer1[BUF_SIZE];
   79 static char buffer2[BUF_SIZE];
   80 static char buffer3[BUF_SIZE];
   81 static char volume_table[256];
   82 
   83 #define DSP_ULAW_NOT_WANTED
   84 #include <i386/isa/snd/ulaw.h>
   85 
   86 static unsigned char alaw_linear[] = {
   87         45,     214,    122,    133,    0,              255,    107,    149, 
   88         86,     171,    126,    129,    0,              255,    117,    138, 
   89         13,     246,    120,    135,    0,              255,    99,     157, 
   90         70,     187,    124,    131,    0,              255,    113,    142, 
   91         61,     198,    123,    132,    0,              255,    111,    145, 
   92         94,     163,    127,    128,    0,              255,    119,    136, 
   93         29,     230,    121,    134,    0,              255,    103,    153, 
   94         78,     179,    125,    130,    0,              255,    115,    140, 
   95         37,     222,    122,    133,    0,              255,    105,    151, 
   96         82,     175,    126,    129,    0,              255,    116,    139, 
   97         5,      254,    120,    135,    0,              255,    97,     159, 
   98         66,     191,    124,    131,    0,              255,    112,    143, 
   99         53,     206,    123,    132,    0,              255,    109,    147, 
  100         90,     167,    127,    128,    0,              255,    118,    137, 
  101         21,     238,    121,    134,    0,              255,    101,    155, 
  102         74,     183,    125,    130,    0,              255,    114,    141, 
  103         49,     210,    123,    133,    0,              255,    108,    148, 
  104         88,     169,    127,    129,    0,              255,    118,    138, 
  105         17,     242,    121,    135,    0,              255,    100,    156, 
  106         72,     185,    125,    131,    0,              255,    114,    142, 
  107         64,     194,    124,    132,    0,              255,    112,    144, 
  108         96,     161,    128,    128,    1,              255,    120,    136, 
  109         33,     226,    122,    134,    0,              255,    104,    152, 
  110         80,     177,    126,    130,    0,              255,    116,    140, 
  111         41,     218,    122,    133,    0,              255,    106,    150, 
  112         84,     173,    126,    129,    0,              255,    117,    139, 
  113         9,      250,    120,    135,    0,              255,    98,     158, 
  114         68,     189,    124,    131,    0,              255,    113,    143, 
  115         57,     202,    123,    132,    0,              255,    110,    146, 
  116         92,     165,    127,    128,    0,              255,    119,    137, 
  117         25,     234,    121,    134,    0,              255,    102,    154, 
  118         76,     181,    125,    130,    0,              255,    115,    141, 
  119 };
  120 
  121 #ifdef DEVFS
  122 static  void    *pca_devfs_token;
  123 static  void    *pcac_devfs_token;
  124 #endif
  125 
  126 static int pca_sleep = 0;
  127 static int pca_initialized = 0;
  128 
  129 static void pcaintr(struct clockframe *frame);
  130 static int pcaprobe(struct isa_device *dvp);
  131 static int pcaattach(struct isa_device *dvp);
  132 
  133 struct  isa_driver pcadriver = {
  134         pcaprobe, pcaattach, "pca",
  135 };
  136 
  137 static  d_open_t        pcaopen;
  138 static  d_close_t       pcaclose;
  139 static  d_write_t       pcawrite;
  140 static  d_ioctl_t       pcaioctl;
  141 static  d_poll_t        pcapoll;
  142 
  143 #define CDEV_MAJOR 24
  144 static struct cdevsw pca_cdevsw = 
  145         { pcaopen,      pcaclose,       noread,         pcawrite,       /*24*/
  146           pcaioctl,     nostop,         nullreset,      nodevtotty,/* pcaudio */
  147           pcapoll,      nommap,         NULL,   "pca",  NULL,   -1 };
  148 
  149 static void pca_continue __P((void));
  150 static void pca_init __P((void));
  151 static void pca_pause __P((void));
  152 
  153 static __inline void
  154 conv(const void *table, void *buff, unsigned int n)
  155 {
  156   __asm__("1:\tmovb (%2), %3\n"
  157           "\txlatb\n"
  158           "\tmovb %3, (%2)\n"
  159           "\tinc %2\n"
  160           "\tdec %1\n"
  161           "\tjnz 1b\n"
  162           :
  163           :"b" (table), "c" (n), "D" (buff), "a" ((char)n)
  164           :"bx","cx","di","ax");
  165 }
  166 
  167 
  168 static void
  169 pca_volume(int volume)
  170 {
  171         int i, j;
  172 
  173         for (i=0; i<256; i++) {
  174                 j = ((i-128)*volume)/25;
  175 /* XXX
  176                 j = ((i-128)*volume)/100;
  177 */
  178                 if (j<-128)
  179                         j = -128;
  180                 if (j>127)
  181                         j = 127;
  182                 volume_table[i] = (((255-(j + 128))/4)+1);
  183         }
  184 }
  185 
  186 
  187 static void
  188 pca_init(void)
  189 {
  190         pca_status.open = 0;
  191         pca_status.queries = 0;
  192         pca_status.timer_on = 0;
  193         pca_status.buf[0] = (unsigned char *)&buffer1[0];
  194         pca_status.buf[1] = (unsigned char *)&buffer2[0];
  195         pca_status.buf[2] = (unsigned char *)&buffer3[0];
  196         pca_status.buffer = pca_status.buf[0];
  197         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
  198         pca_status.current = 0;
  199         pca_status.sample_rate = SAMPLE_RATE;
  200         pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
  201         pca_status.encoding = AUDIO_ENCODING_ULAW;
  202         pca_status.volume = 100;
  203 
  204         pca_volume(pca_status.volume);
  205 }
  206 
  207 
  208 static int
  209 pca_start(void)
  210 {
  211         int x = splhigh();
  212         int rv = 0;
  213 
  214         /* use the first buffer */
  215         pca_status.current  = 0;
  216         pca_status.index = 0;
  217         pca_status.counter = 0;
  218         pca_status.buffer  = pca_status.buf[pca_status.current];
  219         pca_status.oldval = inb(IO_PPI) | 0x03;
  220         /* acquire the timers */
  221         if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
  222                 rv = -1;
  223         else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
  224                 release_timer2();
  225                 rv =  -1;
  226         } else
  227                 pca_status.timer_on = 1;
  228 
  229         splx(x);
  230         return rv;
  231 }
  232 
  233 
  234 static void
  235 pca_stop(void)
  236 {
  237         int x = splhigh();
  238 
  239         /* release the timers */
  240         release_timer0();
  241         release_timer2();
  242         /* reset the buffer */
  243         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
  244         pca_status.index = 0;
  245         pca_status.counter = 0;
  246         pca_status.current = 0;
  247         pca_status.buffer = pca_status.buf[pca_status.current];
  248         pca_status.timer_on = 0;
  249         splx(x);
  250 }
  251 
  252 
  253 static void
  254 pca_pause(void)
  255 {
  256         int x = splhigh();
  257 
  258         release_timer0();
  259         release_timer2();
  260         pca_status.timer_on = 0;
  261         splx(x);
  262 }
  263 
  264 
  265 static void
  266 pca_continue(void)
  267 {
  268         int x = splhigh();
  269 
  270         pca_status.oldval = inb(IO_PPI) | 0x03;
  271         acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
  272         acquire_timer0(INTERRUPT_RATE, pcaintr);
  273         pca_status.timer_on = 1;
  274         splx(x);
  275 }
  276 
  277 
  278 static int
  279 pca_wait(void)
  280 {
  281         int error, x;
  282 
  283         if (!pca_status.timer_on)
  284                 return 0;
  285 
  286         while (pca_status.in_use[0] || pca_status.in_use[1] ||
  287             pca_status.in_use[2]) {
  288                 x = spltty();
  289                 pca_sleep = 1;
  290                 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
  291                 pca_sleep = 0;
  292                 splx(x);
  293                 if (error != 0 && error != ERESTART) {
  294                         pca_stop();
  295                         return error;
  296                 }
  297         }
  298         return 0;
  299 }
  300 
  301 
  302 static int
  303 pcaprobe(struct isa_device *dvp)
  304 {
  305         return(-1);
  306 }
  307 
  308 
  309 static int
  310 pcaattach(struct isa_device *dvp)
  311 {
  312         printf("pca%d: PC speaker audio driver\n", dvp->id_unit);
  313         pca_init();
  314 #ifdef DEVFS
  315         pca_devfs_token = 
  316                 devfs_add_devswf(&pca_cdevsw, 0, DV_CHR, 0, 0, 0600, "pcaudio");
  317         pcac_devfs_token = 
  318                 devfs_add_devswf(&pca_cdevsw, 128, DV_CHR, 0, 0, 0600, 
  319                                  "pcaudioctl");
  320 #endif /*DEVFS*/
  321 
  322         return 1;
  323 }
  324 
  325 
  326 static int
  327 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
  328 {
  329         /* audioctl device can always be opened */
  330         if (minor(dev) == 128)
  331                 return 0;
  332         if (minor(dev) > 0)
  333                 return ENXIO;
  334 
  335         if (!pca_initialized) {
  336                 pca_init();
  337                 pca_initialized = 1;
  338         }
  339 
  340         /* audio device can only be open by one process */
  341         if (pca_status.open) {
  342                 pca_status.queries = 1;
  343                 return EBUSY;
  344         }
  345         pca_status.buffer = pca_status.buf[0];
  346         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
  347         pca_status.timer_on = 0;
  348         pca_status.open = 1;
  349         pca_status.processed = 0;
  350         return 0;
  351 }
  352 
  353 
  354 static int
  355 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
  356 {
  357         /* audioctl device can always be closed */
  358         if (minor(dev) == 128)
  359                 return 0;
  360         if (minor(dev) > 0)
  361                 return ENXIO;
  362         /* audio device close drains all output and restores timers */
  363         pca_wait();
  364         pca_stop();
  365         pca_status.open = 0;
  366         return 0;
  367 }
  368 
  369 
  370 static int
  371 pcawrite(dev_t dev, struct uio *uio, int flag)
  372 {
  373         int count, error, which, x;
  374 
  375         /* only audio device can be written */
  376         if (minor(dev) > 0)
  377                 return ENXIO;
  378 
  379         while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
  380                 if (pca_status.in_use[0] && pca_status.in_use[1] &&
  381                     pca_status.in_use[2]) {
  382                         if (flag & IO_NDELAY)
  383                                 return EWOULDBLOCK;
  384                         x = spltty();
  385                         pca_sleep = 1;
  386                         error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
  387                         pca_sleep = 0;
  388                         splx(x);
  389                         if (error != 0 && error != ERESTART) {
  390                                 pca_stop();
  391                                 return error;
  392                         }
  393                 }
  394                 if (!pca_status.in_use[0])
  395                         which = 0;
  396                 else if (!pca_status.in_use[1])
  397                         which = 1;
  398                 else
  399                         which = 2;
  400                 if (count && !pca_status.in_use[which]) {
  401                         uiomove(pca_status.buf[which], count, uio);
  402                         pca_status.processed += count;
  403                         switch (pca_status.encoding) {
  404                         case AUDIO_ENCODING_ULAW:
  405                                 conv(ulaw_dsp, pca_status.buf[which], count);
  406                                 break;
  407 
  408                         case AUDIO_ENCODING_ALAW:
  409                                 conv(alaw_linear, pca_status.buf[which], count);
  410                                 break;
  411 
  412                         case AUDIO_ENCODING_RAW:
  413                                 break;
  414                         }
  415                         pca_status.in_use[which] = count;
  416                         if (!pca_status.timer_on)
  417                                 if (pca_start())
  418                                         return EBUSY;
  419                 }
  420         }
  421         return 0;
  422 }
  423 
  424 
  425 static int
  426 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
  427 {
  428         audio_info_t *auptr;
  429 
  430         switch(cmd) {
  431 
  432         case AUDIO_GETINFO:
  433                 auptr = (audio_info_t *)data;
  434                 auptr->play.sample_rate = pca_status.sample_rate;
  435                 auptr->play.channels = 1;
  436                 auptr->play.precision = 8;
  437                 auptr->play.encoding = pca_status.encoding;
  438 
  439                 auptr->play.gain = pca_status.volume;
  440                 auptr->play.port = 0;
  441 
  442                 auptr->play.samples = pca_status.processed;
  443                 auptr->play.eof = 0;
  444                 auptr->play.pause = !pca_status.timer_on;
  445                 auptr->play.error = 0;
  446                 auptr->play.waiting = pca_status.queries;
  447 
  448                 auptr->play.open = pca_status.open;
  449                 auptr->play.active = pca_status.timer_on;
  450                 return 0;
  451 
  452         case AUDIO_SETINFO:
  453                 auptr = (audio_info_t *)data;
  454                 if (auptr->play.sample_rate != (unsigned int)~0) {
  455                         pca_status.sample_rate = auptr->play.sample_rate;
  456                         pca_status.scale =
  457                                 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
  458                 }
  459                 if (auptr->play.encoding != (unsigned int)~0) {
  460                         pca_status.encoding = auptr->play.encoding;
  461                 }
  462                 if (auptr->play.gain != (unsigned int)~0) {
  463                         pca_status.volume = auptr->play.gain;
  464                         pca_volume(pca_status.volume);
  465                 }
  466                 if (auptr->play.pause != (unsigned char)~0) {
  467                         if (auptr->play.pause)
  468                                 pca_pause();
  469                         else
  470                                 pca_continue();
  471                 }
  472 
  473                 return 0;
  474 
  475         case AUDIO_DRAIN:
  476         case AUDIO_COMPAT_DRAIN:
  477                 return pca_wait();
  478 
  479         case AUDIO_FLUSH:
  480         case AUDIO_COMPAT_FLUSH:
  481                 pca_stop();
  482                 return 0;
  483         case FIONBIO:
  484                 return 0;
  485         }
  486         return ENXIO;
  487 }
  488 
  489 
  490 static void
  491 pcaintr(struct clockframe *frame)
  492 {
  493         if (pca_status.index < pca_status.in_use[pca_status.current]) {
  494                 disable_intr();
  495                 __asm__("outb %0,$0x61\n"
  496                         "andb $0xFE,%0\n"
  497                         "outb %0,$0x61"
  498                         : : "a" ((char)pca_status.oldval) );
  499                 __asm__("xlatb\n"
  500                         "outb %0,$0x42"
  501                         : : "a" ((char)pca_status.buffer[pca_status.index]),
  502                             "b" (volume_table) );
  503                 enable_intr();
  504                 pca_status.counter += pca_status.scale;
  505                 pca_status.index = (pca_status.counter >> 8);
  506         }
  507         if (pca_status.index >= pca_status.in_use[pca_status.current]) {
  508                 pca_status.index = pca_status.counter = 0;
  509                 pca_status.in_use[pca_status.current] = 0;
  510                 pca_status.current++;
  511                 if (pca_status.current > 2)
  512                         pca_status.current = 0;
  513                 pca_status.buffer = pca_status.buf[pca_status.current];
  514                 if (pca_sleep)
  515                         wakeup(&pca_sleep);
  516                 if (pca_status.wsel.si_pid) {
  517                         selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
  518                         pca_status.wsel.si_pid = 0;
  519                         pca_status.wsel.si_flags = 0;
  520                 }
  521         }
  522 }
  523 
  524 
  525 static int
  526 pcapoll(dev_t dev, int events, struct proc *p)
  527 {
  528         int s;
  529         struct proc *p1;
  530         int revents = 0;
  531 
  532         s = spltty();
  533 
  534         if (events & (POLLOUT | POLLWRNORM))
  535                 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
  536                     !pca_status.in_use[2])
  537                         revents |= events & (POLLOUT | POLLWRNORM);
  538                 else {
  539                         if (pca_status.wsel.si_pid &&
  540                             (p1=pfind(pca_status.wsel.si_pid))
  541                             && p1->p_wchan == (caddr_t)&selwait)
  542                                 pca_status.wsel.si_flags = SI_COLL;
  543                         else
  544                                 pca_status.wsel.si_pid = p->p_pid;
  545                 }
  546 
  547         splx(s);
  548         return (revents);
  549 }
  550 
  551 static pca_devsw_installed = 0;
  552 
  553 static void     pca_drvinit(void *unused)
  554 {
  555         dev_t dev;
  556 
  557         if( ! pca_devsw_installed ) {
  558                 dev = makedev(CDEV_MAJOR, 0);
  559                 cdevsw_add(&dev,&pca_cdevsw, NULL);
  560                 pca_devsw_installed = 1;
  561         }
  562 }
  563 
  564 SYSINIT(pcadev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pca_drvinit,NULL)
  565 
  566 #endif

Cache object: 7c5da39705e0e4b9b3dd52653be9e933


[ 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.