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 <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 static int pca_initialized = 0;
  148 
  149 static void pcaintr(struct clockframe *frame);
  150 
  151 static  d_open_t        pcaopen;
  152 static  d_close_t       pcaclose;
  153 static  d_write_t       pcawrite;
  154 static  d_ioctl_t       pcaioctl;
  155 static  d_poll_t        pcapoll;
  156 
  157 #define CDEV_MAJOR 24
  158 static struct cdevsw pca_cdevsw = {
  159         /* open */      pcaopen,
  160         /* close */     pcaclose,
  161         /* read */      noread,
  162         /* write */     pcawrite,
  163         /* ioctl */     pcaioctl,
  164         /* poll */      pcapoll,
  165         /* mmap */      nommap,
  166         /* strategy */  nostrategy,
  167         /* name */      "pca",
  168         /* maj */       CDEV_MAJOR,
  169         /* dump */      nodump,
  170         /* psize */     nopsize,
  171         /* flags */     0,
  172         /* bmaj */      -1
  173 };
  174 
  175 static void pca_continue __P((void));
  176 static void pca_init __P((void));
  177 static void pca_pause __P((void));
  178 
  179 static void
  180 conv(const unsigned char *table, unsigned char *buff, unsigned n)
  181 {
  182         unsigned i;
  183 
  184         for (i = 0; i < n; i++)
  185                 buff[i] = table[buff[i]];
  186 }
  187 
  188 
  189 static void
  190 pca_volume(int volume)
  191 {
  192         int i, j;
  193 
  194         for (i=0; i<256; i++) {
  195                 j = ((i-128)*volume)/25;
  196 /* XXX
  197                 j = ((i-128)*volume)/100;
  198 */
  199                 if (j<-128)
  200                         j = -128;
  201                 if (j>127)
  202                         j = 127;
  203                 volume_table[i] = (((255-(j + 128))/4)+1);
  204         }
  205 }
  206 
  207 
  208 static void
  209 pca_init(void)
  210 {
  211         pca_status.open = 0;
  212         pca_status.queries = 0;
  213         pca_status.timer_on = 0;
  214         pca_status.buf[0] = (unsigned char *)&buffer1[0];
  215         pca_status.buf[1] = (unsigned char *)&buffer2[0];
  216         pca_status.buf[2] = (unsigned char *)&buffer3[0];
  217         pca_status.buffer = pca_status.buf[0];
  218         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
  219         pca_status.current = 0;
  220         pca_status.sample_rate = SAMPLE_RATE;
  221         pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
  222         pca_status.encoding = AUDIO_ENCODING_ULAW;
  223         pca_status.volume = 100;
  224 
  225         pca_volume(pca_status.volume);
  226 }
  227 
  228 
  229 static int
  230 pca_start(void)
  231 {
  232         int x = splhigh();
  233         int rv = 0;
  234 
  235         /* use the first buffer */
  236         pca_status.current  = 0;
  237         pca_status.index = 0;
  238         pca_status.counter = 0;
  239         pca_status.buffer  = pca_status.buf[pca_status.current];
  240         pca_status.oldval = inb(IO_PPI) | 0x03;
  241         /* acquire the timers */
  242         if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
  243                 rv = -1;
  244         else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
  245                 release_timer2();
  246                 rv =  -1;
  247         } else
  248                 pca_status.timer_on = 1;
  249 
  250         splx(x);
  251         return rv;
  252 }
  253 
  254 
  255 static void
  256 pca_stop(void)
  257 {
  258         int x = splhigh();
  259 
  260         /* release the timers */
  261         release_timer0();
  262         release_timer2();
  263         /* reset the buffer */
  264         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
  265         pca_status.index = 0;
  266         pca_status.counter = 0;
  267         pca_status.current = 0;
  268         pca_status.buffer = pca_status.buf[pca_status.current];
  269         pca_status.timer_on = 0;
  270         splx(x);
  271 }
  272 
  273 
  274 static void
  275 pca_pause(void)
  276 {
  277         int x = splhigh();
  278 
  279         release_timer0();
  280         release_timer2();
  281         pca_status.timer_on = 0;
  282         splx(x);
  283 }
  284 
  285 
  286 static void
  287 pca_continue(void)
  288 {
  289         int x = splhigh();
  290 
  291         pca_status.oldval = inb(IO_PPI) | 0x03;
  292         acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
  293         acquire_timer0(INTERRUPT_RATE, pcaintr);
  294         pca_status.timer_on = 1;
  295         splx(x);
  296 }
  297 
  298 
  299 static int
  300 pca_wait(void)
  301 {
  302         int error, x;
  303 
  304         if (!pca_status.timer_on)
  305                 return 0;
  306 
  307         while (pca_status.in_use[0] || pca_status.in_use[1] ||
  308             pca_status.in_use[2]) {
  309                 x = spltty();
  310                 pca_sleep = 1;
  311                 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
  312                 pca_sleep = 0;
  313                 splx(x);
  314                 if (error != 0 && error != ERESTART) {
  315                         pca_stop();
  316                         return error;
  317                 }
  318         }
  319         return 0;
  320 }
  321 
  322 
  323 static struct isa_pnp_id pca_ids[] = {
  324         {0x0008d041, "AT-style speaker sound"}, /* PNP0800 */
  325         {0}
  326 };
  327 
  328 static int
  329 pcaprobe(device_t dev)
  330 {
  331         int error;
  332 
  333         /* Check isapnp ids */
  334         error = ISA_PNP_PROBE(device_get_parent(dev), dev, pca_ids);
  335         if (error == ENXIO)
  336                 return ENXIO;
  337         return 0;
  338 }
  339 
  340 
  341 static int
  342 pcaattach(device_t dev)
  343 {
  344         pca_init();
  345         make_dev(&pca_cdevsw, 0, 0, 0, 0600, "pcaudio");
  346         make_dev(&pca_cdevsw, 128, 0, 0, 0600, "pcaudioctl");
  347         return 0;
  348 }
  349 
  350 static device_method_t pca_methods[] = {
  351         DEVMETHOD(device_probe,         pcaprobe),
  352         DEVMETHOD(device_attach,        pcaattach),
  353         { 0, 0 }
  354 };
  355 
  356 static driver_t pca_driver = {
  357         "pca",
  358         pca_methods,
  359         1
  360 };
  361 
  362 static devclass_t pca_devclass;
  363 
  364 DRIVER_MODULE(pca, isa, pca_driver, pca_devclass, 0, 0);
  365 
  366 
  367 static int
  368 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
  369 {
  370         /* audioctl device can always be opened */
  371         if (minor(dev) == 128)
  372                 return 0;
  373         if (minor(dev) > 0)
  374                 return ENXIO;
  375 
  376         if (!pca_initialized) {
  377                 pca_init();
  378                 pca_initialized = 1;
  379         }
  380 
  381         /* audio device can only be open by one process */
  382         if (pca_status.open) {
  383                 pca_status.queries = 1;
  384                 return EBUSY;
  385         }
  386         pca_status.buffer = pca_status.buf[0];
  387         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
  388         pca_status.timer_on = 0;
  389         pca_status.open = 1;
  390         pca_status.processed = 0;
  391         return 0;
  392 }
  393 
  394 
  395 static int
  396 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
  397 {
  398         /* audioctl device can always be closed */
  399         if (minor(dev) == 128)
  400                 return 0;
  401         if (minor(dev) > 0)
  402                 return ENXIO;
  403         /* audio device close drains all output and restores timers */
  404         pca_wait();
  405         pca_stop();
  406         pca_status.open = 0;
  407         return 0;
  408 }
  409 
  410 
  411 static int
  412 pcawrite(dev_t dev, struct uio *uio, int flag)
  413 {
  414         int count, error, which, x;
  415 
  416         /* only audio device can be written */
  417         if (minor(dev) > 0)
  418                 return ENXIO;
  419 
  420         while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
  421                 if (pca_status.in_use[0] && pca_status.in_use[1] &&
  422                     pca_status.in_use[2]) {
  423                         if (flag & IO_NDELAY)
  424                                 return EWOULDBLOCK;
  425                         x = spltty();
  426                         pca_sleep = 1;
  427                         error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
  428                         pca_sleep = 0;
  429                         splx(x);
  430                         if (error != 0 && error != ERESTART) {
  431                                 pca_stop();
  432                                 return error;
  433                         }
  434                 }
  435                 if (!pca_status.in_use[0])
  436                         which = 0;
  437                 else if (!pca_status.in_use[1])
  438                         which = 1;
  439                 else
  440                         which = 2;
  441                 if (count && !pca_status.in_use[which]) {
  442                         uiomove(pca_status.buf[which], count, uio);
  443                         pca_status.processed += count;
  444                         switch (pca_status.encoding) {
  445                         case AUDIO_ENCODING_ULAW:
  446                                 conv(ulaw_dsp, pca_status.buf[which], count);
  447                                 break;
  448 
  449                         case AUDIO_ENCODING_ALAW:
  450                                 conv(alaw_linear, pca_status.buf[which], count);
  451                                 break;
  452 
  453                         case AUDIO_ENCODING_RAW:
  454                                 break;
  455                         }
  456                         pca_status.in_use[which] = count;
  457                         if (!pca_status.timer_on)
  458                                 if (pca_start())
  459                                         return EBUSY;
  460                 }
  461         }
  462         return 0;
  463 }
  464 
  465 
  466 static int
  467 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
  468 {
  469         audio_info_t *auptr;
  470 
  471         switch(cmd) {
  472 
  473         case AUDIO_GETINFO:
  474                 auptr = (audio_info_t *)data;
  475                 auptr->play.sample_rate = pca_status.sample_rate;
  476                 auptr->play.channels = 1;
  477                 auptr->play.precision = 8;
  478                 auptr->play.encoding = pca_status.encoding;
  479 
  480                 auptr->play.gain = pca_status.volume;
  481                 auptr->play.port = 0;
  482 
  483                 auptr->play.samples = pca_status.processed;
  484                 auptr->play.eof = 0;
  485                 auptr->play.pause = !pca_status.timer_on;
  486                 auptr->play.error = 0;
  487                 auptr->play.waiting = pca_status.queries;
  488 
  489                 auptr->play.open = pca_status.open;
  490                 auptr->play.active = pca_status.timer_on;
  491                 return 0;
  492 
  493         case AUDIO_SETINFO:
  494                 auptr = (audio_info_t *)data;
  495                 if (auptr->play.sample_rate != (unsigned int)~0) {
  496                         pca_status.sample_rate = auptr->play.sample_rate;
  497                         pca_status.scale =
  498                                 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
  499                 }
  500                 if (auptr->play.encoding != (unsigned int)~0) {
  501                         pca_status.encoding = auptr->play.encoding;
  502                 }
  503                 if (auptr->play.gain != (unsigned int)~0) {
  504                         pca_status.volume = auptr->play.gain;
  505                         pca_volume(pca_status.volume);
  506                 }
  507                 if (auptr->play.pause != (unsigned char)~0) {
  508                         if (auptr->play.pause)
  509                                 pca_pause();
  510                         else
  511                                 pca_continue();
  512                 }
  513 
  514                 return 0;
  515 
  516         case AUDIO_DRAIN:
  517         case AUDIO_COMPAT_DRAIN:
  518                 return pca_wait();
  519 
  520         case AUDIO_FLUSH:
  521         case AUDIO_COMPAT_FLUSH:
  522                 pca_stop();
  523                 return 0;
  524         case FIONBIO:
  525                 return 0;
  526         }
  527         return ENXIO;
  528 }
  529 
  530 
  531 static void
  532 pcaintr(struct clockframe *frame)
  533 {
  534         if (pca_status.index < pca_status.in_use[pca_status.current]) {
  535                 disable_intr();
  536                 __asm__("outb %0,$0x61\n"
  537                         "andb $0xFE,%0\n"
  538                         "outb %0,$0x61"
  539                         : : "a" ((char)pca_status.oldval) );
  540                 __asm__("xlatb\n"
  541                         "outb %0,$0x42"
  542                         : : "a" ((char)pca_status.buffer[pca_status.index]),
  543                             "b" (volume_table) );
  544                 enable_intr();
  545                 pca_status.counter += pca_status.scale;
  546                 pca_status.index = (pca_status.counter >> 8);
  547         }
  548         if (pca_status.index >= pca_status.in_use[pca_status.current]) {
  549                 pca_status.index = pca_status.counter = 0;
  550                 pca_status.in_use[pca_status.current] = 0;
  551                 pca_status.current++;
  552                 if (pca_status.current > 2)
  553                         pca_status.current = 0;
  554                 pca_status.buffer = pca_status.buf[pca_status.current];
  555                 if (pca_sleep)
  556                         wakeup(&pca_sleep);
  557                 if (pca_status.wsel.si_pid) {
  558                         selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
  559                         pca_status.wsel.si_pid = 0;
  560                         pca_status.wsel.si_flags = 0;
  561                 }
  562         }
  563 }
  564 
  565 
  566 static int
  567 pcapoll(dev_t dev, int events, struct proc *p)
  568 {
  569         int s;
  570         struct proc *p1;
  571         int revents = 0;
  572 
  573         s = spltty();
  574 
  575         if (events & (POLLOUT | POLLWRNORM)) {
  576                 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
  577                     !pca_status.in_use[2])
  578                         revents |= events & (POLLOUT | POLLWRNORM);
  579                 else {
  580                         if (pca_status.wsel.si_pid &&
  581                             (p1=pfind(pca_status.wsel.si_pid))
  582                             && p1->p_wchan == (caddr_t)&selwait)
  583                                 pca_status.wsel.si_flags = SI_COLL;
  584                         else
  585                                 pca_status.wsel.si_pid = p->p_pid;
  586                 }
  587         }
  588         splx(s);
  589         return (revents);
  590 }

Cache object: 18726e1cc075c0b3f2b8ae555257706e


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