static void set_switch_boolean( int val );
 static void set_switch_integer( int val );
+static void set_switch_iec958ocs_begin( void );
+static void set_switch_iec958ocs( int idx, unsigned short val, unsigned short mask );
 
        /* local variables */
 
        /* other keywords */
 %token L_SOUNDCARD L_MIXER L_CHANNEL L_STEREO L_MONO L_SWITCH L_RAWDATA
 %token L_CONTROL L_PCM L_RAWMIDI L_PLAYBACK L_RECORD L_OUTPUT L_INPUT
+%token L_IEC958OCS L_3D L_RESET L_USER L_VALID L_DATA L_PROTECTED L_PRE2
+%token L_FSLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE
 
 %type <b_value> boolean
 %type <i_value> integer
 switch : L_TRUE                { set_switch_boolean( 1 ); }
        | L_FALSE               { set_switch_boolean( 0 ); }
        | L_INTEGER             { set_switch_integer( $1 ); }
+       | L_IEC958OCS '(' { set_switch_iec958ocs_begin(); } iec958ocs ')'
        | error                 { yyerror( "unknown keyword in switch() data parameter" ); }
        ;
 
+iec958ocs : iec958ocs1
+       | iec958ocs iec958ocs1
+       ;
+
+iec958ocs1 : L_ENABLE          { set_switch_iec958ocs( 0, 1, 0 ); }
+       | L_DISABLE             { set_switch_iec958ocs( 0, 0, 0 ); }
+       | L_3D                  { set_switch_iec958ocs( 4, 0x2000, ~0x2000 ); }
+       | L_RESET               { set_switch_iec958ocs( 4, 0x0040, ~0x0040 ); }
+       | L_USER                { set_switch_iec958ocs( 4, 0x0020, ~0x0020 ); }
+       | L_VALID               { set_switch_iec958ocs( 4, 0x0010, ~0x0010 ); }
+       | L_DATA                { set_switch_iec958ocs( 5, 0x0002, ~0x0002 ); }
+       | L_PRE2                { set_switch_iec958ocs( 5, 0x0008, ~0x0018 ); }
+       | L_FSLOCK              { set_switch_iec958ocs( 5, 0x0020, ~0x0020 ); }
+       | L_TYPE '(' integer ')' { set_switch_iec958ocs( 5, ($3 & 0x7f) << 6, ~(0x7f<<6) ); }
+       | L_GSTATUS             { set_switch_iec958ocs( 5, 0x2000, ~0x2000 ); }
+       | error                 { yyerror( "unknown keyword in iec958ocs1() arguments" ); }
+       ;
+
 boolean        : L_TRUE                { $$ = 1; }
        | L_FALSE               { $$ = 0; }
        | error                 { yyerror( "unknown boolean value" ); }
   if ( memcmp( &sw -> value, &xx, sizeof(xx) ) ) *Xswitchchange = 1;
   memcpy( &sw -> value, &xx, sizeof(xx) );
 }
+
+static void set_switch_iec958ocs_begin( void )
+{
+  /* ok.. this is a little bit wrong, but at these times are all switches same */
+  snd_ctl_switch_t *sw = (snd_ctl_switch_t *)Xswitch;
+
+  if ( Xswitchtype != SWITCH_MIXER || sw -> type != SND_MIXER_SW_TYPE_BOOLEAN ||
+       !strcmp( sw -> name, SND_MIXER_SW_IEC958OUT ) )
+    yyerror( "Switch '%s' cannot store IEC958 information for Cirrus Logic chips...", sw -> name );
+  if ( sw -> value.data32[1] != (('C'<<8)|'S') )
+    yyerror( "Switch '%s' doesn't have Cirrus Logic signature!!!", sw -> name );
+  sw -> value.enable = 0;
+  sw -> value.data16[4] = 0x0000;
+  sw -> value.data16[5] = 0x0000;
+}
+
+static void set_switch_iec958ocs( int idx, unsigned short val, unsigned short mask )
+{
+  /* ok.. this is a little bit wrong, but at these times are all switches same */
+  snd_ctl_switch_t *sw = (snd_ctl_switch_t *)Xswitch;
+
+  if ( idx == 0 ) {
+    sw -> value.enable = val ? 1 : 0;
+    return;
+  }
+  sw -> value.data16[ idx ] &= ~mask;
+  sw -> value.data16[ idx ] |= val;
+}
 
   };
   char *s, v[16];
   struct data *pdata = (struct data *)data;
-  int idx;
+  int idx, first, switchok = 0;
+  const char *space = "    ";
   
   v[0] = '\0';
   switch ( type ) {
     default:
       s = "unknown";
   }
-  fprintf( out, "    ; Type is '%s'.\n", s );
+  fprintf( out, "%s; Type is '%s'.\n", space, s );
   if ( low != 0 || high != 0 )
-    fprintf( out, "    ; Accepted switch range is from %ui to %ui.\n", low, high );
-  fprintf( out, "    switch( \"%s\", %s", name, v );
-  if ( type < 0 || type > SND_CTL_SW_TYPE_DWORD ) {
-    /* TODO: some well known types should be verbose */
-    fprintf( out, " rawdata( " );
-    for ( idx = 0; idx < 31; idx++ ) {
-      fprintf( out, "@%02x:", pdata -> data8[idx] );
-    }
-    fprintf( out, "%02x@ )\n", pdata -> data8[31] );
+    fprintf( out, "%s; Accepted switch range is from %ui to %ui.\n", space, low, high );
+  if ( interface == SND_INTERFACE_CONTROL && type == SND_CTL_SW_TYPE_WORD &&
+       !strcmp( name, SND_CTL_SW_JOYSTICK_ADDRESS ) ) {
+    for ( idx = 1, first = 1; idx < 16; idx++ ) {
+      if ( pdata -> data16[idx] ) {
+        if ( first ) {
+          fprintf( out, "%s; Available addresses - 0x%x", space, pdata -> data16[idx] );
+          first = 0;
+        } else {
+          fprintf( out, ", 0x%x", pdata -> data16[idx] );
+        }
+      }
+    }
+    if ( !first ) fprintf( out, "\n" );
+  }
+  fprintf( out, "%sswitch( \"%s\", ", space, name );
+  if ( interface == SND_INTERFACE_MIXER && type == SND_MIXER_SW_TYPE_BOOLEAN &&
+       !strcmp( name, SND_MIXER_SW_IEC958OUT ) ) {
+    if ( pdata -> data16[0] == (('C'<<8)|'S') ) {      /* Cirrus Crystal */
+      switchok = 0;
+      fprintf( out, "iec958ocs( %s", pdata -> enable ? "enable" : "disable" );
+      if ( pdata -> data16[4] & 0x2000 ) fprintf( out, " 3d" );
+      if ( pdata -> data16[4] & 0x0040 ) fprintf( out, " reset" );
+      if ( pdata -> data16[4] & 0x0020 ) fprintf( out, " user" );
+      if ( pdata -> data16[4] & 0x0010 ) fprintf( out, " valid" );
+      if ( pdata -> data16[5] & 0x0002 ) fprintf( out, " data" );
+      if ( !(pdata -> data16[5] & 0x0004) ) fprintf( out, " protected" );
+      switch ( pdata -> data16[5] & 0x0018 ) {
+        case 0x0008: fprintf( out, " pre2" ); break;
+        default: break;
+      }
+      if ( pdata -> data16[5] & 0x0020 ) fprintf( out, " fslock" );
+      fprintf( out, " type( 0x%x )", (pdata -> data16[5] >> 6) & 0x7f );
+      if ( pdata -> data16[5] & 0x2000 ) fprintf( out, " gstatus" );
+      fprintf( out, ")" );
+    }
+  }
+  if ( !switchok ) {
+    fprintf( out, v );
+    if ( type < 0 || type > SND_CTL_SW_TYPE_DWORD ) {
+      /* TODO: some well known types should be verbose */
+      fprintf( out, " rawdata( " );
+      for ( idx = 0; idx < 31; idx++ ) {
+        fprintf( out, "@%02x:", pdata -> data8[idx] );
+      }
+      fprintf( out, "%02x@ )\n", pdata -> data8[31] );
+    }
   }
   fprintf( out, " )\n" );
 }