1 /*
2  *              KMix -- KDE's full featured mini mixer
3  *
4  *
5  *              Copyright (C) 1996-2000 Christian Esken <esken@kde.org>
6  *                            2000 Brian Hanson <bhanson@hotmail.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this program; if not, write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22 
23 #include "mixer_sun.h"
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <sys/file.h>
29 #include <sys/audioio.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 
34 #include "core/mixer.h"
35 #include <sys/soundcard.h>
36 #include <QTimer>
37 #include <qplatformdefs.h>
38 //======================================================================
39 // CONSTANT/ENUM DEFINITIONS
40 //======================================================================
41 
42 //
43 // Mixer Device Numbers
44 //
45 // Note: We can't just use the Sun port #defines because :
46 // 1) Some logical devices don't correspond to ports (master&recmon)
47 // 2) The play and record port definitions reuse the same values
48 //
49 enum MixerDevs
50 {
51    MIXERDEV_MASTER_VOLUME,
52    MIXERDEV_INTERNAL_SPEAKER,
53    MIXERDEV_HEADPHONE,
54    MIXERDEV_LINE_OUT,
55    MIXERDEV_RECORD_MONITOR,
56    MIXERDEV_MICROPHONE,
57    MIXERDEV_LINE_IN,
58    MIXERDEV_CD,
59    // Insert new devices before this marker
60    MIXERDEV_END_MARKER
61 };
62 const int numDevs = MIXERDEV_END_MARKER;
63 
64 //
65 // Device name strings
66 //
67 const char* MixerDevNames[] =
68 {
69    I18N_NOOP("Master Volume"),
70    I18N_NOOP("Internal Speaker"),
71    I18N_NOOP("Headphone"),
72    I18N_NOOP("Line Out"),
73    I18N_NOOP("Record Monitor"),
74    I18N_NOOP("Microphone"),
75    I18N_NOOP("Line In"),
76    I18N_NOOP("CD")
77 };
78 
79 //
80 // Channel types (this specifies which icon to display)
81 //
82 const MixDevice::ChannelType MixerChannelTypes[] =
83 {
84    MixDevice::VOLUME,       // MASTER_VOLUME
85    MixDevice::AUDIO,        // INTERNAL_SPEAKER
86    MixDevice::EXTERNAL,     // HEADPHONE (we really need an icon for this)
87    MixDevice::EXTERNAL,     // LINE_OUT
88    MixDevice::RECMONITOR,   // RECORD_MONITOR
89    MixDevice::MICROPHONE,   // MICROPHONE
90    MixDevice::EXTERNAL,     // LINE_IN
91    MixDevice::CD            // CD
92 };
93 
94 //
95 // Mapping from device numbers to Sun port mask values
96 //
97 const uint_t MixerSunPortMasks[] =
98 {
99    0,                  // MASTER_VOLUME - no associated port
100    AUDIO_SPEAKER,
101    AUDIO_HEADPHONE,
102    AUDIO_LINE_OUT,
103    0,                  // RECORD_MONITOR - no associated port
104    AUDIO_MICROPHONE,
105    AUDIO_LINE_IN,
106    AUDIO_CD
107 };
108 
109 
110 //======================================================================
111 // FUNCTION/METHOD DEFINITIONS
112 //======================================================================
113 
114 
115 //======================================================================
116 // FUNCTION    : SUN_getMixer
117 // DESCRIPTION : Creates and returns a new mixer object.
118 //======================================================================
SUN_getMixer(Mixer * mixer,int devnum)119 Mixer_Backend* SUN_getMixer( Mixer *mixer, int devnum )
120 {
121    Mixer_Backend *l_mixer;
122    l_mixer = new Mixer_SUN( mixer, devnum );
123    return l_mixer;
124 }
125 
126 
127 //======================================================================
128 // FUNCTION    : Mixer::Mixer
129 // DESCRIPTION : Class constructor.
130 //======================================================================
Mixer_SUN(Mixer * mixer,int devnum)131 Mixer_SUN::Mixer_SUN(Mixer *mixer, int devnum) : Mixer_Backend(mixer, devnum)
132 {
133    if ( devnum == -1 )
134       m_devnum = 0;
135 }
136 
137 //======================================================================
138 // FUNCTION    : Mixer::Mixer
139 // DESCRIPTION : Class destructor.
140 //======================================================================
~Mixer_SUN()141 Mixer_SUN::~Mixer_SUN()
142 {
143    close();
144 }
145 
146 //======================================================================
147 // FUNCTION    : Mixer::open
148 // DESCRIPTION : Initialize the mixer and open the hardware driver.
149 //======================================================================
open()150 int Mixer_SUN::open()
151 {
152    //
153    // We don't support multiple devices
154    //
155    if ( m_devnum !=0 )
156       return Mixer::ERR_OPEN;
157 
158    //
159    // Open the mixer hardware driver
160    //
161    QString audiodev(qgetenv("AUDIODEV"));
162    if(audiodev.isNull())
163      audiodev = "/dev/audio";
164    audiodev += "ctl";
165    _udi = audiodev; // use device name as UDI. Doesn't matter as we only use it for hotplugging/unplugging.
166    if ( ( fd = QT_OPEN( audiodev.toLatin1().data(), O_RDWR ) ) < 0 )
167    {
168       if ( errno == EACCES )
169          return Mixer::ERR_PERM;
170       else
171          return Mixer::ERR_OPEN;
172    }
173    else
174    {
175       //
176       // Mixer is open. Now define all of the mix devices.
177       //
178 	       int devmask, recmask, i_recsrc, stereodevs;
179       		// Mixer is open. Now define properties
180 	      if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
181 	        return Mixer::ERR_READ;
182 	      if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
183 	        return Mixer::ERR_READ;
184 	      if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1)
185 	        return Mixer::ERR_READ;
186 	      if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1)
187 	        return Mixer::ERR_READ;
188 
189          for ( int idx = 0; idx < numDevs; idx++ )
190          {
191 	    Volume::ChannelMask chnmask = Volume::MLEFT;
192             if ( stereodevs & ( 1 << idx ) ) chnmask = (Volume::ChannelMask)(chnmask|Volume::MRIGHT);
193 
194             Volume playbackVol( 100, 1, true, false );
195 	    QString id;
196             id.setNum(idx);
197             MixDevice* md = new MixDevice( _mixer, id,
198                QString(MixerDevNames[idx]), MixerChannelTypes[idx]);
199             md->addPlaybackVolume(playbackVol);
200                   // Tutorial: Howto add a simple capture switch
201                   if ( recmask & ( 1 << idx ) ) {
202                      // can be captured => add capture volume, with no capture volume
203                      chnmask = Volume::MNONE;
204                      Volume captureVol( 100, 1, true, true );
205                      md->addCaptureVolume(captureVol);
206                  }
207             m_mixDevices.append( md->addToPool() );
208          }
209 
210      registerCard("SUN Audio Mixer");
211      m_isOpen = true;
212 
213      return 0;
214    }
215 }
216 
217 //======================================================================
218 // FUNCTION    : Mixer::close
219 // DESCRIPTION : Close the hardware driver.
220 //======================================================================
close()221 int Mixer_SUN::close()
222 {
223    _pollingTimer->stop();
224    m_isOpen = false;
225    int l_i_ret = ::close( fd );
226    closeCommon();
227    return l_i_ret;
228 }
229 
230 //======================================================================
231 // FUNCTION    : Mixer::errorText
232 // DESCRIPTION : Convert an error code enum to a text string.
233 //======================================================================
errorText(int mixer_error)234 QString Mixer_SUN::errorText( int mixer_error )
235 {
236    QString errmsg;
237    switch (mixer_error)
238    {
239       case Mixer::ERR_PERM:
240          errmsg = i18n(
241            "kmix: You do not have permission to access the mixer device.\n"
242            "Ask your system administrator to fix /dev/audioctl to allow access."
243          );
244          break;
245       default:
246          errmsg = Mixer_Backend::errorText( mixer_error );
247    }
248    return errmsg;
249 }
250 
251 
252 //======================================================================
253 // FUNCTION    : Mixer::readVolumeFromHW
254 // DESCRIPTION : Read the audio information from the driver.
255 //======================================================================
readVolumeFromHW(const QString & id,shared_ptr<MixDevice> md)256 int Mixer_SUN::readVolumeFromHW( const QString& id, shared_ptr<MixDevice> md )
257 {
258    audio_info_t audioinfo;
259    int devnum = id2num(id);
260    uint_t devMask = MixerSunPortMasks[devnum];
261 
262    Volume& volume = md->playbackVolume();
263    //
264    // Read the current audio information from the driver
265    //
266    if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
267    {
268       return( Mixer::ERR_READ );
269    }
270    else
271    {
272       //
273       // Extract the appropriate fields based on the requested device
274       //
275       switch ( devnum )
276       {
277          case MIXERDEV_MASTER_VOLUME :
278             //volume.setSwitchActivated( audioinfo.output_muted );
279             GainBalanceToVolume( audioinfo.play.gain,
280                                  audioinfo.play.balance,
281                                  volume );
282             break;
283 
284          case MIXERDEV_RECORD_MONITOR :
285             md->setMuted(false);
286             volume.setAllVolumes( audioinfo.monitor_gain );
287             break;
288 
289          case MIXERDEV_INTERNAL_SPEAKER :
290          case MIXERDEV_HEADPHONE :
291          case MIXERDEV_LINE_OUT :
292             md->setMuted( (audioinfo.play.port & devMask) ? false : true );
293             GainBalanceToVolume( audioinfo.play.gain,
294                                  audioinfo.play.balance,
295                                  volume );
296             break;
297 
298          case MIXERDEV_MICROPHONE :
299          case MIXERDEV_LINE_IN :
300          case MIXERDEV_CD :
301             md->setMuted( (audioinfo.record.port & devMask) ? false : true );
302             GainBalanceToVolume( audioinfo.record.gain,
303                                  audioinfo.record.balance,
304                                  volume );
305             break;
306 
307          default :
308             return Mixer::ERR_READ;
309       }
310       return 0;
311    }
312 }
313 
314 //======================================================================
315 // FUNCTION    : Mixer::writeVolumeToHW
316 // DESCRIPTION : Write the specified audio settings to the hardware.
317 //======================================================================
writeVolumeToHW(const QString & id,shared_ptr<MixDevice> md)318 int Mixer_SUN::writeVolumeToHW( const QString& id, shared_ptr<MixDevice> md )
319 {
320    uint_t gain;
321    uchar_t balance;
322    uchar_t mute;
323 
324    Volume& volume = md->playbackVolume();
325    int devnum = id2num(id);
326    //
327    // Convert the Volume(left vol, right vol) to the Gain/Balance Sun uses
328    //
329    VolumeToGainBalance( volume, gain, balance );
330    mute = md->isMuted() ? 1 : 0;
331 
332    //
333    // Read the current audio settings from the hardware
334    //
335    audio_info_t audioinfo;
336    if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
337    {
338       return( Mixer::ERR_READ );
339    }
340 
341    //
342    // Now, based on the devnum that we are writing to, update the appropriate
343    // volume field and twiddle the appropriate bitmask to enable/mute the
344    // device as necessary.
345    //
346    switch ( devnum )
347    {
348       case MIXERDEV_MASTER_VOLUME :
349          audioinfo.play.gain = gain;
350          audioinfo.play.balance = balance;
351          audioinfo.output_muted = mute;
352          break;
353 
354       case MIXERDEV_RECORD_MONITOR :
355          audioinfo.monitor_gain = gain;
356          // no mute or balance for record monitor
357          break;
358 
359       case MIXERDEV_INTERNAL_SPEAKER :
360       case MIXERDEV_HEADPHONE :
361       case MIXERDEV_LINE_OUT :
362          audioinfo.play.gain = gain;
363          audioinfo.play.balance = balance;
364          if ( mute )
365             audioinfo.play.port &= ~MixerSunPortMasks[devnum];
366          else
367             audioinfo.play.port |= MixerSunPortMasks[devnum];
368          break;
369 
370       case MIXERDEV_MICROPHONE :
371       case MIXERDEV_LINE_IN :
372       case MIXERDEV_CD :
373          audioinfo.record.gain = gain;
374          audioinfo.record.balance = balance;
375          if ( mute )
376             audioinfo.record.port &= ~MixerSunPortMasks[devnum];
377          else
378             audioinfo.record.port |= MixerSunPortMasks[devnum];
379          break;
380 
381       default :
382          return Mixer::ERR_READ;
383    }
384 
385    //
386    // Now that we've updated the audioinfo struct, write it back to the hardware
387    //
388    if ( ioctl( fd, AUDIO_SETINFO, &audioinfo ) < 0 )
389    {
390       return( Mixer::ERR_WRITE );
391    }
392    else
393    {
394       return 0;
395    }
396 }
397 
398 
399 //======================================================================
400 // FUNCTION    : Mixer::isRecsrcHW
401 // DESCRIPTION : Returns true if the specified device is a record source.
402 //======================================================================
403 
404 // isRecsrcHW() is not supported any longer. You must set the state in the MixDevice in readVolumeFromHW() or writeVolumeFromHW() appropriately
405 
406 //bool Mixer_SUN::isRecsrcHW( isRecsrcHW(const QString& id )
407 //{
408 //   int devnum = id2num(id);
409 //   switch ( devnum )
410 //   {
411 //      case MIXERDEV_MICROPHONE :
412 //      case MIXERDEV_LINE_IN :
413 //      case MIXERDEV_CD :
414 //         return true;
415 //
416 //      default :
417 //         return false;
418 //   }
419 //}
420 
421 //======================================================================
422 // FUNCTION    : Mixer::VolumeToGainBalance
423 // DESCRIPTION : Converts a Volume(left vol + right vol) into the
424 //               Gain/Balance values used by Sun.
425 //======================================================================
VolumeToGainBalance(Volume & volume,uint_t & gain,uchar_t & balance)426 void Mixer_SUN::VolumeToGainBalance( Volume& volume, uint_t& gain, uchar_t& balance )
427 {
428    if ( ( volume.count() == 1 ) ||
429         ( volume.getVolume(Volume::LEFT) == volume.getVolume(Volume::RIGHT) ) )
430    {
431       gain = volume.getVolume(Volume::LEFT);
432       balance = AUDIO_MID_BALANCE;
433    }
434    else
435    {
436       if ( volume.getVolume(Volume::LEFT) > volume.getVolume(Volume::RIGHT) )
437       {
438          gain = volume.getVolume(Volume::LEFT);
439          balance = AUDIO_LEFT_BALANCE +
440            ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) *
441            volume.getVolume(Volume::RIGHT) / volume.getVolume(Volume::LEFT);
442       }
443       else
444       {
445          gain = volume.getVolume(Volume::RIGHT);
446          balance = AUDIO_RIGHT_BALANCE -
447            ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) *
448            volume.getVolume(Volume::LEFT) / volume.getVolume(Volume::RIGHT);
449       }
450    }
451 }
452 
453 //======================================================================
454 // FUNCTION    : Mixer::GainBalanceToVolume
455 // DESCRIPTION : Converts Gain/Balance returned by Sun driver to the
456 //               Volume(left vol + right vol) format used by kmix.
457 //======================================================================
GainBalanceToVolume(uint_t & gain,uchar_t & balance,Volume & volume)458 void Mixer_SUN::GainBalanceToVolume( uint_t& gain, uchar_t& balance, Volume& volume )
459 {
460    if ( volume.count() == 1 )
461    {
462       volume.setVolume( Volume::LEFT, gain );
463    }
464    else
465    {
466       if ( balance <= AUDIO_MID_BALANCE )
467       {
468          volume.setVolume( Volume::LEFT, gain );
469          volume.setVolume( Volume::RIGHT, gain *
470             ( balance - AUDIO_LEFT_BALANCE ) /
471             ( AUDIO_MID_BALANCE - AUDIO_LEFT_BALANCE ) );
472       }
473       else
474       {
475          volume.setVolume( Volume::RIGHT, gain );
476          volume.setVolume( Volume::LEFT, gain *
477             ( AUDIO_RIGHT_BALANCE - balance ) /
478             ( AUDIO_RIGHT_BALANCE - AUDIO_MID_BALANCE ) );
479       }
480    }
481 }
482 
id2num(const QString & id)483 int Mixer_SUN::id2num(const QString& id)
484 {
485 	return id.toInt();
486 }
487 
SUN_getDriverName()488 QString SUN_getDriverName() {
489         return "SUNAudio";
490 }
491 
getDriverName()492 QString Mixer_SUN::getDriverName()
493 {
494         return "SUNAudio";
495 }
496 
497