src/audio/qsa/SDL_qsa_audio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Sun, 28 May 2017 21:50:27 +0200
changeset 11046 8dc8b2ed306e
parent 10737 3406a0f8b041
child 11047 3abb7c6f2e74
permissions -rw-r--r--
qnx: Removed unnecessary call to SDL_zerop() after SDL_calloc().
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 /*
    23  * !!! FIXME: streamline this a little by removing all the
    24  * !!! FIXME:  if (capture) {} else {} sections that are identical
    25  * !!! FIXME:  except for one flag.
    26  */
    27 
    28 /* !!! FIXME: can this target support hotplugging? */
    29 /* !!! FIXME: ...does SDL2 even support QNX? */
    30 
    31 #include "../../SDL_internal.h"
    32 
    33 #if SDL_AUDIO_DRIVER_QSA
    34 
    35 #include <errno.h>
    36 #include <unistd.h>
    37 #include <fcntl.h>
    38 #include <signal.h>
    39 #include <sys/types.h>
    40 #include <sys/time.h>
    41 #include <sched.h>
    42 #include <sys/select.h>
    43 #include <sys/neutrino.h>
    44 #include <sys/asoundlib.h>
    45 
    46 #include "SDL_timer.h"
    47 #include "SDL_audio.h"
    48 #include "../SDL_audio_c.h"
    49 #include "SDL_qsa_audio.h"
    50 
    51 /* default channel communication parameters */
    52 #define DEFAULT_CPARAMS_RATE   44100
    53 #define DEFAULT_CPARAMS_VOICES 1
    54 
    55 #define DEFAULT_CPARAMS_FRAG_SIZE 4096
    56 #define DEFAULT_CPARAMS_FRAGS_MIN 1
    57 #define DEFAULT_CPARAMS_FRAGS_MAX 1
    58 
    59 #define QSA_NO_WORKAROUNDS  0x00000000
    60 #define QSA_MMAP_WORKAROUND 0x00000001
    61 
    62 struct BuggyCards
    63 {
    64     char *cardname;
    65     unsigned long bugtype;
    66 };
    67 
    68 #define QSA_WA_CARDS             3
    69 #define QSA_MAX_CARD_NAME_LENGTH 33
    70 
    71 struct BuggyCards buggycards[QSA_WA_CARDS] = {
    72     {"Sound Blaster Live!", QSA_MMAP_WORKAROUND},
    73     {"Vortex 8820", QSA_MMAP_WORKAROUND},
    74     {"Vortex 8830", QSA_MMAP_WORKAROUND},
    75 };
    76 
    77 /* List of found devices */
    78 #define QSA_MAX_DEVICES       32
    79 #define QSA_MAX_NAME_LENGTH   81+16     /* Hardcoded in QSA, can't be changed */
    80 
    81 typedef struct _QSA_Device
    82 {
    83     char name[QSA_MAX_NAME_LENGTH];     /* Long audio device name for SDL  */
    84     int cardno;
    85     int deviceno;
    86 } QSA_Device;
    87 
    88 QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
    89 uint32_t qsa_playback_devices;
    90 
    91 QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
    92 uint32_t qsa_capture_devices;
    93 
    94 static SDL_INLINE int
    95 QSA_SetError(const char *fn, int status)
    96 {
    97     return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
    98 }
    99 
   100 /* card names check to apply the workarounds */
   101 static int
   102 QSA_CheckBuggyCards(_THIS, unsigned long checkfor)
   103 {
   104     char scardname[QSA_MAX_CARD_NAME_LENGTH];
   105     int it;
   106 
   107     if (snd_card_get_name
   108         (this->hidden->cardno, scardname, QSA_MAX_CARD_NAME_LENGTH - 1) < 0) {
   109         return 0;
   110     }
   111 
   112     for (it = 0; it < QSA_WA_CARDS; it++) {
   113         if (SDL_strcmp(buggycards[it].cardname, scardname) == 0) {
   114             if (buggycards[it].bugtype == checkfor) {
   115                 return 1;
   116             }
   117         }
   118     }
   119 
   120     return 0;
   121 }
   122 
   123 /* !!! FIXME: does this need to be here? Does the SDL version not work? */
   124 static void
   125 QSA_ThreadInit(_THIS)
   126 {
   127     struct sched_param param;
   128     int status;
   129 
   130     /* Increase default 10 priority to 25 to avoid jerky sound */
   131     status = SchedGet(0, 0, &param);
   132     param.sched_priority = param.sched_curpriority + 15;
   133     status = SchedSet(0, 0, SCHED_NOCHANGE, &param);
   134 }
   135 
   136 /* PCM channel parameters initialize function */
   137 static void
   138 QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
   139 {
   140     SDL_zerop(cpars);
   141     cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
   142     cpars->mode = SND_PCM_MODE_BLOCK;
   143     cpars->start_mode = SND_PCM_START_DATA;
   144     cpars->stop_mode = SND_PCM_STOP_STOP;
   145     cpars->format.format = SND_PCM_SFMT_S16_LE;
   146     cpars->format.interleave = 1;
   147     cpars->format.rate = DEFAULT_CPARAMS_RATE;
   148     cpars->format.voices = DEFAULT_CPARAMS_VOICES;
   149     cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
   150     cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
   151     cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
   152 }
   153 
   154 /* This function waits until it is possible to write a full sound buffer */
   155 static void
   156 QSA_WaitDevice(_THIS)
   157 {
   158     fd_set wfds;
   159     fd_set rfds;
   160     int selectret;
   161     struct timeval timeout;
   162 
   163     if (!this->hidden->iscapture) {
   164         FD_ZERO(&wfds);
   165         FD_SET(this->hidden->audio_fd, &wfds);
   166     } else {
   167         FD_ZERO(&rfds);
   168         FD_SET(this->hidden->audio_fd, &rfds);
   169     }
   170 
   171     do {
   172         /* Setup timeout for playing one fragment equal to 2 seconds          */
   173         /* If timeout occured than something wrong with hardware or driver    */
   174         /* For example, Vortex 8820 audio driver stucks on second DAC because */
   175         /* it doesn't exist !                                                 */
   176         timeout.tv_sec = 2;
   177         timeout.tv_usec = 0;
   178         this->hidden->timeout_on_wait = 0;
   179 
   180         if (!this->hidden->iscapture) {
   181             selectret =
   182                 select(this->hidden->audio_fd + 1, NULL, &wfds, NULL,
   183                        &timeout);
   184         } else {
   185             selectret =
   186                 select(this->hidden->audio_fd + 1, &rfds, NULL, NULL,
   187                        &timeout);
   188         }
   189 
   190         switch (selectret) {
   191         case -1:
   192             {
   193                 SDL_SetError("QSA: select() failed: %s", strerror(errno));
   194                 return;
   195             }
   196             break;
   197         case 0:
   198             {
   199                 SDL_SetError("QSA: timeout on buffer waiting occured");
   200                 this->hidden->timeout_on_wait = 1;
   201                 return;
   202             }
   203             break;
   204         default:
   205             {
   206                 if (!this->hidden->iscapture) {
   207                     if (FD_ISSET(this->hidden->audio_fd, &wfds)) {
   208                         return;
   209                     }
   210                 } else {
   211                     if (FD_ISSET(this->hidden->audio_fd, &rfds)) {
   212                         return;
   213                     }
   214                 }
   215             }
   216             break;
   217         }
   218     } while (1);
   219 }
   220 
   221 static void
   222 QSA_PlayDevice(_THIS)
   223 {
   224     snd_pcm_channel_status_t cstatus;
   225     int written;
   226     int status;
   227     int towrite;
   228     void *pcmbuffer;
   229 
   230     if (!SDL_AtomicGet(&this->enabled) || !this->hidden) {
   231         return;
   232     }
   233 
   234     towrite = this->spec.size;
   235     pcmbuffer = this->hidden->pcm_buf;
   236 
   237     /* Write the audio data, checking for EAGAIN (buffer full) and underrun */
   238     do {
   239         written =
   240             snd_pcm_plugin_write(this->hidden->audio_handle, pcmbuffer,
   241                                  towrite);
   242         if (written != towrite) {
   243             /* Check if samples playback got stuck somewhere in hardware or in */
   244             /* the audio device driver */
   245             if ((errno == EAGAIN) && (written == 0)) {
   246                 if (this->hidden->timeout_on_wait != 0) {
   247                     SDL_SetError("QSA: buffer playback timeout");
   248                     return;
   249                 }
   250             }
   251 
   252             /* Check for errors or conditions */
   253             if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
   254                 /* Let a little CPU time go by and try to write again */
   255                 SDL_Delay(1);
   256 
   257                 /* if we wrote some data */
   258                 towrite -= written;
   259                 pcmbuffer += written * this->spec.channels;
   260                 continue;
   261             } else {
   262                 if ((errno == EINVAL) || (errno == EIO)) {
   263                     SDL_zero(cstatus);
   264                     if (!this->hidden->iscapture) {
   265                         cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
   266                     } else {
   267                         cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
   268                     }
   269 
   270                     status =
   271                         snd_pcm_plugin_status(this->hidden->audio_handle,
   272                                               &cstatus);
   273                     if (status < 0) {
   274                         QSA_SetError("snd_pcm_plugin_status", status);
   275                         return;
   276                     }
   277 
   278                     if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
   279                         (cstatus.status == SND_PCM_STATUS_READY)) {
   280                         if (!this->hidden->iscapture) {
   281                             status =
   282                                 snd_pcm_plugin_prepare(this->hidden->
   283                                                        audio_handle,
   284                                                        SND_PCM_CHANNEL_PLAYBACK);
   285                         } else {
   286                             status =
   287                                 snd_pcm_plugin_prepare(this->hidden->
   288                                                        audio_handle,
   289                                                        SND_PCM_CHANNEL_CAPTURE);
   290                         }
   291                         if (status < 0) {
   292                             QSA_SetError("snd_pcm_plugin_prepare", status);
   293                             return;
   294                         }
   295                     }
   296                     continue;
   297                 } else {
   298                     return;
   299                 }
   300             }
   301         } else {
   302             /* we wrote all remaining data */
   303             towrite -= written;
   304             pcmbuffer += written * this->spec.channels;
   305         }
   306     } while ((towrite > 0) && SDL_AtomicGet(&this->enabled));
   307 
   308     /* If we couldn't write, assume fatal error for now */
   309     if (towrite != 0) {
   310         SDL_OpenedAudioDeviceDisconnected(this);
   311     }
   312 }
   313 
   314 static Uint8 *
   315 QSA_GetDeviceBuf(_THIS)
   316 {
   317     return this->hidden->pcm_buf;
   318 }
   319 
   320 static void
   321 QSA_CloseDevice(_THIS)
   322 {
   323     if (this->hidden->audio_handle != NULL) {
   324         if (!this->hidden->iscapture) {
   325             /* Finish playing available samples */
   326             snd_pcm_plugin_flush(this->hidden->audio_handle,
   327                                  SND_PCM_CHANNEL_PLAYBACK);
   328         } else {
   329             /* Cancel unread samples during capture */
   330             snd_pcm_plugin_flush(this->hidden->audio_handle,
   331                                  SND_PCM_CHANNEL_CAPTURE);
   332         }
   333         snd_pcm_close(this->hidden->audio_handle);
   334     }
   335 
   336     SDL_free(this->hidden->pcm_buf);
   337     SDL_free(this->hidden);
   338 }
   339 
   340 static int
   341 QSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   342 {
   343     const QSA_Device *device = (const QSA_Device *) handle;
   344     int status = 0;
   345     int format = 0;
   346     SDL_AudioFormat test_format = 0;
   347     int found = 0;
   348     snd_pcm_channel_setup_t csetup;
   349     snd_pcm_channel_params_t cparams;
   350 
   351     /* Initialize all variables that we clean on shutdown */
   352     this->hidden =
   353         (struct SDL_PrivateAudioData *) SDL_calloc(1,
   354                                                    (sizeof
   355                                                     (struct
   356                                                      SDL_PrivateAudioData)));
   357     if (this->hidden == NULL) {
   358         return SDL_OutOfMemory();
   359     }
   360 
   361     /* Initialize channel transfer parameters to default */
   362     QSA_InitAudioParams(&cparams);
   363 
   364     /* Initialize channel direction: capture or playback */
   365     this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
   366 
   367     if (device != NULL) {
   368         /* Open requested audio device */
   369         this->hidden->deviceno = device->deviceno;
   370         this->hidden->cardno = device->cardno;
   371         status = snd_pcm_open(&this->hidden->audio_handle,
   372                               device->cardno, device->deviceno,
   373                               iscapture ? SND_PCM_OPEN_PLAYBACK : SND_PCM_OPEN_CAPTURE);
   374     } else {
   375         /* Open system default audio device */
   376         status = snd_pcm_open_preferred(&this->hidden->audio_handle,
   377                                         &this->hidden->cardno,
   378                                         &this->hidden->deviceno,
   379                                         iscapture ? SND_PCM_OPEN_PLAYBACK : SND_PCM_OPEN_CAPTURE);
   380     }
   381 
   382     /* Check if requested device is opened */
   383     if (status < 0) {
   384         this->hidden->audio_handle = NULL;
   385         return QSA_SetError("snd_pcm_open", status);
   386     }
   387 
   388     if (!QSA_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) {
   389         /* Disable QSA MMAP plugin for buggy audio drivers */
   390         status =
   391             snd_pcm_plugin_set_disable(this->hidden->audio_handle,
   392                                        PLUGIN_DISABLE_MMAP);
   393         if (status < 0) {
   394             return QSA_SetError("snd_pcm_plugin_set_disable", status);
   395         }
   396     }
   397 
   398     /* Try for a closest match on audio format */
   399     format = 0;
   400     /* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */
   401     found = 0;
   402 
   403     for (test_format = SDL_FirstAudioFormat(this->spec.format); !found;) {
   404         /* if match found set format to equivalent QSA format */
   405         switch (test_format) {
   406         case AUDIO_U8:
   407             {
   408                 format = SND_PCM_SFMT_U8;
   409                 found = 1;
   410             }
   411             break;
   412         case AUDIO_S8:
   413             {
   414                 format = SND_PCM_SFMT_S8;
   415                 found = 1;
   416             }
   417             break;
   418         case AUDIO_S16LSB:
   419             {
   420                 format = SND_PCM_SFMT_S16_LE;
   421                 found = 1;
   422             }
   423             break;
   424         case AUDIO_S16MSB:
   425             {
   426                 format = SND_PCM_SFMT_S16_BE;
   427                 found = 1;
   428             }
   429             break;
   430         case AUDIO_U16LSB:
   431             {
   432                 format = SND_PCM_SFMT_U16_LE;
   433                 found = 1;
   434             }
   435             break;
   436         case AUDIO_U16MSB:
   437             {
   438                 format = SND_PCM_SFMT_U16_BE;
   439                 found = 1;
   440             }
   441             break;
   442         case AUDIO_S32LSB:
   443             {
   444                 format = SND_PCM_SFMT_S32_LE;
   445                 found = 1;
   446             }
   447             break;
   448         case AUDIO_S32MSB:
   449             {
   450                 format = SND_PCM_SFMT_S32_BE;
   451                 found = 1;
   452             }
   453             break;
   454         case AUDIO_F32LSB:
   455             {
   456                 format = SND_PCM_SFMT_FLOAT_LE;
   457                 found = 1;
   458             }
   459             break;
   460         case AUDIO_F32MSB:
   461             {
   462                 format = SND_PCM_SFMT_FLOAT_BE;
   463                 found = 1;
   464             }
   465             break;
   466         default:
   467             {
   468                 break;
   469             }
   470         }
   471 
   472         if (!found) {
   473             test_format = SDL_NextAudioFormat();
   474         }
   475     }
   476 
   477     /* assumes test_format not 0 on success */
   478     if (test_format == 0) {
   479         return SDL_SetError("QSA: Couldn't find any hardware audio formats");
   480     }
   481 
   482     this->spec.format = test_format;
   483 
   484     /* Set the audio format */
   485     cparams.format.format = format;
   486 
   487     /* Set mono/stereo/4ch/6ch/8ch audio */
   488     cparams.format.voices = this->spec.channels;
   489 
   490     /* Set rate */
   491     cparams.format.rate = this->spec.freq;
   492 
   493     /* Setup the transfer parameters according to cparams */
   494     status = snd_pcm_plugin_params(this->hidden->audio_handle, &cparams);
   495     if (status < 0) {
   496         return QSA_SetError("snd_pcm_channel_params", status);
   497     }
   498 
   499     /* Make sure channel is setup right one last time */
   500     SDL_zero(csetup);
   501     if (!this->hidden->iscapture) {
   502         csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
   503     } else {
   504         csetup.channel = SND_PCM_CHANNEL_CAPTURE;
   505     }
   506 
   507     /* Setup an audio channel */
   508     if (snd_pcm_plugin_setup(this->hidden->audio_handle, &csetup) < 0) {
   509         return SDL_SetError("QSA: Unable to setup channel");
   510     }
   511 
   512     /* Calculate the final parameters for this audio specification */
   513     SDL_CalculateAudioSpec(&this->spec);
   514 
   515     this->hidden->pcm_len = this->spec.size;
   516 
   517     if (this->hidden->pcm_len == 0) {
   518         this->hidden->pcm_len =
   519             csetup.buf.block.frag_size * this->spec.channels *
   520             (snd_pcm_format_width(format) / 8);
   521     }
   522 
   523     /*
   524      * Allocate memory to the audio buffer and initialize with silence
   525      *  (Note that buffer size must be a multiple of fragment size, so find
   526      *  closest multiple)
   527      */
   528     this->hidden->pcm_buf =
   529         (Uint8 *) SDL_malloc(this->hidden->pcm_len);
   530     if (this->hidden->pcm_buf == NULL) {
   531         return SDL_OutOfMemory();
   532     }
   533     SDL_memset(this->hidden->pcm_buf, this->spec.silence,
   534                this->hidden->pcm_len);
   535 
   536     /* get the file descriptor */
   537     if (!this->hidden->iscapture) {
   538         this->hidden->audio_fd =
   539             snd_pcm_file_descriptor(this->hidden->audio_handle,
   540                                     SND_PCM_CHANNEL_PLAYBACK);
   541     } else {
   542         this->hidden->audio_fd =
   543             snd_pcm_file_descriptor(this->hidden->audio_handle,
   544                                     SND_PCM_CHANNEL_CAPTURE);
   545     }
   546 
   547     if (this->hidden->audio_fd < 0) {
   548         return QSA_SetError("snd_pcm_file_descriptor", status);
   549     }
   550 
   551     /* Prepare an audio channel */
   552     if (!this->hidden->iscapture) {
   553         /* Prepare audio playback */
   554         status =
   555             snd_pcm_plugin_prepare(this->hidden->audio_handle,
   556                                    SND_PCM_CHANNEL_PLAYBACK);
   557     } else {
   558         /* Prepare audio capture */
   559         status =
   560             snd_pcm_plugin_prepare(this->hidden->audio_handle,
   561                                    SND_PCM_CHANNEL_CAPTURE);
   562     }
   563 
   564     if (status < 0) {
   565         return QSA_SetError("snd_pcm_plugin_prepare", status);
   566     }
   567 
   568     /* We're really ready to rock and roll. :-) */
   569     return 0;
   570 }
   571 
   572 static void
   573 QSA_DetectDevices(void)
   574 {
   575     uint32_t it;
   576     uint32_t cards;
   577     uint32_t devices;
   578     int32_t status;
   579 
   580     /* Detect amount of available devices       */
   581     /* this value can be changed in the runtime */
   582     cards = snd_cards();
   583 
   584     /* If io-audio manager is not running we will get 0 as number */
   585     /* of available audio devices                                 */
   586     if (cards == 0) {
   587         /* We have no any available audio devices */
   588         return;
   589     }
   590 
   591     /* !!! FIXME: code duplication */
   592     /* Find requested devices by type */
   593     {  /* output devices */
   594         /* Playback devices enumeration requested */
   595         for (it = 0; it < cards; it++) {
   596             devices = 0;
   597             do {
   598                 status =
   599                     snd_card_get_longname(it,
   600                                           qsa_playback_device
   601                                           [qsa_playback_devices].name,
   602                                           QSA_MAX_NAME_LENGTH);
   603                 if (status == EOK) {
   604                     snd_pcm_t *handle;
   605 
   606                     /* Add device number to device name */
   607                     sprintf(qsa_playback_device[qsa_playback_devices].name +
   608                             SDL_strlen(qsa_playback_device
   609                                        [qsa_playback_devices].name), " d%d",
   610                             devices);
   611 
   612                     /* Store associated card number id */
   613                     qsa_playback_device[qsa_playback_devices].cardno = it;
   614 
   615                     /* Check if this device id could play anything */
   616                     status =
   617                         snd_pcm_open(&handle, it, devices,
   618                                      SND_PCM_OPEN_PLAYBACK);
   619                     if (status == EOK) {
   620                         qsa_playback_device[qsa_playback_devices].deviceno =
   621                             devices;
   622                         status = snd_pcm_close(handle);
   623                         if (status == EOK) {
   624                             SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, &qsa_playback_device[qsa_playback_devices]);
   625                             qsa_playback_devices++;
   626                         }
   627                     } else {
   628                         /* Check if we got end of devices list */
   629                         if (status == -ENOENT) {
   630                             break;
   631                         }
   632                     }
   633                 } else {
   634                     break;
   635                 }
   636 
   637                 /* Check if we reached maximum devices count */
   638                 if (qsa_playback_devices >= QSA_MAX_DEVICES) {
   639                     break;
   640                 }
   641                 devices++;
   642             } while (1);
   643 
   644             /* Check if we reached maximum devices count */
   645             if (qsa_playback_devices >= QSA_MAX_DEVICES) {
   646                 break;
   647             }
   648         }
   649     }
   650 
   651     {  /* capture devices */
   652         /* Capture devices enumeration requested */
   653         for (it = 0; it < cards; it++) {
   654             devices = 0;
   655             do {
   656                 status =
   657                     snd_card_get_longname(it,
   658                                           qsa_capture_device
   659                                           [qsa_capture_devices].name,
   660                                           QSA_MAX_NAME_LENGTH);
   661                 if (status == EOK) {
   662                     snd_pcm_t *handle;
   663 
   664                     /* Add device number to device name */
   665                     sprintf(qsa_capture_device[qsa_capture_devices].name +
   666                             SDL_strlen(qsa_capture_device
   667                                        [qsa_capture_devices].name), " d%d",
   668                             devices);
   669 
   670                     /* Store associated card number id */
   671                     qsa_capture_device[qsa_capture_devices].cardno = it;
   672 
   673                     /* Check if this device id could play anything */
   674                     status =
   675                         snd_pcm_open(&handle, it, devices,
   676                                      SND_PCM_OPEN_CAPTURE);
   677                     if (status == EOK) {
   678                         qsa_capture_device[qsa_capture_devices].deviceno =
   679                             devices;
   680                         status = snd_pcm_close(handle);
   681                         if (status == EOK) {
   682                             SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, &qsa_capture_device[qsa_capture_devices]);
   683                             qsa_capture_devices++;
   684                         }
   685                     } else {
   686                         /* Check if we got end of devices list */
   687                         if (status == -ENOENT) {
   688                             break;
   689                         }
   690                     }
   691 
   692                     /* Check if we reached maximum devices count */
   693                     if (qsa_capture_devices >= QSA_MAX_DEVICES) {
   694                         break;
   695                     }
   696                 } else {
   697                     break;
   698                 }
   699                 devices++;
   700             } while (1);
   701 
   702             /* Check if we reached maximum devices count */
   703             if (qsa_capture_devices >= QSA_MAX_DEVICES) {
   704                 break;
   705             }
   706         }
   707     }
   708 }
   709 
   710 static void
   711 QSA_Deinitialize(void)
   712 {
   713     /* Clear devices array on shutdown */
   714     /* !!! FIXME: we zero these on init...any reason to do it here? */
   715     SDL_zero(qsa_playback_device);
   716     SDL_zero(qsa_capture_device);
   717     qsa_playback_devices = 0;
   718     qsa_capture_devices = 0;
   719 }
   720 
   721 static int
   722 QSA_Init(SDL_AudioDriverImpl * impl)
   723 {
   724     snd_pcm_t *handle = NULL;
   725     int32_t status = 0;
   726 
   727     /* Clear devices array */
   728     SDL_zero(qsa_playback_device);
   729     SDL_zero(qsa_capture_device);
   730     qsa_playback_devices = 0;
   731     qsa_capture_devices = 0;
   732 
   733     /* Set function pointers                                     */
   734     /* DeviceLock and DeviceUnlock functions are used default,   */
   735     /* provided by SDL, which uses pthread_mutex for lock/unlock */
   736     impl->DetectDevices = QSA_DetectDevices;
   737     impl->OpenDevice = QSA_OpenDevice;
   738     impl->ThreadInit = QSA_ThreadInit;
   739     impl->WaitDevice = QSA_WaitDevice;
   740     impl->PlayDevice = QSA_PlayDevice;
   741     impl->GetDeviceBuf = QSA_GetDeviceBuf;
   742     impl->CloseDevice = QSA_CloseDevice;
   743     impl->Deinitialize = QSA_Deinitialize;
   744     impl->LockDevice = NULL;
   745     impl->UnlockDevice = NULL;
   746 
   747     impl->OnlyHasDefaultOutputDevice = 0;
   748     impl->ProvidesOwnCallbackThread = 0;
   749     impl->SkipMixerLock = 0;
   750     impl->HasCaptureSupport = 1;
   751     impl->OnlyHasDefaultOutputDevice = 0;
   752     impl->OnlyHasDefaultCaptureDevice = 0;
   753 
   754     /* Check if io-audio manager is running or not */
   755     status = snd_cards();
   756     if (status == 0) {
   757         /* if no, return immediately */
   758         return 1;
   759     }
   760 
   761     return 1;   /* this audio target is available. */
   762 }
   763 
   764 AudioBootStrap QSAAUDIO_bootstrap = {
   765     "qsa", "QNX QSA Audio", QSA_Init, 0
   766 };
   767 
   768 #endif /* SDL_AUDIO_DRIVER_QSA */
   769 
   770 /* vi: set ts=4 sw=4 expandtab: */