/* * Purpose: Support for the legacy (SADA) /dev/audio interfaces of Solaris * * Description: * This driver serves as a bridge between the OSS audio drivers and the * native audio support framework of Solaris. * * **** This driver supports only one device instance **** */ /* * * This file is part of Open Sound System. * * Copyright (C) 4Front Technologies 1996-2008. * * This this source file is released under GPL v2 license (no other versions). * See the COPYING file included in the main directory of this source * distribution for the license terms and conditions. * */ #include "oss_sadasupport_cfg.h" #include #include #include #include #include #include #include #include #include #include #ifdef SOL9 #include #else #include #endif int sadasupport_rate = 48000; static int do_byteswap = 0; #ifdef SOL9 extern am_ad_src_entry_t am_src1; #else extern am_ad_src_entry_t am_src2; #endif #define AUDIOS_DEV_NAME "SUNW,oss" #define AUDIOS_DEV_CONFIG "onboard1" #define AUDIOS_DEV_VERSION "a" #define AUDIOS_NAME "oss" #define AUDIOS_IDNUM (0x6175) #define AUDIOS_MINPACKET (0) #define AUDIOS_MAXPACKET (1*1024) #define AUDIOS_HIWATER (64*1024) #define AUDIOS_LOWATER (32*1024) #define AUDIOS_BUFFSIZE (8*1024) #define AUDIOS_SAMPR5510 (5510) #define AUDIOS_SAMPR6620 (6620) #define AUDIOS_SAMPR8000 (8000) #define AUDIOS_SAMPR9600 (9600) #define AUDIOS_SAMPR11025 (11025) #define AUDIOS_SAMPR12000 (12000) #define AUDIOS_SAMPR16000 (16000) #define AUDIOS_SAMPR18900 (18900) #define AUDIOS_SAMPR22050 (22050) #define AUDIOS_SAMPR24000 (24000) #define AUDIOS_SAMPR27420 (27420) #define AUDIOS_SAMPR32000 (32000) #define AUDIOS_SAMPR33075 (33075) #define AUDIOS_SAMPR37800 (37800) #define AUDIOS_SAMPR44100 (44100) #define AUDIOS_SAMPR48000 (48000) #define AUDIOS_DEFAULT_SR AUDIOS_SAMPR8000 #define AUDIOS_DEFAULT_CH AUDIO_CHANNELS_MONO #define AUDIOS_DEFAULT_PREC AUDIO_PRECISION_8 #define AUDIOS_DEFAULT_ENC AUDIO_ENCODING_ULAW #define AUDIOS_DEFAULT_PGAIN (AUDIO_MAX_GAIN * 3 / 4) #define AUDIOS_DEFAULT_RGAIN (127) #define AUDIOS_DEFAULT_MONITOR_GAIN (0) #define AUDIOS_DEFAULT_BAL AUDIO_MID_BALANCE /* * Entry point routine prototypes */ static int sadasupport_open (queue_t * q, dev_t * devp, int flag, int sflag, cred_t * credp); static int sadasupport_close (queue_t * q, int flag, cred_t * credp); static int sadasupport_ad_set_config (audiohdl_t, int, int, int, int, int); static int sadasupport_ad_set_format (audiohdl_t, int, int, int, int, int, int); static int sadasupport_ad_start_play (audiohdl_t, int); static void sadasupport_ad_pause_play (audiohdl_t, int); static void sadasupport_ad_stop_play (audiohdl_t, int); static int sadasupport_ad_start_record (audiohdl_t, int); static void sadasupport_ad_stop_record (audiohdl_t, int); /* now, only support stereo */ static uint_t sadasupport_channels[] = { AUDIO_CHANNELS_STEREO, 0 }; static am_ad_cap_comb_t sadasupport_combinations[] = { {AUDIO_PRECISION_16, AUDIO_ENCODING_LINEAR}, {0} }; static uint_t sadasupport_mixer_srs[] = { AUDIOS_SAMPR8000, AUDIOS_SAMPR48000, 0 }; #ifdef SOL9 /* don't filter the higher sample rates, this causes the high to be lost */ static am_ad_src1_info_t sadasupport_play_sample_rates_info[] = { {AUDIOS_SAMPR8000, AUDIOS_SAMPR48000, 2, /* up 6, down 1 */ 3, (2 | AM_SRC1_FILTER), 0, 0, 1, 1, 0, 0, 3}, {AUDIOS_SAMPR9600, AUDIOS_SAMPR48000, 1, /* up 5, down 1 */ (5 | AM_SRC1_FILTER), 0, 0, 0, 1, 0, 0, 0, 3}, {AUDIOS_SAMPR11025, AUDIOS_SAMPR48000, 3, /* up 640, down 147 */ 10, 8, (8 | AM_SRC1_FILTER), 0, 7, 7, 3, 0, 3}, {AUDIOS_SAMPR12000, AUDIOS_SAMPR48000, 1, /* up 4, down 1 */ (4 | AM_SRC1_FILTER), 0, 0, 0, 1, 0, 0, 0, 3}, {AUDIOS_SAMPR16000, AUDIOS_SAMPR48000, 1, /* up 3, down 1 */ (3 | AM_SRC1_FILTER), 0, 0, 0, 1, 0, 0, 0, 3}, {AUDIOS_SAMPR18900, AUDIOS_SAMPR48000, 3, /* up 160, down 63 */ 8, 5, (4 | AM_SRC1_FILTER), 0, 7, 3, 3, 0, 3}, {AUDIOS_SAMPR22050, AUDIOS_SAMPR48000, 3, /* up 320, down 147 */ 10, 8, (4 | AM_SRC1_FILTER), 0, 7, 7, 3, 0, 3}, {AUDIOS_SAMPR24000, AUDIOS_SAMPR48000, 1, /* up 2, down 1 */ (2 | AM_SRC1_FILTER), 0, 0, 0, 1, 0, 0, 0, 3}, {AUDIOS_SAMPR32000, AUDIOS_SAMPR48000, 1, /* up 3, down 2 */ (3 | AM_SRC1_FILTER), 0, 0, 0, 2, 0, 0, 0, 3}, {AUDIOS_SAMPR33075, AUDIOS_SAMPR48000, 4, /* up 640, down 441 */ 8, 5, 4, 4, 7, 7, 3, 3, 3}, {AUDIOS_SAMPR37800, AUDIOS_SAMPR48000, 3, /* up 80, down 63 */ 5, 4, 4, 0, 7, 3, 3, 0, 3}, {AUDIOS_SAMPR44100, AUDIOS_SAMPR48000, 3, /* up 160, down 147 */ 8, 5, 4, 0, 7, 7, 3, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR48000, 1, /* up 1, down 1 */ 1, 0, 0, 0, 1, 0, 0, 0, 3}, {0} }; static am_ad_src1_info_t sadasupport_record_sample_rates_info[] = { {AUDIOS_SAMPR48000, AUDIOS_SAMPR8000, 1, /* up 1, down 6 */ 1, 0, 0, 0, 6, 0, 0, 0, 0}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR9600, 1, /* up 1, down 5 */ 1, 0, 0, 0, 5, 0, 0, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR11025, 3, /* up 147, down 640 */ 7, 7, (3 | AM_SRC1_FILTER), 0, 10, 8, 8, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR12000, 1, /* up 1, down 4 */ 1, 0, 0, 0, 4, 0, 0, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR16000, 1, /* up 1, down 3 */ 1, 0, 0, 0, 3, 0, 0, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR18900, 3, /* up 63, down 160 */ 7, 3, (3 | AM_SRC1_FILTER), 0, 8, 5, 4, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR22050, 3, /* up 147, down 320 */ 7, 7, (3 | AM_SRC1_FILTER), 0, 10, 8, 4, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR24000, 1, /* up 1, down 2 */ 1, 0, 0, 0, 2, 0, 0, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR32000, 1, /* up 2, down 3 */ (2 | AM_SRC1_FILTER), 0, 0, 0, 3, 0, 0, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR33075, 4, /* up 441, down 640 */ 7, 7, 3, 3, 8, 5, 4, 4, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR37800, 3, /* up 63, down 80 */ 7, 3, 3, 0, 5, 4, 4, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR44100, 3, /* up 147, down 160 */ 7, 7, 3, 0, 8, 5, 4, 0, 3}, {AUDIOS_SAMPR48000, AUDIOS_SAMPR48000, 3, /* up 1, down 1 */ 1, 0, 0, 0, 1, 0, 0, 0, 3}, {0} }; #endif #if 0 static uint_t sadasupport_compat_srs[] = { AUDIOS_SAMPR5510, AUDIOS_SAMPR6620, AUDIOS_SAMPR8000, AUDIOS_SAMPR9600, AUDIOS_SAMPR11025, AUDIOS_SAMPR12000, AUDIOS_SAMPR16000, AUDIOS_SAMPR18900, AUDIOS_SAMPR22050, AUDIOS_SAMPR24000, AUDIOS_SAMPR27420, AUDIOS_SAMPR32000, AUDIOS_SAMPR33075, AUDIOS_SAMPR37800, AUDIOS_SAMPR44100, AUDIOS_SAMPR48000, 0 }; #endif static am_ad_sample_rates_t sadasupport_mixer_sample_rates = { MIXER_SRS_FLAG_SR_LIMITS, sadasupport_mixer_srs }; #if 0 static am_ad_sample_rates_t sadasupport_compat_sample_rates = { MIXER_SRS_FLAG_SR_NOT_LIMITS, sadasupport_compat_srs }; #endif typedef struct { oss_device_t *osdev; kmutex_t inst_lock; /* * Fields related with the OSS audio device */ volatile int audio_busy; int audio_mode; int oss_audiodev; int play_frag; int rec_frag; int start_flags; int sample_rate; int mute; /* Play silence if mute is set */ /* * Fields related with the audiosup and mixer modules. */ audiohdl_t audio_handle; am_ad_info_t ad_info; /* audio device info state */ uint16_t vol_bits_mask; /* bits used to ctrl volume */ audio_info_t audios_defaults; /* default state for dev */ audio_device_t audios_dev_info; /* audio device info state */ #ifndef SOL9 ldi_handle_t lh; ldi_ident_t li; #endif } sadasupport_devc; static sadasupport_devc *devc = NULL; static struct fileinfo tmp_file = { 0 }; static am_ad_entry_t sadasupport_entry = { NULL, /* ad_setup() */ NULL, /* ad_teardown() */ sadasupport_ad_set_config, /* ad_set_config() */ sadasupport_ad_set_format, /* ad_set_format() */ sadasupport_ad_start_play, /* ad_start_play() */ sadasupport_ad_pause_play, /* ad_pause_play() */ sadasupport_ad_stop_play, /* ad_stop_play() */ sadasupport_ad_start_record, /* ad_start_record() */ sadasupport_ad_stop_record, /* ad_stop_record() */ NULL, /* ad_ioctl() */ NULL /* ad_iocdata() */ }; /* * STREAMS structures */ /* STREAMS driver id and limit value struct */ static struct module_info sadasupport_modinfo = { AUDIOS_IDNUM, /* module ID number */ AUDIOS_NAME, /* module name */ AUDIOS_MINPACKET, /* minimum packet size */ AUDIOS_MAXPACKET, /* maximum packet size */ AUDIOS_HIWATER, /* high water mark */ AUDIOS_LOWATER, /* low water mark */ }; /* STREAMS queue processing procedures structures */ /* read queue */ static struct qinit sadasupport_rqueue = { audio_sup_rput, /* put procedure */ audio_sup_rsvc, /* service procedure */ sadasupport_open, /* open procedure */ sadasupport_close, /* close procedure */ NULL, /* unused */ &sadasupport_modinfo, /* module parameters */ NULL /* module statistics */ }; /* write queue */ static struct qinit sadasupport_wqueue = { audio_sup_wput, /* write procedure */ audio_sup_wsvc, /* service procedure */ NULL, /* open procedure */ NULL, /* close procedure */ NULL, /* unused */ &sadasupport_modinfo, /* module parameters */ NULL /* module statistics */ }; /* STREAMS entity declaration structure */ struct streamtab oss_sadasupport_str_info = { &sadasupport_rqueue, /* read queue */ &sadasupport_wqueue, /* write queue */ NULL, /* mux lower read queue */ NULL, /* mux lower write queue */ }; /*ARGSUSED*/ static void input_callback (int dev, int parm) { dmap_p dmap; adev_p adev; unsigned char *buf; int samples, i; oss_native_word flags; adev = audio_engines[dev]; dmap = adev->dmap_in; if (dmap->dmabuf == NULL) { cmn_err (CE_WARN, "Dmabuf==NULL\n"); return; } MUTEX_ENTER (dmap->mutex, flags); samples = dmap->fragment_size / 2; /* Number of 16 bit samples */ buf = dmap->dmabuf + devc->rec_frag * dmap->fragment_size; if (dmap->nfrags == 0) { cmn_err (CE_WARN, "Nfrags==0\n"); MUTEX_EXIT (dmap->mutex, flags); return; } devc->rec_frag = (devc->rec_frag + 1) % dmap->nfrags; dmap->user_counter += dmap->fragment_size; MUTEX_EXIT (dmap->mutex, flags); /* Perform byte swapping (if necessary) */ if (do_byteswap) for (i = 0; i < samples * 2; i += 2) { unsigned char tmp; tmp = buf[i]; buf[i] = buf[i + 1]; buf[i + 1] = tmp; } am_send_audio (devc->audio_handle, buf, AUDIO_NO_CHANNEL, samples); } static int fill_play_buf (sadasupport_devc * devc); /*ARGSUSED*/ static void output_callback (int dev, int parm) { fill_play_buf (devc); } static int setup_device (sadasupport_devc * devc) { int fmt = AFMT_S16_NE; int ch = 2; int rate = devc->sample_rate; int mode; devc->mute = 0; /* Unmute every time the device gets opened */ mode = 0; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_COOKEDMODE, (ioctl_arg) & mode); #if 0 int frag; //frag=0x0002000d; /* Two 8k fragments */ frag = 0x0008000a; /* 1k fragments */ oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETFRAGMENT, (ioctl_arg) & frag); #endif do_byteswap = 0; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETFMT, (ioctl_arg) & fmt); if (fmt != AFMT_S16_NE) { fmt = AFMT_S16_OE; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETFMT, (ioctl_arg) & fmt); if (fmt != AFMT_S16_OE) { cmn_err (CE_WARN, "Internal error (format=%x)\n", fmt); return AUDIO_FAILURE; } do_byteswap = 1; } oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_CHANNELS, (ioctl_arg) & ch); if (ch != 2) { cmn_err (CE_WARN, "Internal error (channels=%d)\n", ch); return AUDIO_FAILURE; } oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SPEED, (ioctl_arg) & rate); if (rate != devc->sample_rate) { /* * Disabling SADA mixing/src may cause problems if the application wants * to use rate that is not supported by the device itself. In that case * we need to switch to the rate returned by the device. */ cmn_err (CE_WARN, "Returned sample rate %d is different than requested %d)\n", rate, devc->sample_rate); cmn_err (CE_CONT, "Switching to %d\n", rate); devc->sample_rate = rate; // return AUDIO_FAILURE; } return AUDIO_SUCCESS; } #ifndef SOL9 static int sadasupport_open (queue_t * q, dev_t * devp, int flag, int sflag, cred_t * credp) { int retval, i; int devnum; int dev = *devp; char *cmd; unsigned int bl_flags = 0; int oss_mode = 0; DDB (cmn_err (CE_CONT, "sadasupport_open(dev=%x, flag=%x, sflag=%x)\n", dev, flag, sflag)); if (devc == NULL) return EIO; oss_mode = OPEN_READWRITE; mutex_enter (&devc->inst_lock); if (devc->audio_busy++ == 0) { oss_audioinfo ai; int err; mutex_exit (&devc->inst_lock); tmp_file.acc_flags = 0; tmp_file.mode = oss_mode; devc->audio_mode = tmp_file.mode; DDB (cmn_err (CE_CONT, "Opening /dev/dsp (mode=%x)\n", tmp_file.mode)); if (ldi_ident_from_dev (dev, &devc->li) != 0) { cmn_err (CE_WARN, "ldi_ident_from_dev failed\n"); devc->audio_busy--; return EIO; } if ((err = ldi_open_by_name ("/dev/dsp", FWRITE | FREAD, credp, &devc->lh, devc->li)) != 0) { cmn_err (CE_WARN, "ldi_open_by_name(\"/dev/dsp\") failed, errno=%d\n", err); devc->audio_busy--; return -err; } ai.dev = -1; if ((err = ldi_ioctl (devc->lh, SNDCTL_ENGINEINFO, (intptr_t) & ai, FKIOCTL, credp, &retval)) != 0) { ldi_close (devc->lh, FREAD | FWRITE, credp); ldi_ident_release (devc->li); cmn_err (CE_WARN, "ldi_ioctl(SNDCTL_AUDIOINFO) failed, err=%d\n", err); devc->audio_busy--; return err; } devnum = ai.dev; DDB (cmn_err (CE_CONT, "Opened OSS audio engine %d:%s\n", devnum, ai.name)); if (devnum < 0 || devnum >= num_audio_engines) { cmn_err (CE_WARN, "OSS audio engine number %d is out of range.\n", devnum); ldi_close (devc->lh, FREAD | FWRITE, credp); ldi_ident_release (devc->li); devc->audio_busy--; return ENXIO; } devc->oss_audiodev = devnum; if (tmp_file.mode & OPEN_READ) audio_engines[devnum]->dmap_in->audio_callback = input_callback; if (tmp_file.mode & OPEN_WRITE) audio_engines[devnum]->dmap_out->audio_callback = output_callback; strcpy (audio_engines[devnum]->cmd, "SADA"); strcpy (audio_engines[devnum]->label, "SADA"); audio_engines[devnum]->pid = 0; if ((retval = setup_device (devc)) != AUDIO_SUCCESS) { cmn_err (CE_NOTE, "setup_device failed\n"); mutex_enter (&devc->inst_lock); if (--devc->audio_busy == 0) { ldi_close (devc->lh, FREAD | FWRITE, credp); ldi_ident_release (devc->li); mutex_exit (&devc->inst_lock); } else mutex_exit (&devc->inst_lock); return EIO; } } else { if (oss_mode & ~devc->audio_mode) { cmn_err (CE_NOTE, "Conflicting access modes\n"); if (--devc->audio_busy == 0) { mutex_exit (&devc->inst_lock); ldi_close (devc->lh, FREAD | FWRITE, credp); ldi_ident_release (devc->li); return EBUSY; } mutex_exit (&devc->inst_lock); return EBUSY; } mutex_exit (&devc->inst_lock); } DDB (cmn_err (CE_CONT, "Open count %d\n", devc->audio_busy)); DDB (cmn_err (CE_CONT, "audio_sup_open(q=%p, dev=%x, flag=%x, sflag=%x)\n", q, dev, flag, sflag)); if ((retval = audio_sup_open (q, devp, flag, sflag, credp)) != 0) { cmn_err (CE_NOTE, "audio_sup_open() returned %d\n", retval); mutex_enter (&devc->inst_lock); if (--devc->audio_busy == 0) { mutex_exit (&devc->inst_lock); ldi_close (devc->lh, FREAD | FWRITE, credp); ldi_ident_release (devc->li); } else mutex_exit (&devc->inst_lock); } return retval; } static int sadasupport_close (queue_t * q, int flag, cred_t * credp) { int retval, count; DDB (cmn_err (CE_CONT, "sadasupport_close(q=%p, flag=%x)\n", q, flag)); if (!devc->audio_busy) cmn_err (CE_WARN, "Close while not busy.\n"); retval = audio_sup_close (q, flag, credp); mutex_enter (&devc->inst_lock); count = --devc->audio_busy; mutex_exit (&devc->inst_lock); DDB (cmn_err (CE_CONT, "Open count (close) %d\n", count)); if (count == 0) { DDB (cmn_err (CE_CONT, "Closing OSS audiodev %d\n", devc->oss_audiodev)); ldi_close (devc->lh, FREAD | FWRITE, credp); ldi_ident_release (devc->li); devc->audio_mode = 0; } return retval; } #else #include "sadasupport_sol9.h" #endif static void set_port (sadasupport_devc * devc, int dir, int port) { int recdev = SOUND_MASK_MIC; if (dir == AUDIO_PLAY) /* Don't support this */ return; switch (port) { case AUDIO_MICROPHONE: recdev = SOUND_MASK_MIC; break; case AUDIO_LINE_IN: recdev = SOUND_MASK_LINE; break; case AUDIO_CD: recdev = SOUND_MASK_CD; break; } oss_audio_ioctl (devc->oss_audiodev, NULL, SOUND_MIXER_WRITE_RECSRC, (ioctl_arg) & recdev); /* Let's hope that worked */ } static void set_gain (sadasupport_devc * devc, int dir, int gain, int channel) { int cmd; int val; if (gain < 0) gain = 0; if (gain > 255) gain = 255; gain = (gain * 100) / 255; if (dir == AUDIO_PLAY) cmd = SNDCTL_DSP_GETPLAYVOL; else cmd = SNDCTL_DSP_GETRECVOL; if (oss_audio_ioctl (devc->oss_audiodev, NULL, cmd, (ioctl_arg) & val) < 0) return; switch (channel) { case 0: /* Left channel */ val = (val & 0xff00) | gain; break; case 1: /* Right channel */ val = (val & 0x00ff) | (gain << 8); break; } if (dir == AUDIO_PLAY) cmd = SNDCTL_DSP_SETPLAYVOL; else cmd = SNDCTL_DSP_SETRECVOL; oss_audio_ioctl (devc->oss_audiodev, NULL, cmd, (ioctl_arg) & val); } /*ARGSUSED*/ static void set_monitor_gain (sadasupport_devc * devc, int gain) { /* NOP. SADA applications are not allowed to change monitor gain. */ } /*ARGSUSED*/ static int sadasupport_ad_set_config (audiohdl_t ahandle, int stream, int command, int dir, int arg1, int arg2) { /* * We do not support most gain settings. They are incompatible with the way * how OSS 4.0 (and later) works. */ #if 0 typedef struct { uint_t cmd; char *name; } cmds_t; static const cmds_t cmds[] = { {AM_SET_GAIN, "AM_SET_GAIN"}, {AM_SET_GAIN_BAL, "AM_SET_GAIN_BAL"}, {AM_SET_PORT, "AM_SET_PORT"}, {AM_SET_MONITOR_GAIN, "AM_SET_MONITOR_GAIN"}, {AM_OUTPUT_MUTE, "AM_OUTPUT_MUTE"}, {AM_MONO_MIC, "AM_MONO_MIC"}, {AM_MIC_BOOST, "AM_MIC_BOOST"}, {AM_BASS_BOOST, "AM_BASS_BOOST"}, {AM_MID_BOOST, "AM_MID_BOOST"}, {AM_TREBLE_BOOST, "AM_TREBLE_BOOST"}, {AM_LOUDNESS, "AM_LOUDNESS"}, {AM_SET_DIAG_MODE, "AM_SET_DIAG_MODE"}, {0, NULL} }; #endif if (devc->audio_mode == 0) return AUDIO_SUCCESS; switch (command) { case AM_SET_PORT: set_port (devc, dir, arg1); break; case AM_SET_GAIN_BAL: return AUDIO_FAILURE; break; case AM_SET_GAIN: set_gain (devc, dir, arg1, arg2); break; case AM_SET_MONITOR_GAIN: set_monitor_gain (devc, arg1); break; case AM_OUTPUT_MUTE: devc->mute = arg1; break; default: #if 0 int i; cmn_err (CE_CONT, "sadasupport_ad_set_config(stream=%d, cmd=%x, dir=%d, arg1=%x, arg2=%x)\n", stream, command, dir, arg1, arg2); for (i = 0; cmds[i].cmd != 0; i++) if (cmds[i].cmd == command) { cmn_err (CE_CONT, " Unsupported mixer command =%s\n", cmds[i].name); break; } #endif break; } return AUDIO_SUCCESS; } /*ARGSUSED*/ static int sadasupport_ad_set_format (audiohdl_t ahandle, int stream, int dir, int sample_rate, int channels, int precision, int encoding) { DDB (cmn_err (CE_CONT, "sadasupport_ad_set_format(stream=%d, dir=%d, sr=%d, ch=%d, prec=%d, enc=%d)\n", stream, dir, sample_rate, channels, precision, encoding)); if (precision != AUDIO_PRECISION_16) { cmn_err (CE_WARN, "Sample size must be 16 bits.\n"); return AUDIO_FAILURE; } if (channels != AUDIO_CHANNELS_STEREO) { cmn_err (CE_WARN, "Channels must be stereo.\n"); return AUDIO_FAILURE; } if (encoding != AUDIO_ENCODING_LINEAR) { cmn_err (CE_WARN, "Channels must be stereo.\n"); return AUDIO_FAILURE; } devc->sample_rate = sample_rate; return AUDIO_SUCCESS; } static int fill_play_buf (sadasupport_devc * devc) { int rs; int dev; dmap_p dmap; adev_p adev; unsigned char *buf; int samples, i; oss_native_word flags; dev = devc->oss_audiodev; adev = audio_engines[dev]; dmap = adev->dmap_out; if (dmap->dmabuf == NULL) { cmn_err (CE_WARN, "Dmabuf==NULL\n"); return AUDIO_FAILURE; } MUTEX_ENTER (dmap->mutex, flags); samples = dmap->fragment_size / 2; /* Number of 16 bit samples */ buf = dmap->dmabuf + devc->play_frag * dmap->fragment_size; if (dmap->nfrags == 0) { cmn_err (CE_WARN, "Nfrags==0\n"); MUTEX_EXIT (dmap->mutex, flags); return AUDIO_FAILURE; } rs = am_get_audio (devc->audio_handle, buf, AUDIO_NO_CHANNEL, samples); if (devc->mute || rs == 0) /* Device paused or muted */ { memset (buf, 0, dmap->fragment_size); } else { /* Perform byte swapping (if necessary) */ if (do_byteswap) for (i = 0; i < samples * 2; i += 2) { unsigned char tmp; tmp = buf[i]; buf[i] = buf[i + 1]; buf[i + 1] = tmp; } } devc->play_frag = (devc->play_frag + 1) % dmap->nfrags; dmap->user_counter += dmap->fragment_size; MUTEX_EXIT (dmap->mutex, flags); return rs; } /*ARGSUSED*/ static int sadasupport_ad_start_play (audiohdl_t ahandle, int stream) { int dev; dmap_p dmap; adev_p adev; dev = devc->oss_audiodev; if (dev < 0 || dev >= num_audio_engines) { cmn_err (CE_CONT, "Bad oss_audiodev %d\n", dev); return AUDIO_FAILURE; } adev = audio_engines[dev]; dmap = adev->dmap_out; mutex_enter (&devc->inst_lock); /* * Already playing? */ if (devc->start_flags & PCM_ENABLE_OUTPUT) { mutex_exit (&devc->inst_lock); return 0; /* All buffers are full at this moment */ } DDB (cmn_err (CE_CONT, "sadasupport_ad_start_play(stream=%d)\n", stream)); /* * Skip the first fragment (fill with silence) to avoid underruns */ devc->play_frag = 1; dmap->user_counter = dmap->fragment_size; /* * Trigger the play engine */ dmap->dma_mode = PCM_ENABLE_OUTPUT; devc->start_flags &= ~PCM_ENABLE_OUTPUT; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETTRIGGER, (ioctl_arg) & devc->start_flags); devc->start_flags |= PCM_ENABLE_OUTPUT; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETTRIGGER, (ioctl_arg) & devc->start_flags); //dmap->user_counter += dmap->fragment_size; devc->audios_defaults.play.buffer_size = dmap->fragment_size; devc->ad_info.ad_play.ad_bsize = dmap->fragment_size; mutex_exit (&devc->inst_lock); return fill_play_buf (devc); } /*ARGSUSED*/ static void sadasupport_ad_stop_play (audiohdl_t ahandle, int stream) { DDB (cmn_err (CE_CONT, "sadasupport_ad_stop_play (stream=%d)\n", stream)); mutex_enter (&devc->inst_lock); devc->start_flags &= ~PCM_ENABLE_OUTPUT; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETTRIGGER, (ioctl_arg) & devc->start_flags); oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_HALT_OUTPUT, (ioctl_arg) NULL); mutex_exit (&devc->inst_lock); } static void sadasupport_ad_pause_play (audiohdl_t ahandle, int stream) { DDB (cmn_err (CE_CONT, "sadasupport_ad_pause_play\n")); sadasupport_ad_stop_play (ahandle, stream); } /*ARGSUSED*/ static int sadasupport_ad_start_record (audiohdl_t ahandle, int stream) { int dev; dmap_p dmap; adev_p adev; dev = devc->oss_audiodev; if (dev < 0 || dev >= num_audio_engines) { cmn_err (CE_CONT, "Bad oss_audiodev %d\n", dev); return AUDIO_FAILURE; } mutex_enter (&devc->inst_lock); adev = audio_engines[dev]; dmap = adev->dmap_in; /* * Already Recording? */ if (devc->start_flags & PCM_ENABLE_INPUT) { mutex_exit (&devc->inst_lock); return AUDIO_SUCCESS; /* All buffers are full at this moment */ } DDB (cmn_err (CE_CONT, "sadasupport_ad_start_record(stream=%d)\n", stream)); /* * Skip the first fragment (fill with silence) to avoid underruns */ devc->rec_frag = 0; dmap->user_counter = 0; /* * Trigger the rec engine */ dmap->dma_mode = PCM_ENABLE_INPUT; devc->start_flags &= ~PCM_ENABLE_INPUT; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETTRIGGER, (ioctl_arg) & devc->start_flags); devc->start_flags |= PCM_ENABLE_INPUT; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETTRIGGER, (ioctl_arg) & devc->start_flags); devc->audios_defaults.record.buffer_size = dmap->fragment_size; devc->ad_info.ad_record.ad_bsize = dmap->fragment_size; mutex_exit (&devc->inst_lock); return AUDIO_SUCCESS; } /*ARGSUSED*/ static void sadasupport_ad_stop_record (audiohdl_t ahandle, int stream) { DDB (cmn_err (CE_CONT, "sadasupport_ad_stop_record(stream=%d)\n", stream)); mutex_enter (&devc->inst_lock); devc->start_flags &= ~PCM_ENABLE_INPUT; oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_SETTRIGGER, (ioctl_arg) & devc->start_flags); oss_audio_ioctl (devc->oss_audiodev, NULL, SNDCTL_DSP_HALT_INPUT, (ioctl_arg) NULL); mutex_exit (&devc->inst_lock); } static int sadasupport_init_state (sadasupport_devc * devc, dev_info_t * dip) { int rints = 175; int pints = 175; int cdrom; int mode; devc->vol_bits_mask = 5; cdrom = ddi_prop_get_int (DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "cdrom", 0); /* get the mode from the .conf file */ if (ddi_prop_get_int (DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "mixer-mode", 1)) { mode = AM_MIXER_MODE; } else { mode = AM_COMPAT_MODE; } /* fill in the device default state */ devc->audios_defaults.play.sample_rate = AUDIOS_DEFAULT_SR; devc->audios_defaults.play.channels = AUDIOS_DEFAULT_CH; devc->audios_defaults.play.precision = AUDIOS_DEFAULT_PREC; devc->audios_defaults.play.encoding = AUDIOS_DEFAULT_ENC; devc->audios_defaults.play.gain = AUDIOS_DEFAULT_PGAIN; devc->audios_defaults.play.port = AUDIO_SPEAKER | AUDIO_LINE_OUT; devc->audios_defaults.play.avail_ports = AUDIO_SPEAKER | AUDIO_LINE_OUT; devc->audios_defaults.play.mod_ports = AUDIO_SPEAKER | AUDIO_LINE_OUT; devc->audios_defaults.play.buffer_size = AUDIOS_BUFFSIZE; devc->audios_defaults.play.balance = AUDIOS_DEFAULT_BAL; devc->audios_defaults.record.sample_rate = AUDIOS_DEFAULT_SR; devc->audios_defaults.record.channels = AUDIOS_DEFAULT_CH; devc->audios_defaults.record.precision = AUDIOS_DEFAULT_PREC; devc->audios_defaults.record.encoding = AUDIOS_DEFAULT_ENC; devc->audios_defaults.record.gain = AUDIOS_DEFAULT_PGAIN; devc->audios_defaults.record.port = AUDIO_MICROPHONE; devc->audios_defaults.record.avail_ports = AUDIO_MICROPHONE | AUDIO_LINE_IN | AUDIO_CD; devc->audios_defaults.record.mod_ports = AUDIO_MICROPHONE | AUDIO_LINE_IN | AUDIO_CD; devc->audios_defaults.record.buffer_size = AUDIOS_BUFFSIZE; devc->audios_defaults.record.balance = AUDIOS_DEFAULT_BAL; devc->audios_defaults.monitor_gain = AUDIOS_DEFAULT_MONITOR_GAIN; devc->audios_defaults.output_muted = B_FALSE; devc->audios_defaults.ref_cnt = B_FALSE; devc->audios_defaults.hw_features = AUDIO_HWFEATURE_DUPLEX | AUDIO_HWFEATURE_PLAY | AUDIO_HWFEATURE_IN2OUT | AUDIO_HWFEATURE_RECORD; devc->audios_defaults.sw_features = AUDIO_SWFEATURE_MIXER; if (cdrom) { devc->audios_defaults.record.avail_ports |= AUDIO_CD; devc->audios_defaults.record.mod_ports |= AUDIO_CD; } #if 0 devc->audios_psample_rate = devc->audios_defaults.play.sample_rate; devc->audios_pchannels = devc->audios_defaults.play.channels; devc->audios_pprecision = devc->audios_defaults.play.precision; devc->audios_csample_rate = devc->audios_defaults.record.sample_rate; devc->audios_cchannels = devc->audios_defaults.record.channels; devc->audios_cprecision = devc->audios_defaults.record.precision; #endif /* * fill in the ad_info structure */ devc->ad_info.ad_mode = mode; devc->ad_info.ad_int_vers = AM_VERSION; devc->ad_info.ad_add_mode = NULL; devc->ad_info.ad_codec_type = AM_TRAD_CODEC; devc->ad_info.ad_defaults = &devc->audios_defaults; devc->ad_info.ad_play_comb = sadasupport_combinations; devc->ad_info.ad_rec_comb = sadasupport_combinations; devc->ad_info.ad_entry = &sadasupport_entry; devc->ad_info.ad_dev_info = &devc->audios_dev_info; devc->ad_info.ad_diag_flags = AM_DIAG_INTERNAL_LOOP; devc->ad_info.ad_diff_flags = AM_DIFF_SR | AM_DIFF_CH | AM_DIFF_PREC | AM_DIFF_ENC; devc->ad_info.ad_assist_flags = AM_ASSIST_MIC; devc->ad_info.ad_misc_flags = AM_MISC_RP_EXCL | AM_MISC_MONO_DUP; devc->ad_info.ad_translate_flags = AM_MISC_8_P_TRANSLATE | AM_MISC_8_R_TRANSLATE; devc->ad_info.ad_num_mics = 1; /* play capabilities */ devc->ad_info.ad_play.ad_mixer_srs = sadasupport_mixer_sample_rates; devc->ad_info.ad_play.ad_compat_srs = sadasupport_mixer_sample_rates; #ifdef SOL9 devc->ad_info.ad_play.ad_conv = &am_src1; devc->ad_info.ad_play.ad_sr_info = sadasupport_play_sample_rates_info; #else devc->ad_info.ad_play.ad_conv = &am_src2; devc->ad_info.ad_play.ad_sr_info = NULL; #endif devc->ad_info.ad_play.ad_chs = sadasupport_channels; devc->ad_info.ad_play.ad_int_rate = pints; devc->ad_info.ad_play.ad_max_chs = 32; devc->ad_info.ad_play.ad_bsize = AUDIOS_BUFFSIZE; /* record capabilities */ devc->ad_info.ad_record.ad_mixer_srs = sadasupport_mixer_sample_rates; devc->ad_info.ad_record.ad_compat_srs = sadasupport_mixer_sample_rates; #ifdef SOL9 devc->ad_info.ad_record.ad_conv = &am_src1; devc->ad_info.ad_record.ad_sr_info = sadasupport_record_sample_rates_info; #else devc->ad_info.ad_record.ad_conv = &am_src2; devc->ad_info.ad_record.ad_sr_info = NULL; #endif devc->ad_info.ad_record.ad_chs = sadasupport_channels; devc->ad_info.ad_record.ad_int_rate = rints; devc->ad_info.ad_record.ad_max_chs = 32; devc->ad_info.ad_record.ad_bsize = AUDIOS_BUFFSIZE; /* fill in device info strings */ (void) strcpy (devc->audios_dev_info.name, AUDIOS_DEV_NAME); (void) strcpy (devc->audios_dev_info.config, AUDIOS_DEV_CONFIG); (void) strcpy (devc->audios_dev_info.version, AUDIOS_DEV_VERSION); #if 0 devc->play_buf_size = AUDIOS_SAMPR48000 * AUDIO_CHANNELS_STEREO * (AUDIO_PRECISION_16 >> AUDIO_PRECISION_SHIFT) / pints; devc->play_buf_size += AUDIOS_MOD_SIZE - (devc->play_buf_size % AUDIOS_MOD_SIZE); devc->record_buf_size = AUDIOS_SAMPR48000 * AUDIO_CHANNELS_STEREO * (AUDIO_PRECISION_16 >> AUDIO_PRECISION_SHIFT) / rints; devc->record_buf_size += AUDIOS_MOD_SIZE - (devc->record_buf_size % AUDIOS_MOD_SIZE); #endif return (AUDIO_SUCCESS); } /* sadasupport_init_state */ int oss_sadasupport_attach (oss_device_t * osdev) { #ifndef SOL9 audio_sup_reg_data_t data; #endif int instance; /* extern struct cb_ops ossdrv_streams_cb_ops; */ sadasupport_mixer_srs[1] = sadasupport_rate; /* Set the maximum rate */ instance = ddi_get_instance (osdev->dip); DDB (cmn_err (CE_CONT, "sadasupport_attach\n")); if (devc != NULL) { cmn_err (CE_WARN, "Multiple instances are not permitted\n"); return 0; } devc = PMALLOC (osdev, sizeof (*devc)); devc->osdev = osdev; devc->oss_audiodev = -1; devc->start_flags = 0; mutex_init (&devc->inst_lock, NULL, MUTEX_DRIVER, NULL); osdev->devc = devc; #ifdef SOL9 if ((devc->audio_handle = audio_sup_attach (osdev->dip, DDI_ATTACH)) == NULL) { audio_sup_log (NULL, CE_WARN, "!%s%d: attach() audio_sup_attach () failed", DRIVER_NICK, instance); return 0; } DDB (cmn_err (CE_CONT, "audio_sup_attach() OK, handle=%p\n", devc->audio_handle)); #else data.asrd_version = AUDIOSUP_VERSION; data.asrd_key = NULL; if ((devc->audio_handle = audio_sup_register (osdev->dip, &data)) == NULL) { audio_sup_log (NULL, CE_WARN, "!%s%d: attach() audio_sup_register() failed", DRIVER_NICK, instance); return 0; } DDB (cmn_err (CE_CONT, "audio_sup_register() OK, handle=%x\n", devc->audio_handle)); #endif /* Save private data */ audio_sup_set_private (devc->audio_handle, devc); if ((sadasupport_init_state (devc, osdev->dip)) != AUDIO_SUCCESS) { audio_sup_log (devc->audio_handle, CE_WARN, "!attach() init state structure failed"); return 0; } /* call the mixer attach() routine */ if (am_attach (devc->audio_handle, DDI_ATTACH, &devc->ad_info) != AUDIO_SUCCESS) { audio_sup_log (devc->audio_handle, CE_WARN, "!attach() am_attach() failed"); return 0; } oss_register_device (osdev, "SADA compatibility layer"); ddi_report_dev (osdev->dip); /* * Take a copy of the callback options and replace the open/close * entry points with our own version. */ return 1; } int oss_sadasupport_detach (oss_device_t * osdev) { /* int instance; */ sadasupport_devc *devc = osdev->devc; if (devc != NULL && devc->audio_busy) return 0; /* instance = ddi_get_instance (osdev->dip); */ #if 0 /* stop DMA engines */ mutex_enter (&devc->inst_lock); sadasupport_stop_dma (devc); mutex_exit (&devc->inst_lock); /* remove the interrupt handler */ ddi_remove_intr (dip, 0, devc->intr_iblock); /* free DMA memory */ sadasupport_free_sample_buf (devc, &devc->play_buf); sadasupport_free_sample_buf (devc, &devc->record_buf); /* free the kernel statistics structure */ if (devc->audios_ksp) { kstat_delete (devc->audios_ksp); } #endif /* detach audio mixer */ (void) am_detach (devc->audio_handle, DDI_DETACH); /* * call the audio support module's detach routine to remove this * driver completely from the audio driver architecture. */ #ifdef SOL9 (void) audio_sup_detach (devc->audio_handle, DDI_DETACH); #else (void) audio_sup_unregister (devc->audio_handle); #endif mutex_destroy (&devc->inst_lock); devc = NULL; return 1; }