"Fossies" - the Fresh Open Source Software Archive

Member "alsa-lib-1.1.9/src/pcm/pcm_iec958.c" (10 May 2019, 21129 Bytes) of package /linux/misc/alsa-lib-1.1.9.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pcm_iec958.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.1.5_vs_1.1.6.

    1 /**
    2  * \file pcm/pcm_iec958.c
    3  * \ingroup PCM_Plugins
    4  * \brief PCM IEC958 Subframe Conversion Plugin Interface
    5  * \author Takashi Iwai <tiwai@suse.de>
    6  * \date 2004
    7  */
    8 /*
    9  *  PCM - IEC958 Subframe Conversion Plugin
   10  *  Copyright (c) 2004 by Takashi Iwai <tiwai@suse.de>
   11  *
   12  *
   13  *   This library is free software; you can redistribute it and/or modify
   14  *   it under the terms of the GNU Lesser General Public License as
   15  *   published by the Free Software Foundation; either version 2.1 of
   16  *   the License, or (at your option) any later version.
   17  *
   18  *   This program is distributed in the hope that it will be useful,
   19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   21  *   GNU Lesser General Public License for more details.
   22  *
   23  *   You should have received a copy of the GNU Lesser General Public
   24  *   License along with this library; if not, write to the Free Software
   25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   26  *
   27  */
   28   
   29 #include "bswap.h"
   30 #include "pcm_local.h"
   31 #include "pcm_plugin.h"
   32 
   33 #include "plugin_ops.h"
   34 
   35 #ifndef PIC
   36 /* entry for static linking */
   37 const char *_snd_module_pcm_iec958 = "";
   38 #endif
   39 
   40 /*
   41  */
   42 
   43 #ifndef DOC_HIDDEN
   44 
   45 typedef struct snd_pcm_iec958 snd_pcm_iec958_t;
   46 
   47 typedef void (*iec958_f)(snd_pcm_iec958_t *iec,
   48              const snd_pcm_channel_area_t *dst_areas,
   49              snd_pcm_uframes_t dst_offset,
   50              const snd_pcm_channel_area_t *src_areas,
   51              snd_pcm_uframes_t src_offset,
   52              unsigned int channels, snd_pcm_uframes_t frames);
   53 
   54 struct snd_pcm_iec958 {
   55     /* This field need to be the first */
   56     snd_pcm_plugin_t plug;
   57     unsigned int getput_idx;
   58     iec958_f func;
   59     snd_pcm_format_t sformat;
   60     snd_pcm_format_t format;
   61     unsigned int counter;
   62     unsigned char status[24];
   63     unsigned int byteswap;
   64     unsigned char preamble[3];  /* B/M/W or Z/X/Y */
   65     snd_pcm_fast_ops_t fops;
   66 };
   67 
   68 enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y };
   69 
   70 #endif /* DOC_HIDDEN */
   71 
   72 /*
   73  * Determine parity for time slots 4 upto 30
   74  * to be sure that bit 4 upt 31 will carry
   75  * an even number of ones and zeros.
   76  */
   77 static unsigned int iec958_parity(unsigned int data)
   78 {
   79     unsigned int parity;
   80     int bit;
   81 
   82     data >>= 4;     /* start from bit 4 */
   83     parity = 0;
   84     for (bit = 4; bit <= 30; bit++) {
   85         if (data & 1)
   86             parity++;
   87         data >>= 1;
   88     }
   89     return (parity & 1);
   90 }
   91 
   92 /*
   93  * Compose 32bit IEC958 subframe, two sub frames
   94  * build one frame with two channels.
   95  *
   96  * bit 0-3  = preamble
   97  *     4-7  = AUX (=0)
   98  *     8-27 = data (12-27 for 16bit, 8-27 for 20bit, and 24bit without AUX)
   99  *     28   = validity (0 for valid data, else 'in error')
  100  *     29   = user data (0)
  101  *     30   = channel status (24 bytes for 192 frames)
  102  *     31   = parity
  103  */
  104 
  105 static inline uint32_t iec958_subframe(snd_pcm_iec958_t *iec, uint32_t data, int channel)
  106 {
  107     unsigned int byte = iec->counter >> 3;
  108     unsigned int mask = 1 << (iec->counter - (byte << 3));
  109 
  110     /* bit 4-27 */
  111     data >>= 4;
  112     data &= ~0xf;
  113 
  114     /* set IEC status bits (up to 192 bits) */
  115     if (iec->status[byte] & mask)
  116         data |= 0x40000000;
  117 
  118     if (iec958_parity(data))    /* parity bit 4-30 */
  119         data |= 0x80000000;
  120 
  121     /* Preamble */
  122     if (channel)
  123         data |= iec->preamble[PREAMBLE_Y];  /* odd sub frame, 'Y' */
  124     else if (! iec->counter)
  125         data |= iec->preamble[PREAMBLE_Z];  /* Block start, 'Z' */
  126     else
  127         data |= iec->preamble[PREAMBLE_X];  /* even sub frame, 'X' */
  128 
  129     if (iec->byteswap)
  130         data = bswap_32(data);
  131 
  132     return data;
  133 }
  134 
  135 static inline int32_t iec958_to_s32(snd_pcm_iec958_t *iec, uint32_t data)
  136 {
  137     if (iec->byteswap)
  138         data = bswap_32(data);
  139     data &= ~0xf;
  140     data <<= 4;
  141     return (int32_t)data;
  142 }
  143 
  144 #ifndef DOC_HIDDEN
  145 static void snd_pcm_iec958_decode(snd_pcm_iec958_t *iec,
  146                   const snd_pcm_channel_area_t *dst_areas,
  147                   snd_pcm_uframes_t dst_offset,
  148                   const snd_pcm_channel_area_t *src_areas,
  149                   snd_pcm_uframes_t src_offset,
  150                   unsigned int channels, snd_pcm_uframes_t frames)
  151 {
  152 #define PUT32_LABELS
  153 #include "plugin_ops.h"
  154 #undef PUT32_LABELS
  155     void *put = put32_labels[iec->getput_idx];
  156     unsigned int channel;
  157     for (channel = 0; channel < channels; ++channel) {
  158         const uint32_t *src;
  159         char *dst;
  160         int src_step, dst_step;
  161         snd_pcm_uframes_t frames1;
  162         const snd_pcm_channel_area_t *src_area = &src_areas[channel];
  163         const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
  164         src = snd_pcm_channel_area_addr(src_area, src_offset);
  165         dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
  166         src_step = snd_pcm_channel_area_step(src_area) / sizeof(uint32_t);
  167         dst_step = snd_pcm_channel_area_step(dst_area);
  168         frames1 = frames;
  169         while (frames1-- > 0) {
  170             int32_t sample = iec958_to_s32(iec, *src);
  171             goto *put;
  172 #define PUT32_END after
  173 #include "plugin_ops.h"
  174 #undef PUT32_END
  175         after:
  176             src += src_step;
  177             dst += dst_step;
  178         }
  179     }
  180 }
  181 
  182 static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
  183                   const snd_pcm_channel_area_t *dst_areas,
  184                   snd_pcm_uframes_t dst_offset,
  185                   const snd_pcm_channel_area_t *src_areas,
  186                   snd_pcm_uframes_t src_offset,
  187                   unsigned int channels, snd_pcm_uframes_t frames)
  188 {
  189 #define GET32_LABELS
  190 #include "plugin_ops.h"
  191 #undef GET32_LABELS
  192     void *get = get32_labels[iec->getput_idx];
  193     unsigned int channel;
  194     int32_t sample = 0;
  195     int counter = iec->counter;
  196     for (channel = 0; channel < channels; ++channel) {
  197         const char *src;
  198         uint32_t *dst;
  199         int src_step, dst_step;
  200         snd_pcm_uframes_t frames1;
  201         const snd_pcm_channel_area_t *src_area = &src_areas[channel];
  202         const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
  203         src = snd_pcm_channel_area_addr(src_area, src_offset);
  204         dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
  205         src_step = snd_pcm_channel_area_step(src_area);
  206         dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(uint32_t);
  207         frames1 = frames;
  208         iec->counter = counter;
  209         while (frames1-- > 0) {
  210             goto *get;
  211 #define GET32_END after
  212 #include "plugin_ops.h"
  213 #undef GET32_END
  214         after:
  215             sample = iec958_subframe(iec, sample, channel);
  216             // fprintf(stderr, "%d:%08x\n", frames1, sample);
  217             *dst = sample;
  218             src += src_step;
  219             dst += dst_step;
  220             iec->counter++;
  221             iec->counter %= 192;
  222         }
  223     }
  224 }
  225 #endif /* DOC_HIDDEN */
  226 
  227 static int snd_pcm_iec958_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
  228 {
  229     snd_pcm_iec958_t *iec = pcm->private_data;
  230     int err;
  231     snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
  232     err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
  233                      &access_mask);
  234     if (err < 0)
  235         return err;
  236     if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
  237         iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
  238         snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
  239         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
  240                          &format_mask);
  241     } else {
  242         snd_pcm_format_mask_t format_mask = {
  243             { (1U << SND_PCM_FORMAT_IEC958_SUBFRAME_LE) |
  244               (1U << SND_PCM_FORMAT_IEC958_SUBFRAME_BE) }
  245         };
  246         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
  247                          &format_mask);
  248     }
  249     if (err < 0)
  250         return err;
  251     err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
  252     if (err < 0)
  253         return err;
  254     params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
  255     return 0;
  256 }
  257 
  258 static int snd_pcm_iec958_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
  259 {
  260     snd_pcm_iec958_t *iec = pcm->private_data;
  261     snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
  262     _snd_pcm_hw_params_any(sparams);
  263     _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
  264                    &saccess_mask);
  265     _snd_pcm_hw_params_set_format(sparams, iec->sformat);
  266     _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
  267     return 0;
  268 }
  269 
  270 static int snd_pcm_iec958_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
  271                         snd_pcm_hw_params_t *sparams)
  272 {
  273     int err;
  274     unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
  275                   SND_PCM_HW_PARBIT_RATE |
  276                   SND_PCM_HW_PARBIT_PERIOD_SIZE |
  277                   SND_PCM_HW_PARBIT_BUFFER_SIZE |
  278                   SND_PCM_HW_PARBIT_PERIODS |
  279                   SND_PCM_HW_PARBIT_PERIOD_TIME |
  280                   SND_PCM_HW_PARBIT_BUFFER_TIME |
  281                   SND_PCM_HW_PARBIT_TICK_TIME);
  282     err = _snd_pcm_hw_params_refine(sparams, links, params);
  283     if (err < 0)
  284         return err;
  285     return 0;
  286 }
  287     
  288 static int snd_pcm_iec958_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
  289                         snd_pcm_hw_params_t *sparams)
  290 {
  291     int err;
  292     unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
  293                   SND_PCM_HW_PARBIT_RATE |
  294                   SND_PCM_HW_PARBIT_PERIOD_SIZE |
  295                   SND_PCM_HW_PARBIT_BUFFER_SIZE |
  296                   SND_PCM_HW_PARBIT_PERIODS |
  297                   SND_PCM_HW_PARBIT_PERIOD_TIME |
  298                   SND_PCM_HW_PARBIT_BUFFER_TIME |
  299                   SND_PCM_HW_PARBIT_TICK_TIME);
  300     err = _snd_pcm_hw_params_refine(params, links, sparams);
  301     if (err < 0)
  302         return err;
  303     return 0;
  304 }
  305 
  306 static int snd_pcm_iec958_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
  307 {
  308     return snd_pcm_hw_refine_slave(pcm, params,
  309                        snd_pcm_iec958_hw_refine_cprepare,
  310                        snd_pcm_iec958_hw_refine_cchange,
  311                        snd_pcm_iec958_hw_refine_sprepare,
  312                        snd_pcm_iec958_hw_refine_schange,
  313                        snd_pcm_generic_hw_refine);
  314 }
  315 
  316 static int snd_pcm_iec958_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
  317 {
  318     snd_pcm_iec958_t *iec = pcm->private_data;
  319     snd_pcm_format_t format;
  320     int err = snd_pcm_hw_params_slave(pcm, params,
  321                       snd_pcm_iec958_hw_refine_cchange,
  322                       snd_pcm_iec958_hw_refine_sprepare,
  323                       snd_pcm_iec958_hw_refine_schange,
  324                       snd_pcm_generic_hw_params);
  325     if (err < 0)
  326         return err;
  327 
  328     err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
  329     if (err < 0)
  330         return err;
  331 
  332     iec->format = format;
  333     if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
  334         if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
  335             iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
  336             iec->getput_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S32);
  337             iec->func = snd_pcm_iec958_encode;
  338             iec->byteswap = iec->sformat != SND_PCM_FORMAT_IEC958_SUBFRAME;
  339         } else {
  340             iec->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, iec->sformat);
  341             iec->func = snd_pcm_iec958_decode;
  342             iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME;
  343         }
  344     } else {
  345         if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
  346             iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
  347             iec->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, format);
  348             iec->func = snd_pcm_iec958_decode;
  349             iec->byteswap = iec->sformat != SND_PCM_FORMAT_IEC958_SUBFRAME;
  350         } else {
  351             iec->getput_idx = snd_pcm_linear_get_index(iec->sformat, SND_PCM_FORMAT_S32);
  352             iec->func = snd_pcm_iec958_encode;
  353             iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME;
  354         }
  355     }
  356     /* FIXME: needs to adjust status_bits according to the format
  357      *        and sample rate
  358      */
  359     return 0;
  360 }
  361 
  362 static snd_pcm_uframes_t
  363 snd_pcm_iec958_write_areas(snd_pcm_t *pcm,
  364                const snd_pcm_channel_area_t *areas,
  365                snd_pcm_uframes_t offset,
  366                snd_pcm_uframes_t size,
  367                const snd_pcm_channel_area_t *slave_areas,
  368                snd_pcm_uframes_t slave_offset,
  369                snd_pcm_uframes_t *slave_sizep)
  370 {
  371     snd_pcm_iec958_t *iec = pcm->private_data;
  372     if (size > *slave_sizep)
  373         size = *slave_sizep;
  374     iec->func(iec, slave_areas, slave_offset,
  375           areas, offset, 
  376           pcm->channels, size);
  377     *slave_sizep = size;
  378     return size;
  379 }
  380 
  381 static snd_pcm_uframes_t
  382 snd_pcm_iec958_read_areas(snd_pcm_t *pcm,
  383               const snd_pcm_channel_area_t *areas,
  384               snd_pcm_uframes_t offset,
  385               snd_pcm_uframes_t size,
  386               const snd_pcm_channel_area_t *slave_areas,
  387               snd_pcm_uframes_t slave_offset,
  388               snd_pcm_uframes_t *slave_sizep)
  389 {
  390     snd_pcm_iec958_t *iec = pcm->private_data;
  391     if (size > *slave_sizep)
  392         size = *slave_sizep;
  393     iec->func(iec, areas, offset, 
  394           slave_areas, slave_offset,
  395           pcm->channels, size);
  396     *slave_sizep = size;
  397     return size;
  398 }
  399 
  400 static int snd_pcm_iec958_init(snd_pcm_t *pcm)
  401 {
  402     snd_pcm_iec958_t *iec = pcm->private_data;
  403     iec->counter = 0;
  404     return 0;
  405 }
  406 
  407 static void snd_pcm_iec958_dump(snd_pcm_t *pcm, snd_output_t *out)
  408 {
  409     snd_pcm_iec958_t *iec = pcm->private_data;
  410     snd_output_printf(out, "IEC958 subframe conversion PCM (%s)\n", 
  411               snd_pcm_format_name(iec->sformat));
  412     if (pcm->setup) {
  413         snd_output_printf(out, "Its setup is:\n");
  414         snd_pcm_dump_setup(pcm, out);
  415     }
  416     snd_output_printf(out, "Slave: ");
  417     snd_pcm_dump(iec->plug.gen.slave, out);
  418 }
  419 
  420 static snd_pcm_sframes_t snd_pcm_iec958_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
  421 {
  422     unsigned int counter_decrement;
  423     snd_pcm_iec958_t *iec = pcm->private_data;
  424     snd_pcm_sframes_t result = snd_pcm_plugin_rewind(pcm, frames);
  425     if (result <= 0)
  426         return result;
  427 
  428     counter_decrement = result % 192;
  429     iec->counter += 192 - counter_decrement;
  430     iec->counter %= 192;
  431     return result;
  432 }
  433 
  434 static snd_pcm_sframes_t snd_pcm_iec958_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
  435 
  436 {
  437     unsigned int counter_increment;
  438     snd_pcm_iec958_t *iec = pcm->private_data;
  439     snd_pcm_sframes_t result = snd_pcm_plugin_rewind(pcm, frames);
  440     if (result <= 0)
  441         return result;
  442 
  443     counter_increment = result % 192;
  444     iec->counter += counter_increment;
  445     iec->counter %= 192;
  446     return result;
  447 }
  448 
  449 static const snd_pcm_ops_t snd_pcm_iec958_ops = {
  450     .close = snd_pcm_generic_close,
  451     .info = snd_pcm_generic_info,
  452     .hw_refine = snd_pcm_iec958_hw_refine,
  453     .hw_params = snd_pcm_iec958_hw_params,
  454     .hw_free = snd_pcm_generic_hw_free,
  455     .sw_params = snd_pcm_generic_sw_params,
  456     .channel_info = snd_pcm_generic_channel_info,
  457     .dump = snd_pcm_iec958_dump,
  458     .nonblock = snd_pcm_generic_nonblock,
  459     .async = snd_pcm_generic_async,
  460     .mmap = snd_pcm_generic_mmap,
  461     .munmap = snd_pcm_generic_munmap,
  462     .query_chmaps = snd_pcm_generic_query_chmaps,
  463     .get_chmap = snd_pcm_generic_get_chmap,
  464     .set_chmap = snd_pcm_generic_set_chmap,
  465 };
  466 
  467 /**
  468  * \brief Creates a new IEC958 subframe conversion PCM
  469  * \param pcmp Returns created PCM handle
  470  * \param name Name of PCM
  471  * \param sformat Slave (destination) format
  472  * \param slave Slave PCM handle
  473  * \param close_slave When set, the slave PCM handle is closed with copy PCM
  474  * \param status_bits The IEC958 status bits
  475  * \param preamble_vals The preamble byte values
  476  * \retval zero on success otherwise a negative error code
  477  * \warning Using of this function might be dangerous in the sense
  478  *          of compatibility reasons. The prototype might be freely
  479  *          changed in future.
  480  */           
  481 int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
  482             snd_pcm_t *slave, int close_slave,
  483             const unsigned char *status_bits,
  484             const unsigned char *preamble_vals)
  485 {
  486     snd_pcm_t *pcm;
  487     snd_pcm_iec958_t *iec;
  488     int err;
  489     static const unsigned char default_status_bits[] = {
  490         IEC958_AES0_CON_EMPHASIS_NONE,
  491         IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
  492         0,
  493         IEC958_AES3_CON_FS_48000
  494     };
  495 
  496     assert(pcmp && slave);
  497     if (snd_pcm_format_linear(sformat) != 1 &&
  498         sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_LE &&
  499         sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_BE)
  500         return -EINVAL;
  501     iec = calloc(1, sizeof(snd_pcm_iec958_t));
  502     if (!iec) {
  503         return -ENOMEM;
  504     }
  505     snd_pcm_plugin_init(&iec->plug);
  506     iec->sformat = sformat;
  507     iec->plug.read = snd_pcm_iec958_read_areas;
  508     iec->plug.write = snd_pcm_iec958_write_areas;
  509     iec->plug.init = snd_pcm_iec958_init;
  510     iec->plug.undo_read = snd_pcm_plugin_undo_read_generic;
  511     iec->plug.undo_write = snd_pcm_plugin_undo_write_generic;
  512     iec->plug.gen.slave = slave;
  513     iec->plug.gen.close_slave = close_slave;
  514 
  515     if (status_bits)
  516         memcpy(iec->status, status_bits, sizeof(iec->status));
  517     else
  518         memcpy(iec->status, default_status_bits, sizeof(default_status_bits));
  519 
  520     memcpy(iec->preamble, preamble_vals, 3);
  521 
  522     err = snd_pcm_new(&pcm, SND_PCM_TYPE_IEC958, name, slave->stream, slave->mode);
  523     if (err < 0) {
  524         free(iec);
  525         return err;
  526     }
  527     pcm->ops = &snd_pcm_iec958_ops;
  528 
  529     iec->fops = snd_pcm_plugin_fast_ops;
  530     iec->fops.rewind = snd_pcm_iec958_rewind;
  531     iec->fops.forward = snd_pcm_iec958_forward;
  532     pcm->fast_ops = &iec->fops;
  533 
  534     pcm->private_data = iec;
  535     pcm->poll_fd = slave->poll_fd;
  536     pcm->poll_events = slave->poll_events;
  537     pcm->tstamp_type = slave->tstamp_type;
  538     snd_pcm_set_hw_ptr(pcm, &iec->plug.hw_ptr, -1, 0);
  539     snd_pcm_set_appl_ptr(pcm, &iec->plug.appl_ptr, -1, 0);
  540     *pcmp = pcm;
  541 
  542     return 0;
  543 }
  544 
  545 /*! \page pcm_plugins
  546 
  547 \section pcm_plugins_iec958 Plugin: IEC958
  548 
  549 This plugin converts 32bit IEC958 subframe samples to linear, or linear to
  550 32bit IEC958 subframe samples.
  551 
  552 \code
  553 pcm.name {
  554         type iec958             # IEC958 subframe conversion PCM
  555         slave STR               # Slave name
  556         # or
  557         slave {                 # Slave definition
  558                 pcm STR         # Slave PCM name
  559                 # or
  560                 pcm { }         # Slave PCM definition
  561         }
  562     [status status-bytes]   # IEC958 status bits (given in byte array)
  563     # IEC958 preamble bits definitions
  564     # B/M/W or Z/X/Y, B = block start, M = even subframe, W = odd subframe
  565     # As default, Z = 0x08, Y = 0x04, X = 0x02
  566     [preamble.z or preamble.b val]
  567     [preamble.x or preamble.m val]
  568     [preamble.y or preamble.w val]
  569 }
  570 \endcode
  571 
  572 \subsection pcm_plugins_iec958_funcref Function reference
  573 
  574 <UL>
  575   <LI>snd_pcm_iec958_open()
  576   <LI>_snd_pcm_iec958_open()
  577 </UL>
  578 
  579 */
  580 
  581 /**
  582  * \brief Creates a new IEC958 subframe conversion PCM
  583  * \param pcmp Returns created PCM handle
  584  * \param name Name of PCM
  585  * \param root Root configuration node
  586  * \param conf Configuration node with copy PCM description
  587  * \param stream Stream type
  588  * \param mode Stream mode
  589  * \retval zero on success otherwise a negative error code
  590  * \warning Using of this function might be dangerous in the sense
  591  *          of compatibility reasons. The prototype might be freely
  592  *          changed in future.
  593  */
  594 int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
  595              snd_config_t *root, snd_config_t *conf, 
  596              snd_pcm_stream_t stream, int mode)
  597 {
  598     snd_config_iterator_t i, next;
  599     int err;
  600     snd_pcm_t *spcm;
  601     snd_config_t *slave = NULL, *sconf;
  602     snd_config_t *status = NULL, *preamble = NULL;
  603     snd_pcm_format_t sformat;
  604     unsigned char status_bits[24];
  605     unsigned char preamble_vals[3] = {
  606         0x08, 0x02, 0x04 /* Z, X, Y */
  607     };
  608 
  609     snd_config_for_each(i, next, conf) {
  610         snd_config_t *n = snd_config_iterator_entry(i);
  611         const char *id;
  612         if (snd_config_get_id(n, &id) < 0)
  613             continue;
  614         if (snd_pcm_conf_generic_id(id))
  615             continue;
  616         if (strcmp(id, "slave") == 0) {
  617             slave = n;
  618             continue;
  619         }
  620         if (strcmp(id, "status") == 0) {
  621             if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
  622                 SNDERR("Invalid type for %s", id);
  623                 return -EINVAL;
  624             }
  625             status = n;
  626             continue;
  627         }
  628         if (strcmp(id, "preamble") == 0) {
  629             if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
  630                 SNDERR("Invalid type for %s", id);
  631                 return -EINVAL;
  632             }
  633             preamble = n;
  634             continue;
  635         }
  636         SNDERR("Unknown field %s", id);
  637         return -EINVAL;
  638     }
  639     memset(status_bits, 0, sizeof(status_bits));
  640     if (status) {
  641         snd_config_iterator_t i, inext;
  642         int bytes = 0;
  643         snd_config_for_each(i, inext, status) {
  644             long val;
  645             snd_config_t *n = snd_config_iterator_entry(i);
  646             if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
  647                 SNDERR("invalid IEC958 status bits");
  648                 return -EINVAL;
  649             }
  650             err = snd_config_get_integer(n, &val);
  651             if (err < 0) {
  652                 SNDERR("invalid IEC958 status bits");
  653                 return err;
  654             }
  655             status_bits[bytes] = val;
  656             bytes++;
  657             if (bytes >= (int)sizeof(status_bits))
  658                 break;
  659         }
  660         // fprintf(stderr, "STATUS bits: %02x %02x %02x %02x\n", status_bits[0], status_bits[1], status_bits[2], status_bits[3]);
  661     }
  662     if (preamble) {
  663         snd_config_iterator_t i, inext;
  664         snd_config_for_each(i, inext, preamble) {
  665             long val;
  666             snd_config_t *n = snd_config_iterator_entry(i);
  667             const char *id;
  668             int idx;
  669             if (snd_config_get_id(n, &id) < 0)
  670                 continue;
  671             if (strcmp(id, "b") == 0 || strcmp(id, "z") == 0)
  672                 idx = PREAMBLE_Z;
  673             else if (strcmp(id, "m") == 0 || strcmp(id, "x") == 0)
  674                 idx = PREAMBLE_X;
  675             else if (strcmp(id, "w") == 0 || strcmp(id, "y") == 0)
  676                 idx = PREAMBLE_Y;
  677             else {
  678                 SNDERR("invalid IEC958 preamble type %s", id);
  679                 return -EINVAL;
  680             }
  681             err = snd_config_get_integer(n, &val);
  682             if (err < 0) {
  683                 SNDERR("invalid IEC958 preamble value");
  684                 return err;
  685             }
  686             preamble_vals[idx] = val;
  687         }
  688     }
  689     if (!slave) {
  690         SNDERR("slave is not defined");
  691         return -EINVAL;
  692     }
  693     err = snd_pcm_slave_conf(root, slave, &sconf, 1,
  694                  SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
  695     if (err < 0)
  696         return err;
  697     if (snd_pcm_format_linear(sformat) != 1 &&
  698         sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_LE &&
  699         sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
  700             snd_config_delete(sconf);
  701         SNDERR("invalid slave format");
  702         return -EINVAL;
  703     }
  704     err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
  705     snd_config_delete(sconf);
  706     if (err < 0)
  707         return err;
  708     err = snd_pcm_iec958_open(pcmp, name, sformat, spcm, 1,
  709                   status ? status_bits : NULL,
  710                   preamble_vals);
  711     if (err < 0)
  712         snd_pcm_close(spcm);
  713     return err;
  714 }
  715 #ifndef DOC_HIDDEN
  716 SND_DLSYM_BUILD_VERSION(_snd_pcm_iec958_open, SND_PCM_DLSYM_VERSION);
  717 #endif