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

Cache object: d004c2cfc95e806924e9119100cf4c33


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