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

Cache object: 69cfe6aea6876560f79d777ab5413802


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