sb16_mixer.c

Go to the documentation of this file.
00001 /* This file contains the driver for the mixer on
00002  * a SoundBlaster 16 soundcard.
00003  *
00004  * The driver supports the following operations (using message format m2):
00005  *
00006  *    m_type      DEVICE    IO_ENDPT     COUNT    POSITION  ADRRESS
00007  * ----------------------------------------------------------------
00008  * |  DEV_OPEN  | device  | proc nr |         |         |         |
00009  * |------------+---------+---------+---------+---------+---------|
00010  * |  DEV_CLOSE | device  | proc nr |         |         |         |
00011  * |------------+---------+---------+---------+---------+---------|
00012  * |  DEV_IOCTL | device  | proc nr |func code|         | buf_ptr |
00013  * ----------------------------------------------------------------
00014  *
00015  * The file contains one entry point:
00016  *
00017  *   sb16mixer_task:  main entry when system is brought up
00018  *
00019  *      August 24 2005          Ported driver to user space (Peter Boonstoppel)
00020  *  May 20 1995                 Author: Michel R. Prevenier 
00021  */
00022 
00023 
00024 #include "sb16.h"
00025 
00026 
00027 _PROTOTYPE(void main, (void));
00028 FORWARD _PROTOTYPE( int mixer_init, (void)); 
00029 FORWARD _PROTOTYPE( int mixer_open, (message *m_ptr));
00030 FORWARD _PROTOTYPE( int mixer_close, (message *m_ptr));
00031 FORWARD _PROTOTYPE( int mixer_ioctl, (message *m_ptr));
00032 FORWARD _PROTOTYPE( int mixer_get, (int reg));
00033 FORWARD _PROTOTYPE( int get_set_volume, (message *m_ptr, int flag));
00034 FORWARD _PROTOTYPE( int get_set_input, (message *m_ptr, int flag, int channel));
00035 FORWARD _PROTOTYPE( int get_set_output, (message *m_ptr, int flag));
00036 
00037 
00038 PRIVATE int mixer_avail = 0;    /* Mixer exists? */
00039 
00040 
00041 #define dprint (void)
00042 
00043 
00044 /*===========================================================================*
00045  *                              main
00046  *===========================================================================*/
00047 PUBLIC void main() {
00048 message mess;
00049         int err, caller, proc_nr;
00050 
00051         /* Here is the main loop of the mixer task. It waits for a message, carries
00052         * it out, and sends a reply.
00053         */
00054         while (TRUE) {
00055                 receive(ANY, &mess);
00056 
00057                 caller = mess.m_source;
00058                 proc_nr = mess.IO_ENDPT;
00059 
00060                 switch (caller) {
00061                         case HARDWARE: /* Leftover interrupt. */
00062                                 continue;
00063                         case FS_PROC_NR: /* The only legitimate caller. */
00064                                 break;
00065                         default:
00066                                 dprint("sb16: got message from %d\n", caller);
00067                                 continue;
00068                 }
00069 
00070                 /* Now carry out the work. */
00071                 switch(mess.m_type) {
00072                         case DEV_OPEN:      err = mixer_open(&mess); break;     
00073                         case DEV_CLOSE:     err = mixer_close(&mess); break; 
00074                         case DEV_IOCTL:     err = mixer_ioctl(&mess); break;
00075                         default:                err = EINVAL; break;
00076                 }
00077 
00078                 /* Finally, prepare and send the reply message. */
00079                 mess.m_type = TASK_REPLY;
00080                 mess.REP_ENDPT = proc_nr;
00081         
00082                 dprint("%d %d", err, OK);
00083                 
00084                 mess.REP_STATUS = err;  /* error code */
00085                 send(caller, &mess);    /* send reply to caller */
00086         }
00087 }
00088 
00089 
00090 /*=========================================================================*
00091  *                              mixer_open                                      
00092  *=========================================================================*/
00093 PRIVATE int mixer_open(m_ptr)
00094 message *m_ptr;
00095 {
00096         dprint("mixer_open\n");
00097 
00098         /* try to detect the mixer type */
00099         if (!mixer_avail && mixer_init() != OK) return EIO;
00100 
00101         return OK;
00102 }
00103 
00104 
00105 /*=========================================================================*
00106  *                              mixer_close                                     
00107  *=========================================================================*/
00108 PRIVATE int mixer_close(m_ptr)
00109 message *m_ptr;
00110 {
00111         dprint("mixer_close\n");
00112 
00113         return OK;
00114 }
00115 
00116 
00117 /*=========================================================================*
00118  *                              mixer_ioctl                                     
00119  *=========================================================================*/
00120 PRIVATE int mixer_ioctl(m_ptr)
00121 message *m_ptr;
00122 {
00123         int status;
00124 
00125         dprint("mixer: got ioctl %d\n", m_ptr->REQUEST);
00126 
00127 
00128         switch(m_ptr->REQUEST) {
00129                 case MIXIOGETVOLUME:      status = get_set_volume(m_ptr, 0); break;
00130                 case MIXIOSETVOLUME:      status = get_set_volume(m_ptr, 1); break;
00131                 case MIXIOGETINPUTLEFT:   status = get_set_input(m_ptr, 0, 0); break;
00132                 case MIXIOGETINPUTRIGHT:  status = get_set_input(m_ptr, 0, 1); break;
00133                 case MIXIOGETOUTPUT:      status = get_set_output(m_ptr, 0); break;
00134                 case MIXIOSETINPUTLEFT:   status = get_set_input(m_ptr, 1, 0); break;
00135                 case MIXIOSETINPUTRIGHT:  status = get_set_input(m_ptr, 1, 1); break;
00136                 case MIXIOSETOUTPUT:      status = get_set_output(m_ptr, 1); break;
00137                 default:                  status = ENOTTY;
00138         }
00139 
00140         return status;
00141 }
00142 
00143 
00144 /*=========================================================================*
00145  *                              mixer_init                                 
00146  *=========================================================================*/
00147 PRIVATE int mixer_init()
00148 {
00149         /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
00150         * value written can be read back the mixer is there
00151         */
00152 
00153         mixer_set(MIXER_DAC_LEVEL, 0x10);       /* write something to it */
00154         if(mixer_get(MIXER_DAC_LEVEL) != 0x10) {
00155                 dprint("sb16: Mixer not detected\n");
00156                 return EIO;
00157         }
00158 
00159         /* Enable Automatic Gain Control */
00160         mixer_set(MIXER_AGC, 0x01);
00161 
00162         dprint("Mixer detected\n");
00163 
00164         mixer_avail = 1;
00165         return OK;
00166 }
00167 
00168 
00169 /*=========================================================================*
00170  *                              mixer_get                                 
00171  *=========================================================================*/
00172 PRIVATE int mixer_get(reg)
00173 int reg;
00174 {
00175         int i;
00176 
00177         sb16_outb(MIXER_REG, reg);
00178         for(i = 0; i < 100; i++);
00179         return sb16_inb(MIXER_DATA) & 0xff;
00180 }  
00181 
00182 
00183 /*=========================================================================*
00184  *                              get_set_volume                             *
00185  *=========================================================================*/
00186 PRIVATE int get_set_volume(m_ptr, flag)
00187 message *m_ptr;
00188 int flag;       /* 0 = get, 1 = set */
00189 {
00190         phys_bytes user_phys;
00191         struct volume_level level;
00192         int cmd_left, cmd_right, shift, max_level;
00193 
00194         sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&level, (phys_bytes)sizeof(level));
00195 
00196         shift = 3;
00197         max_level = 0x1F;
00198 
00199         switch(level.device) {
00200                 case Master:
00201                         cmd_left = MIXER_MASTER_LEFT;
00202                         cmd_right = MIXER_MASTER_RIGHT;
00203                         break;
00204                 case Dac:
00205                         cmd_left = MIXER_DAC_LEFT;
00206                         cmd_right = MIXER_DAC_RIGHT;
00207                         break;
00208                 case Fm:
00209                         cmd_left = MIXER_FM_LEFT;
00210                         cmd_right = MIXER_FM_RIGHT;
00211                         break;
00212                 case Cd:
00213                         cmd_left = MIXER_CD_LEFT;
00214                         cmd_right = MIXER_CD_RIGHT;
00215                         break;
00216                 case Line:
00217                         cmd_left = MIXER_LINE_LEFT;
00218                         cmd_right = MIXER_LINE_RIGHT;
00219                         break;
00220                 case Mic:
00221                         cmd_left = cmd_right = MIXER_MIC_LEVEL;
00222                         break;
00223                 case Speaker:
00224                         cmd_left = cmd_right = MIXER_PC_LEVEL;
00225                         shift = 6;
00226                         max_level = 0x03;
00227                         break;
00228                 case Treble:
00229                         cmd_left = MIXER_TREBLE_LEFT;
00230                         cmd_right = MIXER_TREBLE_RIGHT;
00231                         shift = 4;
00232                         max_level = 0x0F;
00233                         break;
00234                 case Bass:  
00235                         cmd_left = MIXER_BASS_LEFT;
00236                         cmd_right = MIXER_BASS_RIGHT;
00237                         shift = 4;
00238                         max_level = 0x0F;
00239                         break;
00240                 default:     
00241                         return EINVAL;
00242         }
00243 
00244         if(flag) { /* Set volume level */
00245                 if(level.right < 0) level.right = 0;
00246                 else if(level.right > max_level) level.right = max_level;
00247                 if(level.left < 0) level.left = 0;
00248                 else if(level.left > max_level) level.left = max_level;
00249 
00250                 mixer_set(cmd_right, (level.right << shift));
00251                 mixer_set(cmd_left, (level.left << shift));
00252         } else { /* Get volume level */
00253                 level.left = mixer_get(cmd_left);
00254                 level.right = mixer_get(cmd_right);
00255 
00256                 level.left >>= shift;
00257                 level.right >>= shift;
00258 
00259                 /* Copy back to user */
00260                 sys_datacopy(SELF, (vir_bytes)&level, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(level));
00261         }
00262 
00263         return OK;
00264 }
00265 
00266 
00267 /*=========================================================================*
00268  *                              get_set_input                              *
00269  *=========================================================================*/
00270 PRIVATE int get_set_input(m_ptr, flag, channel)
00271 message *m_ptr;
00272 int flag;       /* 0 = get, 1 = set */
00273 int channel;    /* 0 = left, 1 = right */
00274 {
00275         phys_bytes user_phys;
00276         struct inout_ctrl input;
00277         int input_cmd, input_mask, mask, del_mask, shift;
00278 
00279         sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&input, (phys_bytes)sizeof(input));
00280 
00281         input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT);
00282 
00283         mask = mixer_get(input_cmd); 
00284 
00285         switch (input.device) {
00286                 case Fm:
00287                         shift = 5;
00288                         del_mask = 0x1F; 
00289                         break;
00290                 case Cd: 
00291                         shift = 1;
00292                         del_mask = 0x79;
00293                         break;
00294                 case Line:
00295                         shift = 3;
00296                         del_mask = 0x67;
00297                         break;
00298                 case Mic: 
00299                         shift = 0;
00300                         del_mask = 0x7E;
00301                         break;
00302                 default:   
00303                         return EINVAL;
00304         }
00305 
00306         if (flag) {  /* Set input */
00307                 input_mask = ((input.left == ON ? 1 : 0) << 1) | (input.right == ON ? 1 : 0);
00308 
00309                 if (shift > 0) input_mask <<= shift;
00310                 else input_mask >>= 1;
00311 
00312                 mask &= del_mask;   
00313                 mask |= input_mask;
00314 
00315                 mixer_set(input_cmd, mask);
00316         } else {        /* Get input */
00317                 if (shift > 0) {
00318                         input.left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
00319                         input.right = ((mask >> shift) & 1 == 1 ? ON : OFF);
00320                 } else {
00321                         input.left = ((mask & 1) == 1 ? ON : OFF);
00322                 }
00323 
00324                 /* Copy back to user */
00325                 sys_datacopy(SELF, (vir_bytes)&input, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(input));
00326         }
00327 
00328         return OK;
00329 }
00330 
00331 
00332 /*=========================================================================*
00333  *                              get_set_output                             *
00334  *=========================================================================*/
00335 PRIVATE int get_set_output(m_ptr, flag)
00336 message *m_ptr;
00337 int flag;       /* 0 = get, 1 = set */
00338 {
00339         phys_bytes user_phys;
00340         struct inout_ctrl output;
00341         int output_mask, mask, del_mask, shift;
00342 
00343         sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&output, (phys_bytes)sizeof(output));
00344 
00345         mask = mixer_get(MIXER_OUTPUT_CTRL); 
00346 
00347         switch (output.device) {
00348                 case Cd:
00349                         shift = 1;
00350                         del_mask = 0x79;
00351                         break;
00352                 case Line:
00353                         shift = 3;
00354                         del_mask = 0x67;
00355                         break;
00356                 case Mic:
00357                         shift = 0;
00358                         del_mask = 0x7E;
00359                         break;
00360                 default:   
00361                         return EINVAL;
00362         }
00363 
00364         if (flag) {  /* Set input */
00365                 output_mask = ((output.left == ON ? 1 : 0) << 1) | (output.right == ON ? 1 : 0);
00366 
00367                 if (shift > 0) output_mask <<= shift;
00368                 else output_mask >>= 1;
00369 
00370                 mask &= del_mask;   
00371                 mask |= output_mask;
00372 
00373                 mixer_set(MIXER_OUTPUT_CTRL, mask);
00374         } else {    /* Get input */
00375                 if (shift > 0) {
00376                         output.left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
00377                         output.right = ((mask >> shift) & 1 == 1 ? ON : OFF);
00378                 } else {
00379                         output.left = ((mask & 1) == 1 ? ON : OFF);
00380                 }
00381 
00382                 /* Copy back to user */
00383                 sys_datacopy(SELF, (vir_bytes)&output, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(output));
00384         }
00385 
00386         return OK;
00387 }

Generated on Fri Apr 14 22:57:18 2006 for minix by  doxygen 1.4.6