From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49082) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z4TUy-0006iX-Ii for qemu-devel@nongnu.org; Mon, 15 Jun 2015 08:28:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Z4TUv-0005z0-5V for qemu-devel@nongnu.org; Mon, 15 Jun 2015 08:28:20 -0400 Received: from mx1.redhat.com ([209.132.183.28]:57919) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z4TUu-0005yM-PI for qemu-devel@nongnu.org; Mon, 15 Jun 2015 08:28:17 -0400 From: Gerd Hoffmann Date: Mon, 15 Jun 2015 14:27:54 +0200 Message-Id: <1434371291-6994-4-git-send-email-kraxel@redhat.com> In-Reply-To: <1434371291-6994-1-git-send-email-kraxel@redhat.com> References: <1434371291-6994-1-git-send-email-kraxel@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] [PULL 03/20] audio: remove winwave audio driver List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Gerd Hoffmann , =?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?= From: K=C5=91v=C3=A1g=C3=B3, Zolt=C3=A1n DirectSound should be a superior choice on Windows. Signed-off-by: K=C5=91v=C3=A1g=C3=B3, Zolt=C3=A1n Signed-off-by: Gerd Hoffmann --- audio/Makefile.objs | 1 - audio/audio_int.h | 1 - audio/winwaveaudio.c | 717 ---------------------------------------------= ------ configure | 13 +- 4 files changed, 4 insertions(+), 728 deletions(-) delete mode 100644 audio/winwaveaudio.c diff --git a/audio/Makefile.objs b/audio/Makefile.objs index b4c0608..481d1aa 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -6,7 +6,6 @@ common-obj-$(CONFIG_COREAUDIO) +=3D coreaudio.o common-obj-$(CONFIG_ALSA) +=3D alsaaudio.o common-obj-$(CONFIG_DSOUND) +=3D dsoundaudio.o common-obj-$(CONFIG_PA) +=3D paaudio.o -common-obj-$(CONFIG_WINWAVE) +=3D winwaveaudio.o common-obj-$(CONFIG_AUDIO_PT_INT) +=3D audio_pt_int.o common-obj-$(CONFIG_AUDIO_WIN_INT) +=3D audio_win_int.o common-obj-y +=3D wavcapture.o diff --git a/audio/audio_int.h b/audio/audio_int.h index 7445602..0eba44f 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -211,7 +211,6 @@ extern struct audio_driver coreaudio_audio_driver; extern struct audio_driver dsound_audio_driver; extern struct audio_driver pa_audio_driver; extern struct audio_driver spice_audio_driver; -extern struct audio_driver winwave_audio_driver; extern const struct mixeng_volume nominal_volume; =20 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsetting= s *as); diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c deleted file mode 100644 index 8dbd145..0000000 --- a/audio/winwaveaudio.c +++ /dev/null @@ -1,717 +0,0 @@ -/* public domain */ - -#include "qemu-common.h" -#include "sysemu/sysemu.h" -#include "audio.h" - -#define AUDIO_CAP "winwave" -#include "audio_int.h" - -#include -#include - -#include "audio_win_int.h" - -static struct { - int dac_headers; - int dac_samples; - int adc_headers; - int adc_samples; -} conf =3D { - .dac_headers =3D 4, - .dac_samples =3D 1024, - .adc_headers =3D 4, - .adc_samples =3D 1024 -}; - -typedef struct { - HWVoiceOut hw; - HWAVEOUT hwo; - WAVEHDR *hdrs; - HANDLE event; - void *pcm_buf; - int avail; - int pending; - int curhdr; - int paused; - CRITICAL_SECTION crit_sect; -} WaveVoiceOut; - -typedef struct { - HWVoiceIn hw; - HWAVEIN hwi; - WAVEHDR *hdrs; - HANDLE event; - void *pcm_buf; - int curhdr; - int paused; - int rpos; - int avail; - CRITICAL_SECTION crit_sect; -} WaveVoiceIn; - -static void winwave_log_mmresult (MMRESULT mr) -{ - const char *str =3D "BUG"; - - switch (mr) { - case MMSYSERR_NOERROR: - str =3D "Success"; - break; - - case MMSYSERR_INVALHANDLE: - str =3D "Specified device handle is invalid"; - break; - - case MMSYSERR_BADDEVICEID: - str =3D "Specified device id is out of range"; - break; - - case MMSYSERR_NODRIVER: - str =3D "No device driver is present"; - break; - - case MMSYSERR_NOMEM: - str =3D "Unable to allocate or lock memory"; - break; - - case WAVERR_SYNC: - str =3D "Device is synchronous but waveOutOpen was called " - "without using the WINWAVE_ALLOWSYNC flag"; - break; - - case WAVERR_UNPREPARED: - str =3D "The data block pointed to by the pwh parameter " - "hasn't been prepared"; - break; - - case WAVERR_STILLPLAYING: - str =3D "There are still buffers in the queue"; - break; - - default: - dolog ("Reason: Unknown (MMRESULT %#x)\n", mr); - return; - } - - dolog ("Reason: %s\n", str); -} - -static void GCC_FMT_ATTR (2, 3) winwave_logerr ( - MMRESULT mr, - const char *fmt, - ... - ) -{ - va_list ap; - - va_start (ap, fmt); - AUD_vlog (AUDIO_CAP, fmt, ap); - va_end (ap); - - AUD_log (NULL, " failed\n"); - winwave_log_mmresult (mr); -} - -static void winwave_anal_close_out (WaveVoiceOut *wave) -{ - MMRESULT mr; - - mr =3D waveOutClose (wave->hwo); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutClose"); - } - wave->hwo =3D NULL; -} - -static void CALLBACK winwave_callback_out ( - HWAVEOUT hwo, - UINT msg, - DWORD_PTR dwInstance, - DWORD_PTR dwParam1, - DWORD_PTR dwParam2 - ) -{ - WaveVoiceOut *wave =3D (WaveVoiceOut *) dwInstance; - - switch (msg) { - case WOM_DONE: - { - WAVEHDR *h =3D (WAVEHDR *) dwParam1; - if (!h->dwUser) { - h->dwUser =3D 1; - EnterCriticalSection (&wave->crit_sect); - { - wave->avail +=3D conf.dac_samples; - } - LeaveCriticalSection (&wave->crit_sect); - if (wave->hw.poll_mode) { - if (!SetEvent (wave->event)) { - dolog ("DAC SetEvent failed %lx\n", GetLastError= ()); - } - } - } - } - break; - - case WOM_CLOSE: - case WOM_OPEN: - break; - - default: - dolog ("unknown wave out callback msg %x\n", msg); - } -} - -static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) -{ - int i; - int err; - MMRESULT mr; - WAVEFORMATEX wfx; - WaveVoiceOut *wave; - - wave =3D (WaveVoiceOut *) hw; - - InitializeCriticalSection (&wave->crit_sect); - - err =3D waveformat_from_audio_settings (&wfx, as); - if (err) { - goto err0; - } - - mr =3D waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx, - (DWORD_PTR) winwave_callback_out, - (DWORD_PTR) wave, CALLBACK_FUNCTION); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutOpen"); - goto err1; - } - - wave->hdrs =3D audio_calloc (AUDIO_FUNC, conf.dac_headers, - sizeof (*wave->hdrs)); - if (!wave->hdrs) { - goto err2; - } - - audio_pcm_init_info (&hw->info, as); - hw->samples =3D conf.dac_samples * conf.dac_headers; - wave->avail =3D hw->samples; - - wave->pcm_buf =3D audio_calloc (AUDIO_FUNC, conf.dac_samples, - conf.dac_headers << hw->info.shift); - if (!wave->pcm_buf) { - goto err3; - } - - for (i =3D 0; i < conf.dac_headers; ++i) { - WAVEHDR *h =3D &wave->hdrs[i]; - - h->dwUser =3D 0; - h->dwBufferLength =3D conf.dac_samples << hw->info.shift; - h->lpData =3D advance (wave->pcm_buf, i * h->dwBufferLength); - h->dwFlags =3D 0; - - mr =3D waveOutPrepareHeader (wave->hwo, h, sizeof (*h)); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutPrepareHeader(%d)", i); - goto err4; - } - } - - return 0; - - err4: - g_free (wave->pcm_buf); - err3: - g_free (wave->hdrs); - err2: - winwave_anal_close_out (wave); - err1: - err0: - return -1; -} - -static int winwave_write (SWVoiceOut *sw, void *buf, int len) -{ - return audio_pcm_sw_write (sw, buf, len); -} - -static int winwave_run_out (HWVoiceOut *hw, int live) -{ - WaveVoiceOut *wave =3D (WaveVoiceOut *) hw; - int decr; - int doreset; - - EnterCriticalSection (&wave->crit_sect); - { - decr =3D audio_MIN (live, wave->avail); - decr =3D audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->p= ending); - wave->pending +=3D decr; - wave->avail -=3D decr; - } - LeaveCriticalSection (&wave->crit_sect); - - doreset =3D hw->poll_mode && (wave->pending >=3D conf.dac_samples); - if (doreset && !ResetEvent (wave->event)) { - dolog ("DAC ResetEvent failed %lx\n", GetLastError ()); - } - - while (wave->pending >=3D conf.dac_samples) { - MMRESULT mr; - WAVEHDR *h =3D &wave->hdrs[wave->curhdr]; - - h->dwUser =3D 0; - mr =3D waveOutWrite (wave->hwo, h, sizeof (*h)); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr); - break; - } - - wave->pending -=3D conf.dac_samples; - wave->curhdr =3D (wave->curhdr + 1) % conf.dac_headers; - } - - return decr; -} - -static void winwave_poll (void *opaque) -{ - (void) opaque; - audio_run ("winwave_poll"); -} - -static void winwave_fini_out (HWVoiceOut *hw) -{ - int i; - MMRESULT mr; - WaveVoiceOut *wave =3D (WaveVoiceOut *) hw; - - mr =3D waveOutReset (wave->hwo); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutReset"); - } - - for (i =3D 0; i < conf.dac_headers; ++i) { - mr =3D waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i], - sizeof (wave->hdrs[i])); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i); - } - } - - winwave_anal_close_out (wave); - - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - if (!CloseHandle (wave->event)) { - dolog ("DAC CloseHandle failed %lx\n", GetLastError ()); - } - wave->event =3D NULL; - } - - g_free (wave->pcm_buf); - wave->pcm_buf =3D NULL; - - g_free (wave->hdrs); - wave->hdrs =3D NULL; -} - -static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...) -{ - MMRESULT mr; - WaveVoiceOut *wave =3D (WaveVoiceOut *) hw; - - switch (cmd) { - case VOICE_ENABLE: - { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode =3D va_arg (ap, int); - va_end (ap); - - if (poll_mode && !wave->event) { - wave->event =3D CreateEvent (NULL, TRUE, TRUE, NULL); - if (!wave->event) { - dolog ("DAC CreateEvent: %lx, poll mode will be disa= bled\n", - GetLastError ()); - } - } - - if (wave->event) { - int ret; - - ret =3D qemu_add_wait_object (wave->event, winwave_poll,= wave); - hw->poll_mode =3D (ret =3D=3D 0); - } - else { - hw->poll_mode =3D 0; - } - wave->paused =3D 0; - } - return 0; - - case VOICE_DISABLE: - if (!wave->paused) { - mr =3D waveOutReset (wave->hwo); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutReset"); - } - else { - wave->paused =3D 1; - } - } - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - } - return 0; - } - return -1; -} - -static void winwave_anal_close_in (WaveVoiceIn *wave) -{ - MMRESULT mr; - - mr =3D waveInClose (wave->hwi); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInClose"); - } - wave->hwi =3D NULL; -} - -static void CALLBACK winwave_callback_in ( - HWAVEIN *hwi, - UINT msg, - DWORD_PTR dwInstance, - DWORD_PTR dwParam1, - DWORD_PTR dwParam2 - ) -{ - WaveVoiceIn *wave =3D (WaveVoiceIn *) dwInstance; - - switch (msg) { - case WIM_DATA: - { - WAVEHDR *h =3D (WAVEHDR *) dwParam1; - if (!h->dwUser) { - h->dwUser =3D 1; - EnterCriticalSection (&wave->crit_sect); - { - wave->avail +=3D conf.adc_samples; - } - LeaveCriticalSection (&wave->crit_sect); - if (wave->hw.poll_mode) { - if (!SetEvent (wave->event)) { - dolog ("ADC SetEvent failed %lx\n", GetLastError= ()); - } - } - } - } - break; - - case WIM_CLOSE: - case WIM_OPEN: - break; - - default: - dolog ("unknown wave in callback msg %x\n", msg); - } -} - -static void winwave_add_buffers (WaveVoiceIn *wave, int samples) -{ - int doreset; - - doreset =3D wave->hw.poll_mode && (samples >=3D conf.adc_samples); - if (doreset && !ResetEvent (wave->event)) { - dolog ("ADC ResetEvent failed %lx\n", GetLastError ()); - } - - while (samples >=3D conf.adc_samples) { - MMRESULT mr; - WAVEHDR *h =3D &wave->hdrs[wave->curhdr]; - - h->dwUser =3D 0; - mr =3D waveInAddBuffer (wave->hwi, h, sizeof (*h)); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr); - } - wave->curhdr =3D (wave->curhdr + 1) % conf.adc_headers; - samples -=3D conf.adc_samples; - } -} - -static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) -{ - int i; - int err; - MMRESULT mr; - WAVEFORMATEX wfx; - WaveVoiceIn *wave; - - wave =3D (WaveVoiceIn *) hw; - - InitializeCriticalSection (&wave->crit_sect); - - err =3D waveformat_from_audio_settings (&wfx, as); - if (err) { - goto err0; - } - - mr =3D waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx, - (DWORD_PTR) winwave_callback_in, - (DWORD_PTR) wave, CALLBACK_FUNCTION); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInOpen"); - goto err1; - } - - wave->hdrs =3D audio_calloc (AUDIO_FUNC, conf.dac_headers, - sizeof (*wave->hdrs)); - if (!wave->hdrs) { - goto err2; - } - - audio_pcm_init_info (&hw->info, as); - hw->samples =3D conf.adc_samples * conf.adc_headers; - wave->avail =3D 0; - - wave->pcm_buf =3D audio_calloc (AUDIO_FUNC, conf.adc_samples, - conf.adc_headers << hw->info.shift); - if (!wave->pcm_buf) { - goto err3; - } - - for (i =3D 0; i < conf.adc_headers; ++i) { - WAVEHDR *h =3D &wave->hdrs[i]; - - h->dwUser =3D 0; - h->dwBufferLength =3D conf.adc_samples << hw->info.shift; - h->lpData =3D advance (wave->pcm_buf, i * h->dwBufferLength); - h->dwFlags =3D 0; - - mr =3D waveInPrepareHeader (wave->hwi, h, sizeof (*h)); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInPrepareHeader(%d)", i); - goto err4; - } - } - - wave->paused =3D 1; - winwave_add_buffers (wave, hw->samples); - return 0; - - err4: - g_free (wave->pcm_buf); - err3: - g_free (wave->hdrs); - err2: - winwave_anal_close_in (wave); - err1: - err0: - return -1; -} - -static void winwave_fini_in (HWVoiceIn *hw) -{ - int i; - MMRESULT mr; - WaveVoiceIn *wave =3D (WaveVoiceIn *) hw; - - mr =3D waveInReset (wave->hwi); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInReset"); - } - - for (i =3D 0; i < conf.adc_headers; ++i) { - mr =3D waveInUnprepareHeader (wave->hwi, &wave->hdrs[i], - sizeof (wave->hdrs[i])); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInUnprepareHeader(%d)", i); - } - } - - winwave_anal_close_in (wave); - - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - if (!CloseHandle (wave->event)) { - dolog ("ADC CloseHandle failed %lx\n", GetLastError ()); - } - wave->event =3D NULL; - } - - g_free (wave->pcm_buf); - wave->pcm_buf =3D NULL; - - g_free (wave->hdrs); - wave->hdrs =3D NULL; -} - -static int winwave_run_in (HWVoiceIn *hw) -{ - WaveVoiceIn *wave =3D (WaveVoiceIn *) hw; - int live =3D audio_pcm_hw_get_live_in (hw); - int dead =3D hw->samples - live; - int decr, ret; - - if (!dead) { - return 0; - } - - EnterCriticalSection (&wave->crit_sect); - { - decr =3D audio_MIN (dead, wave->avail); - wave->avail -=3D decr; - } - LeaveCriticalSection (&wave->crit_sect); - - ret =3D decr; - while (decr) { - int left =3D hw->samples - hw->wpos; - int conv =3D audio_MIN (left, decr); - hw->conv (hw->conv_buf + hw->wpos, - advance (wave->pcm_buf, wave->rpos << hw->info.shift), - conv); - - wave->rpos =3D (wave->rpos + conv) % hw->samples; - hw->wpos =3D (hw->wpos + conv) % hw->samples; - decr -=3D conv; - } - - winwave_add_buffers (wave, ret); - return ret; -} - -static int winwave_read (SWVoiceIn *sw, void *buf, int size) -{ - return audio_pcm_sw_read (sw, buf, size); -} - -static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...) -{ - MMRESULT mr; - WaveVoiceIn *wave =3D (WaveVoiceIn *) hw; - - switch (cmd) { - case VOICE_ENABLE: - { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode =3D va_arg (ap, int); - va_end (ap); - - if (poll_mode && !wave->event) { - wave->event =3D CreateEvent (NULL, TRUE, TRUE, NULL); - if (!wave->event) { - dolog ("ADC CreateEvent: %lx, poll mode will be disa= bled\n", - GetLastError ()); - } - } - - if (wave->event) { - int ret; - - ret =3D qemu_add_wait_object (wave->event, winwave_poll,= wave); - hw->poll_mode =3D (ret =3D=3D 0); - } - else { - hw->poll_mode =3D 0; - } - if (wave->paused) { - mr =3D waveInStart (wave->hwi); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInStart"); - } - wave->paused =3D 0; - } - } - return 0; - - case VOICE_DISABLE: - if (!wave->paused) { - mr =3D waveInStop (wave->hwi); - if (mr !=3D MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInStop"); - } - else { - wave->paused =3D 1; - } - } - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - } - return 0; - } - return 0; -} - -static void *winwave_audio_init (void) -{ - return &conf; -} - -static void winwave_audio_fini (void *opaque) -{ - (void) opaque; -} - -static struct audio_option winwave_options[] =3D { - { - .name =3D "DAC_HEADERS", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.dac_headers, - .descr =3D "DAC number of headers", - }, - { - .name =3D "DAC_SAMPLES", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.dac_samples, - .descr =3D "DAC number of samples per header", - }, - { - .name =3D "ADC_HEADERS", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.adc_headers, - .descr =3D "ADC number of headers", - }, - { - .name =3D "ADC_SAMPLES", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.adc_samples, - .descr =3D "ADC number of samples per header", - }, - { /* End of list */ } -}; - -static struct audio_pcm_ops winwave_pcm_ops =3D { - .init_out =3D winwave_init_out, - .fini_out =3D winwave_fini_out, - .run_out =3D winwave_run_out, - .write =3D winwave_write, - .ctl_out =3D winwave_ctl_out, - .init_in =3D winwave_init_in, - .fini_in =3D winwave_fini_in, - .run_in =3D winwave_run_in, - .read =3D winwave_read, - .ctl_in =3D winwave_ctl_in -}; - -struct audio_driver winwave_audio_driver =3D { - .name =3D "winwave", - .descr =3D "Windows Waveform Audio http://msdn.microsoft.co= m", - .options =3D winwave_options, - .init =3D winwave_audio_init, - .fini =3D winwave_audio_fini, - .pcm_ops =3D &winwave_pcm_ops, - .can_be_default =3D 1, - .max_voices_out =3D INT_MAX, - .max_voices_in =3D INT_MAX, - .voice_size_out =3D sizeof (WaveVoiceOut), - .voice_size_in =3D sizeof (WaveVoiceIn) -}; diff --git a/configure b/configure index 58d212a..ccf8dc7 100755 --- a/configure +++ b/configure @@ -562,13 +562,13 @@ case $targetos in CYGWIN*) mingw32=3D"yes" QEMU_CFLAGS=3D"-mno-cygwin $QEMU_CFLAGS" - audio_possible_drivers=3D"winwave sdl" - audio_drv_list=3D"winwave" + audio_possible_drivers=3D"sdl" + audio_drv_list=3D"sdl" ;; MINGW32*) mingw32=3D"yes" - audio_possible_drivers=3D"winwave dsound sdl" - audio_drv_list=3D"winwave" + audio_possible_drivers=3D"dsound sdl" + audio_drv_list=3D"dsound" ;; GNU/kFreeBSD) bsd=3D"yes" @@ -2634,11 +2634,6 @@ for drv in $audio_drv_list; do # XXX: Probes for CoreAudio, DirectSound, SDL(?) ;; =20 - winwave) - libs_softmmu=3D"-lwinmm $libs_softmmu" - audio_win_int=3D"yes" - ;; - *) echo "$audio_possible_drivers" | grep -q "\<$drv\>" || { error_exit "Unknown driver '$drv' selected" \ --=20 1.8.3.1