version 1.28, 2019/07/10 13:26:47 |
version 1.28.2.14, 2020/05/18 18:05:34 |
|
|
* set_port - x + |
* set_port - x + |
* get_port - x + |
* get_port - x + |
* query_devinfo - x |
* query_devinfo - x |
* allocm - - + (*1) |
* allocm - - + |
* freem - - + (*1) |
* freem - - + |
* round_buffersize - x |
* round_buffersize - x |
* get_props - x Called at attach time |
* get_props - x Called at attach time |
* trigger_output x x + |
* trigger_output x x + |
|
|
* dev_ioctl - x |
* dev_ioctl - x |
* get_locks - - Called at attach time |
* get_locks - - Called at attach time |
* |
* |
* *1 Note: Before 8.0, since these have been called only at attach time, |
|
* neither lock were necessary. Currently, on the other hand, since |
|
* these may be also called after attach, the thread lock is required. |
|
* |
|
* In addition, there is an additional lock. |
* In addition, there is an additional lock. |
* |
* |
* - track->lock. This is an atomic variable and is similar to the |
* - track->lock. This is an atomic variable and is similar to the |
Line 466 audio_track_bufstat(audio_track_t *track |
|
Line 462 audio_track_bufstat(audio_track_t *track |
|
int audio_idle_timeout = 30; |
int audio_idle_timeout = 30; |
#endif |
#endif |
|
|
|
/* Number of elements of async mixer's pid */ |
|
#define AM_CAPACITY (4) |
|
|
struct portname { |
struct portname { |
const char *name; |
const char *name; |
int mask; |
int mask; |
Line 497 static void audio_mixer_restore(struct a |
|
Line 496 static void audio_mixer_restore(struct a |
|
static void audio_softintr_rd(void *); |
static void audio_softintr_rd(void *); |
static void audio_softintr_wr(void *); |
static void audio_softintr_wr(void *); |
|
|
static int audio_enter_exclusive(struct audio_softc *); |
static int audio_exlock_mutex_enter(struct audio_softc *); |
static void audio_exit_exclusive(struct audio_softc *); |
static void audio_exlock_mutex_exit(struct audio_softc *); |
|
static int audio_exlock_enter(struct audio_softc *); |
|
static void audio_exlock_exit(struct audio_softc *); |
|
static struct audio_softc *audio_file_enter(audio_file_t *, struct psref *); |
|
static void audio_file_exit(struct audio_softc *, struct psref *); |
static int audio_track_waitio(struct audio_softc *, audio_track_t *); |
static int audio_track_waitio(struct audio_softc *, audio_track_t *); |
|
|
static int audioclose(struct file *); |
static int audioclose(struct file *); |
Line 519 static int filt_audioread_event(struct |
|
Line 522 static int filt_audioread_event(struct |
|
static int audio_open(dev_t, struct audio_softc *, int, int, struct lwp *, |
static int audio_open(dev_t, struct audio_softc *, int, int, struct lwp *, |
audio_file_t **); |
audio_file_t **); |
static int audio_close(struct audio_softc *, audio_file_t *); |
static int audio_close(struct audio_softc *, audio_file_t *); |
|
static int audio_unlink(struct audio_softc *, audio_file_t *); |
static int audio_read(struct audio_softc *, struct uio *, int, audio_file_t *); |
static int audio_read(struct audio_softc *, struct uio *, int, audio_file_t *); |
static int audio_write(struct audio_softc *, struct uio *, int, audio_file_t *); |
static int audio_write(struct audio_softc *, struct uio *, int, audio_file_t *); |
static void audio_file_clear(struct audio_softc *, audio_file_t *); |
static void audio_file_clear(struct audio_softc *, audio_file_t *); |
Line 539 static int audio_query_devinfo(struct au |
|
Line 543 static int audio_query_devinfo(struct au |
|
static __inline int audio_track_readablebytes(const audio_track_t *); |
static __inline int audio_track_readablebytes(const audio_track_t *); |
static int audio_file_setinfo(struct audio_softc *, audio_file_t *, |
static int audio_file_setinfo(struct audio_softc *, audio_file_t *, |
const struct audio_info *); |
const struct audio_info *); |
static int audio_track_setinfo_check(audio_format2_t *, |
static int audio_track_setinfo_check(audio_track_t *, |
const struct audio_prinfo *); |
audio_format2_t *, const struct audio_prinfo *); |
static void audio_track_setinfo_water(audio_track_t *, |
static void audio_track_setinfo_water(audio_track_t *, |
const struct audio_info *); |
const struct audio_info *); |
static int audio_hw_setinfo(struct audio_softc *, const struct audio_info *, |
static int audio_hw_setinfo(struct audio_softc *, const struct audio_info *, |
Line 557 static int audio_mixers_init(struct audi |
|
Line 561 static int audio_mixers_init(struct audi |
|
const audio_format2_t *, const audio_format2_t *, |
const audio_format2_t *, const audio_format2_t *, |
const audio_filter_reg_t *, const audio_filter_reg_t *); |
const audio_filter_reg_t *, const audio_filter_reg_t *); |
static int audio_select_freq(const struct audio_format *); |
static int audio_select_freq(const struct audio_format *); |
static int audio_hw_probe(struct audio_softc *, int, int *, |
static int audio_hw_probe(struct audio_softc *, audio_format2_t *, int); |
audio_format2_t *, audio_format2_t *); |
|
static int audio_hw_probe_fmt(struct audio_softc *, audio_format2_t *, int); |
|
static int audio_hw_validate_format(struct audio_softc *, int, |
static int audio_hw_validate_format(struct audio_softc *, int, |
const audio_format2_t *); |
const audio_format2_t *); |
static int audio_mixers_set_format(struct audio_softc *, |
static int audio_mixers_set_format(struct audio_softc *, |
Line 606 static void mixer_init(struct audio_soft |
|
Line 608 static void mixer_init(struct audio_soft |
|
static int mixer_open(dev_t, struct audio_softc *, int, int, struct lwp *); |
static int mixer_open(dev_t, struct audio_softc *, int, int, struct lwp *); |
static int mixer_close(struct audio_softc *, audio_file_t *); |
static int mixer_close(struct audio_softc *, audio_file_t *); |
static int mixer_ioctl(struct audio_softc *, u_long, void *, int, struct lwp *); |
static int mixer_ioctl(struct audio_softc *, u_long, void *, int, struct lwp *); |
static void mixer_remove(struct audio_softc *); |
static void mixer_async_add(struct audio_softc *, pid_t); |
|
static void mixer_async_remove(struct audio_softc *, pid_t); |
static void mixer_signal(struct audio_softc *); |
static void mixer_signal(struct audio_softc *); |
|
|
static int au_portof(struct audio_softc *, char *, int); |
static int au_portof(struct audio_softc *, char *, int); |
Line 810 static const struct portname otable[] = |
|
Line 813 static const struct portname otable[] = |
|
{ 0, 0 } |
{ 0, 0 } |
}; |
}; |
|
|
|
static struct psref_class *audio_psref_class __read_mostly; |
|
|
CFATTACH_DECL3_NEW(audio, sizeof(struct audio_softc), |
CFATTACH_DECL3_NEW(audio, sizeof(struct audio_softc), |
audiomatch, audioattach, audiodetach, audioactivate, audiorescan, |
audiomatch, audioattach, audiodetach, audioactivate, audiorescan, |
audiochilddet, DVF_DETACH_SHUTDOWN); |
audiochilddet, DVF_DETACH_SHUTDOWN); |
Line 877 audioattach(device_t parent, device_t se |
|
Line 882 audioattach(device_t parent, device_t se |
|
sc->hw_hdl = hdlp; |
sc->hw_hdl = hdlp; |
sc->hw_dev = parent; |
sc->hw_dev = parent; |
|
|
|
sc->sc_exlock = 1; |
sc->sc_blk_ms = AUDIO_BLK_MS; |
sc->sc_blk_ms = AUDIO_BLK_MS; |
SLIST_INIT(&sc->sc_files); |
SLIST_INIT(&sc->sc_files); |
cv_init(&sc->sc_exlockcv, "audiolk"); |
cv_init(&sc->sc_exlockcv, "audiolk"); |
|
sc->sc_am_capacity = 0; |
|
sc->sc_am_used = 0; |
|
sc->sc_am = NULL; |
|
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_lock); |
sc->sc_props = hw_if->get_props(sc->hw_hdl); |
sc->sc_props = hw_if->get_props(sc->hw_hdl); |
Line 927 audioattach(device_t parent, device_t se |
|
Line 936 audioattach(device_t parent, device_t se |
|
memset(&rhwfmt, 0, sizeof(rhwfmt)); |
memset(&rhwfmt, 0, sizeof(rhwfmt)); |
memset(&pfil, 0, sizeof(pfil)); |
memset(&pfil, 0, sizeof(pfil)); |
memset(&rfil, 0, sizeof(rfil)); |
memset(&rfil, 0, sizeof(rfil)); |
mutex_enter(sc->sc_lock); |
if (has_indep) { |
error = audio_hw_probe(sc, has_indep, &mode, &phwfmt, &rhwfmt); |
int perror, rerror; |
if (error) { |
|
mutex_exit(sc->sc_lock); |
/* On independent devices, probe separately. */ |
aprint_error_dev(self, "audio_hw_probe failed, " |
perror = audio_hw_probe(sc, &phwfmt, AUMODE_PLAY); |
"error = %d\n", error); |
rerror = audio_hw_probe(sc, &rhwfmt, AUMODE_RECORD); |
goto bad; |
if (perror && rerror) { |
} |
aprint_error_dev(self, "audio_hw_probe failed, " |
if (mode == 0) { |
"perror = %d, rerror = %d\n", perror, rerror); |
mutex_exit(sc->sc_lock); |
goto bad; |
aprint_error_dev(self, "audio_hw_probe failed, no mode\n"); |
} |
goto bad; |
if (perror) { |
|
mode &= ~AUMODE_PLAY; |
|
aprint_error_dev(self, "audio_hw_probe failed with " |
|
"%d, playback disabled\n", perror); |
|
} |
|
if (rerror) { |
|
mode &= ~AUMODE_RECORD; |
|
aprint_error_dev(self, "audio_hw_probe failed with " |
|
"%d, capture disabled\n", rerror); |
|
} |
|
} else { |
|
/* |
|
* On non independent devices or uni-directional devices, |
|
* probe once (simultaneously). |
|
*/ |
|
audio_format2_t *fmt = has_playback ? &phwfmt : &rhwfmt; |
|
error = audio_hw_probe(sc, fmt, mode); |
|
if (error) { |
|
aprint_error_dev(self, "audio_hw_probe failed, " |
|
"error = %d\n", error); |
|
goto bad; |
|
} |
|
if (has_playback && has_capture) |
|
rhwfmt = phwfmt; |
} |
} |
|
|
/* Init hardware. */ |
/* Init hardware. */ |
/* hw_probe() also validates [pr]hwfmt. */ |
/* hw_probe() also validates [pr]hwfmt. */ |
error = audio_hw_set_format(sc, mode, &phwfmt, &rhwfmt, &pfil, &rfil); |
error = audio_hw_set_format(sc, mode, &phwfmt, &rhwfmt, &pfil, &rfil); |
if (error) { |
if (error) { |
mutex_exit(sc->sc_lock); |
|
aprint_error_dev(self, "audio_hw_set_format failed, " |
aprint_error_dev(self, "audio_hw_set_format failed, " |
"error = %d\n", error); |
"error = %d\n", error); |
goto bad; |
goto bad; |
Line 955 audioattach(device_t parent, device_t se |
|
Line 987 audioattach(device_t parent, device_t se |
|
* attach time, we assume a success. |
* attach time, we assume a success. |
*/ |
*/ |
error = audio_mixers_init(sc, mode, &phwfmt, &rhwfmt, &pfil, &rfil); |
error = audio_mixers_init(sc, mode, &phwfmt, &rhwfmt, &pfil, &rfil); |
mutex_exit(sc->sc_lock); |
|
if (sc->sc_pmixer == NULL && sc->sc_rmixer == NULL) { |
if (sc->sc_pmixer == NULL && sc->sc_rmixer == NULL) { |
aprint_error_dev(self, "audio_mixers_init failed, " |
aprint_error_dev(self, "audio_mixers_init failed, " |
"error = %d\n", error); |
"error = %d\n", error); |
goto bad; |
goto bad; |
} |
} |
|
|
|
sc->sc_psz = pserialize_create(); |
|
psref_target_init(&sc->sc_psref, audio_psref_class); |
|
|
selinit(&sc->sc_wsel); |
selinit(&sc->sc_wsel); |
selinit(&sc->sc_rsel); |
selinit(&sc->sc_rsel); |
|
|
Line 1044 audioattach(device_t parent, device_t se |
|
Line 1078 audioattach(device_t parent, device_t se |
|
#endif |
#endif |
|
|
audiorescan(self, "audio", NULL); |
audiorescan(self, "audio", NULL); |
|
sc->sc_exlock = 0; |
return; |
return; |
|
|
bad: |
bad: |
/* Clearing hw_if means that device is attached but disabled. */ |
/* Clearing hw_if means that device is attached but disabled. */ |
sc->hw_if = NULL; |
sc->hw_if = NULL; |
|
sc->sc_exlock = 0; |
aprint_error_dev(sc->sc_dev, "disabled\n"); |
aprint_error_dev(sc->sc_dev, "disabled\n"); |
return; |
return; |
} |
} |
|
|
audiodetach(device_t self, int flags) |
audiodetach(device_t self, int flags) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
int maj, mn; |
struct audio_file *file; |
int error; |
int error; |
|
|
sc = device_private(self); |
sc = device_private(self); |
Line 1234 audiodetach(device_t self, int flags) |
|
Line 1270 audiodetach(device_t self, int flags) |
|
if (error) |
if (error) |
return error; |
return error; |
|
|
|
/* delete sysctl nodes */ |
|
sysctl_teardown(&sc->sc_log); |
|
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_lock); |
sc->sc_dying = true; |
sc->sc_dying = true; |
cv_broadcast(&sc->sc_exlockcv); |
cv_broadcast(&sc->sc_exlockcv); |
Line 1241 audiodetach(device_t self, int flags) |
|
Line 1280 audiodetach(device_t self, int flags) |
|
cv_broadcast(&sc->sc_pmixer->outcv); |
cv_broadcast(&sc->sc_pmixer->outcv); |
if (sc->sc_rmixer) |
if (sc->sc_rmixer) |
cv_broadcast(&sc->sc_rmixer->outcv); |
cv_broadcast(&sc->sc_rmixer->outcv); |
mutex_exit(sc->sc_lock); |
|
|
|
/* delete sysctl nodes */ |
/* Prevent new users */ |
sysctl_teardown(&sc->sc_log); |
SLIST_FOREACH(file, &sc->sc_files, entry) { |
|
atomic_store_relaxed(&file->dying, true); |
|
} |
|
|
/* locate the major number */ |
/* |
maj = cdevsw_lookup_major(&audio_cdevsw); |
* Wait for existing users to drain. |
|
* - pserialize_perform waits for all pserialize_read sections on |
|
* all CPUs; after this, no more new psref_acquire can happen. |
|
* - psref_target_destroy waits for all extant acquired psrefs to |
|
* be psref_released. |
|
*/ |
|
pserialize_perform(sc->sc_psz); |
|
mutex_exit(sc->sc_lock); |
|
psref_target_destroy(&sc->sc_psref, audio_psref_class); |
|
|
/* |
/* |
* Nuke the vnodes for any open instances (calls close). |
* We are now guaranteed that there are no calls to audio fileops |
* Will wait until any activity on the device nodes has ceased. |
* that hold sc, and any new calls with files that were for sc will |
|
* fail. Thus, we now have exclusive access to the softc. |
*/ |
*/ |
mn = device_unit(self); |
sc->sc_exlock = 1; |
vdevgone(maj, mn | SOUND_DEVICE, mn | SOUND_DEVICE, VCHR); |
|
vdevgone(maj, mn | AUDIO_DEVICE, mn | AUDIO_DEVICE, VCHR); |
/* |
vdevgone(maj, mn | AUDIOCTL_DEVICE, mn | AUDIOCTL_DEVICE, VCHR); |
* Nuke all open instances. |
vdevgone(maj, mn | MIXER_DEVICE, mn | MIXER_DEVICE, VCHR); |
* Here, we no longer need any locks to traverse sc_files. |
|
*/ |
|
while ((file = SLIST_FIRST(&sc->sc_files)) != NULL) { |
|
audio_unlink(sc, file); |
|
} |
|
|
pmf_event_deregister(self, PMFE_AUDIO_VOLUME_DOWN, |
pmf_event_deregister(self, PMFE_AUDIO_VOLUME_DOWN, |
audio_volume_down, true); |
audio_volume_down, true); |
Line 1275 audiodetach(device_t self, int flags) |
|
Line 1328 audiodetach(device_t self, int flags) |
|
pmf_device_deregister(self); |
pmf_device_deregister(self); |
|
|
/* Free resources */ |
/* Free resources */ |
mutex_enter(sc->sc_lock); |
|
if (sc->sc_pmixer) { |
if (sc->sc_pmixer) { |
audio_mixer_destroy(sc, sc->sc_pmixer); |
audio_mixer_destroy(sc, sc->sc_pmixer); |
kmem_free(sc->sc_pmixer, sizeof(*sc->sc_pmixer)); |
kmem_free(sc->sc_pmixer, sizeof(*sc->sc_pmixer)); |
Line 1284 audiodetach(device_t self, int flags) |
|
Line 1336 audiodetach(device_t self, int flags) |
|
audio_mixer_destroy(sc, sc->sc_rmixer); |
audio_mixer_destroy(sc, sc->sc_rmixer); |
kmem_free(sc->sc_rmixer, sizeof(*sc->sc_rmixer)); |
kmem_free(sc->sc_rmixer, sizeof(*sc->sc_rmixer)); |
} |
} |
mutex_exit(sc->sc_lock); |
if (sc->sc_am) |
|
kern_free(sc->sc_am); |
|
|
seldestroy(&sc->sc_wsel); |
seldestroy(&sc->sc_wsel); |
seldestroy(&sc->sc_rsel); |
seldestroy(&sc->sc_rsel); |
Line 1354 audio_attach_mi(const struct audio_hw_if |
|
Line 1407 audio_attach_mi(const struct audio_hw_if |
|
} |
} |
|
|
/* |
/* |
* Acquire sc_lock and enter exlock critical section. |
* Enter critical section and also keep sc_lock. |
* If successful, it returns 0. Otherwise returns errno. |
* If successful, returns 0 with sc_lock held. Otherwise returns errno. |
|
* Must be called without sc_lock held. |
*/ |
*/ |
static int |
static int |
audio_enter_exclusive(struct audio_softc *sc) |
audio_exlock_mutex_enter(struct audio_softc *sc) |
{ |
{ |
int error; |
int error; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_lock); |
if (sc->sc_dying) { |
if (sc->sc_dying) { |
mutex_exit(sc->sc_lock); |
mutex_exit(sc->sc_lock); |
Line 1386 audio_enter_exclusive(struct audio_softc |
|
Line 1438 audio_enter_exclusive(struct audio_softc |
|
} |
} |
|
|
/* |
/* |
* Leave exlock critical section and release sc_lock. |
* Exit critical section and exit sc_lock. |
* Must be called with sc_lock held. |
* Must be called with sc_lock held. |
*/ |
*/ |
static void |
static void |
audio_exit_exclusive(struct audio_softc *sc) |
audio_exlock_mutex_exit(struct audio_softc *sc) |
{ |
{ |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock); |
|
|
|
/* Leave critical section */ |
|
sc->sc_exlock = 0; |
sc->sc_exlock = 0; |
cv_broadcast(&sc->sc_exlockcv); |
cv_broadcast(&sc->sc_exlockcv); |
mutex_exit(sc->sc_lock); |
mutex_exit(sc->sc_lock); |
} |
} |
|
|
/* |
/* |
|
* Enter critical section. |
|
* If successful, it returns 0. Otherwise returns errno. |
|
* Must be called without sc_lock held. |
|
* This function returns without sc_lock held. |
|
*/ |
|
static int |
|
audio_exlock_enter(struct audio_softc *sc) |
|
{ |
|
int error; |
|
|
|
error = audio_exlock_mutex_enter(sc); |
|
if (error) |
|
return error; |
|
mutex_exit(sc->sc_lock); |
|
return 0; |
|
} |
|
|
|
/* |
|
* Exit critical section. |
|
* Must be called without sc_lock held. |
|
*/ |
|
static void |
|
audio_exlock_exit(struct audio_softc *sc) |
|
{ |
|
|
|
mutex_enter(sc->sc_lock); |
|
audio_exlock_mutex_exit(sc); |
|
} |
|
|
|
/* |
|
* Acquire sc from file, and increment the psref count. |
|
* If successful, returns sc. Otherwise returns NULL. |
|
*/ |
|
struct audio_softc * |
|
audio_file_enter(audio_file_t *file, struct psref *refp) |
|
{ |
|
int s; |
|
bool dying; |
|
|
|
/* psref(9) forbids to migrate CPUs */ |
|
curlwp_bind(); |
|
|
|
/* Block audiodetach while we acquire a reference */ |
|
s = pserialize_read_enter(); |
|
|
|
/* If close or audiodetach already ran, tough -- no more audio */ |
|
dying = atomic_load_relaxed(&file->dying); |
|
if (dying) { |
|
pserialize_read_exit(s); |
|
return NULL; |
|
} |
|
|
|
/* Acquire a reference */ |
|
psref_acquire(refp, &file->sc->sc_psref, audio_psref_class); |
|
|
|
/* Now sc won't go away until we drop the reference count */ |
|
pserialize_read_exit(s); |
|
|
|
return file->sc; |
|
} |
|
|
|
/* |
|
* Decrement the psref count. |
|
*/ |
|
void |
|
audio_file_exit(struct audio_softc *sc, struct psref *refp) |
|
{ |
|
|
|
psref_release(refp, &sc->sc_psref, audio_psref_class); |
|
} |
|
|
|
/* |
* Wait for I/O to complete, releasing sc_lock. |
* Wait for I/O to complete, releasing sc_lock. |
* Must be called with sc_lock held. |
* Must be called with sc_lock held. |
*/ |
*/ |
Line 1474 audioopen(dev_t dev, int flags, int ifmt |
|
Line 1596 audioopen(dev_t dev, int flags, int ifmt |
|
if (sc == NULL || sc->hw_if == NULL) |
if (sc == NULL || sc->hw_if == NULL) |
return ENXIO; |
return ENXIO; |
|
|
error = audio_enter_exclusive(sc); |
error = audio_exlock_enter(sc); |
if (error) |
if (error) |
return error; |
return error; |
|
|
Line 1494 audioopen(dev_t dev, int flags, int ifmt |
|
Line 1616 audioopen(dev_t dev, int flags, int ifmt |
|
error = ENXIO; |
error = ENXIO; |
break; |
break; |
} |
} |
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
|
|
return error; |
return error; |
} |
} |
|
|
audioclose(struct file *fp) |
audioclose(struct file *fp) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
int error; |
int error; |
dev_t dev; |
dev_t dev; |
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
sc = file->sc; |
|
dev = file->dev; |
dev = file->dev; |
|
error = 0; |
|
|
/* audio_{enter,exit}_exclusive() is called by lower audio_close() */ |
/* |
|
* audioclose() must |
|
* - unplug track from the trackmixer (and unplug anything from softc), |
|
* if sc exists. |
|
* - free all memory objects, regardless of sc. |
|
*/ |
|
|
device_active(sc->sc_dev, DVA_SYSTEM); |
sc = audio_file_enter(file, &sc_ref); |
switch (AUDIODEV(dev)) { |
if (sc) { |
case SOUND_DEVICE: |
switch (AUDIODEV(dev)) { |
case AUDIO_DEVICE: |
case SOUND_DEVICE: |
error = audio_close(sc, file); |
case AUDIO_DEVICE: |
break; |
error = audio_close(sc, file); |
case AUDIOCTL_DEVICE: |
break; |
error = 0; |
case AUDIOCTL_DEVICE: |
break; |
error = 0; |
case MIXER_DEVICE: |
break; |
error = mixer_close(sc, file); |
case MIXER_DEVICE: |
break; |
error = mixer_close(sc, file); |
default: |
break; |
error = ENXIO; |
default: |
break; |
error = ENXIO; |
} |
break; |
if (error == 0) { |
} |
kmem_free(fp->f_audioctx, sizeof(audio_file_t)); |
|
fp->f_audioctx = NULL; |
audio_file_exit(sc, &sc_ref); |
} |
} |
|
|
|
/* Free memory objects anyway */ |
|
TRACEF(2, file, "free memory"); |
|
if (file->ptrack) |
|
audio_track_destroy(file->ptrack); |
|
if (file->rtrack) |
|
audio_track_destroy(file->rtrack); |
|
kmem_free(file, sizeof(*file)); |
|
fp->f_audioctx = NULL; |
|
|
return error; |
return error; |
} |
} |
|
|
Line 1543 audioread(struct file *fp, off_t *offp, |
|
Line 1680 audioread(struct file *fp, off_t *offp, |
|
int ioflag) |
int ioflag) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
int error; |
int error; |
dev_t dev; |
dev_t dev; |
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
sc = file->sc; |
|
dev = file->dev; |
dev = file->dev; |
|
|
|
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
if (fp->f_flag & O_NONBLOCK) |
if (fp->f_flag & O_NONBLOCK) |
ioflag |= IO_NDELAY; |
ioflag |= IO_NDELAY; |
|
|
Line 1569 audioread(struct file *fp, off_t *offp, |
|
Line 1710 audioread(struct file *fp, off_t *offp, |
|
break; |
break; |
} |
} |
|
|
|
audio_file_exit(sc, &sc_ref); |
return error; |
return error; |
} |
} |
|
|
Line 1577 audiowrite(struct file *fp, off_t *offp, |
|
Line 1719 audiowrite(struct file *fp, off_t *offp, |
|
int ioflag) |
int ioflag) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
int error; |
int error; |
dev_t dev; |
dev_t dev; |
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
sc = file->sc; |
|
dev = file->dev; |
dev = file->dev; |
|
|
|
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
if (fp->f_flag & O_NONBLOCK) |
if (fp->f_flag & O_NONBLOCK) |
ioflag |= IO_NDELAY; |
ioflag |= IO_NDELAY; |
|
|
Line 1603 audiowrite(struct file *fp, off_t *offp, |
|
Line 1749 audiowrite(struct file *fp, off_t *offp, |
|
break; |
break; |
} |
} |
|
|
|
audio_file_exit(sc, &sc_ref); |
return error; |
return error; |
} |
} |
|
|
|
|
audioioctl(struct file *fp, u_long cmd, void *addr) |
audioioctl(struct file *fp, u_long cmd, void *addr) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
struct lwp *l = curlwp; |
struct lwp *l = curlwp; |
int error; |
int error; |
Line 1617 audioioctl(struct file *fp, u_long cmd, |
|
Line 1765 audioioctl(struct file *fp, u_long cmd, |
|
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
sc = file->sc; |
|
dev = file->dev; |
dev = file->dev; |
|
|
|
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
switch (AUDIODEV(dev)) { |
switch (AUDIODEV(dev)) { |
case SOUND_DEVICE: |
case SOUND_DEVICE: |
case AUDIO_DEVICE: |
case AUDIO_DEVICE: |
Line 1641 audioioctl(struct file *fp, u_long cmd, |
|
Line 1792 audioioctl(struct file *fp, u_long cmd, |
|
break; |
break; |
} |
} |
|
|
|
audio_file_exit(sc, &sc_ref); |
return error; |
return error; |
} |
} |
|
|
static int |
static int |
audiostat(struct file *fp, struct stat *st) |
audiostat(struct file *fp, struct stat *st) |
{ |
{ |
|
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
|
|
|
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
memset(st, 0, sizeof(*st)); |
memset(st, 0, sizeof(*st)); |
|
|
st->st_dev = file->dev; |
st->st_dev = file->dev; |
st->st_uid = kauth_cred_geteuid(fp->f_cred); |
st->st_uid = kauth_cred_geteuid(fp->f_cred); |
st->st_gid = kauth_cred_getegid(fp->f_cred); |
st->st_gid = kauth_cred_getegid(fp->f_cred); |
st->st_mode = S_IFCHR; |
st->st_mode = S_IFCHR; |
|
|
|
audio_file_exit(sc, &sc_ref); |
return 0; |
return 0; |
} |
} |
|
|
|
|
audiopoll(struct file *fp, int events) |
audiopoll(struct file *fp, int events) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
struct lwp *l = curlwp; |
struct lwp *l = curlwp; |
int revents; |
int revents; |
Line 1672 audiopoll(struct file *fp, int events) |
|
Line 1833 audiopoll(struct file *fp, int events) |
|
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
sc = file->sc; |
|
dev = file->dev; |
dev = file->dev; |
|
|
|
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
switch (AUDIODEV(dev)) { |
switch (AUDIODEV(dev)) { |
case SOUND_DEVICE: |
case SOUND_DEVICE: |
case AUDIO_DEVICE: |
case AUDIO_DEVICE: |
Line 1689 audiopoll(struct file *fp, int events) |
|
Line 1853 audiopoll(struct file *fp, int events) |
|
break; |
break; |
} |
} |
|
|
|
audio_file_exit(sc, &sc_ref); |
return revents; |
return revents; |
} |
} |
|
|
|
|
audiokqfilter(struct file *fp, struct knote *kn) |
audiokqfilter(struct file *fp, struct knote *kn) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
dev_t dev; |
dev_t dev; |
int error; |
int error; |
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
sc = file->sc; |
|
dev = file->dev; |
dev = file->dev; |
|
|
|
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
switch (AUDIODEV(dev)) { |
switch (AUDIODEV(dev)) { |
case SOUND_DEVICE: |
case SOUND_DEVICE: |
case AUDIO_DEVICE: |
case AUDIO_DEVICE: |
Line 1719 audiokqfilter(struct file *fp, struct kn |
|
Line 1888 audiokqfilter(struct file *fp, struct kn |
|
break; |
break; |
} |
} |
|
|
|
audio_file_exit(sc, &sc_ref); |
return error; |
return error; |
} |
} |
|
|
Line 1727 audiommap(struct file *fp, off_t *offp, |
|
Line 1897 audiommap(struct file *fp, off_t *offp, |
|
int *advicep, struct uvm_object **uobjp, int *maxprotp) |
int *advicep, struct uvm_object **uobjp, int *maxprotp) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
audio_file_t *file; |
audio_file_t *file; |
dev_t dev; |
dev_t dev; |
int error; |
int error; |
|
|
KASSERT(fp->f_audioctx); |
KASSERT(fp->f_audioctx); |
file = fp->f_audioctx; |
file = fp->f_audioctx; |
sc = file->sc; |
|
dev = file->dev; |
dev = file->dev; |
|
|
|
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_lock); |
device_active(sc->sc_dev, DVA_SYSTEM); /* XXXJDM */ |
device_active(sc->sc_dev, DVA_SYSTEM); /* XXXJDM */ |
mutex_exit(sc->sc_lock); |
mutex_exit(sc->sc_lock); |
Line 1753 audiommap(struct file *fp, off_t *offp, |
|
Line 1927 audiommap(struct file *fp, off_t *offp, |
|
break; |
break; |
} |
} |
|
|
|
audio_file_exit(sc, &sc_ref); |
return error; |
return error; |
} |
} |
|
|
Line 1775 audiobellopen(dev_t dev, audio_file_t ** |
|
Line 1950 audiobellopen(dev_t dev, audio_file_t ** |
|
if (sc == NULL || sc->hw_if == NULL) |
if (sc == NULL || sc->hw_if == NULL) |
return ENXIO; |
return ENXIO; |
|
|
error = audio_enter_exclusive(sc); |
error = audio_exlock_enter(sc); |
if (error) |
if (error) |
return error; |
return error; |
|
|
device_active(sc->sc_dev, DVA_SYSTEM); |
device_active(sc->sc_dev, DVA_SYSTEM); |
error = audio_open(dev, sc, FWRITE, 0, curlwp, filep); |
error = audio_open(dev, sc, FWRITE, 0, curlwp, filep); |
|
|
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
return error; |
return error; |
} |
} |
|
|
|
|
audiobellclose(audio_file_t *file) |
audiobellclose(audio_file_t *file) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
int error; |
int error; |
|
|
sc = file->sc; |
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
device_active(sc->sc_dev, DVA_SYSTEM); |
|
error = audio_close(sc, file); |
error = audio_close(sc, file); |
|
|
/* |
audio_file_exit(sc, &sc_ref); |
* Since file has already been destructed, |
|
* audio_file_release() is not necessary. |
|
*/ |
|
|
|
|
KASSERT(file->ptrack); |
|
audio_track_destroy(file->ptrack); |
|
KASSERT(file->rtrack == NULL); |
|
kmem_free(file, sizeof(*file)); |
return error; |
return error; |
} |
} |
|
|
|
|
audiobellsetrate(audio_file_t *file, u_int sample_rate) |
audiobellsetrate(audio_file_t *file, u_int sample_rate) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
struct audio_info ai; |
struct audio_info ai; |
int error; |
int error; |
|
|
sc = file->sc; |
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
AUDIO_INITINFO(&ai); |
AUDIO_INITINFO(&ai); |
ai.play.sample_rate = sample_rate; |
ai.play.sample_rate = sample_rate; |
|
|
error = audio_enter_exclusive(sc); |
error = audio_exlock_enter(sc); |
if (error) |
if (error) |
return error; |
goto done; |
error = audio_file_setinfo(sc, file, &ai); |
error = audio_file_setinfo(sc, file, &ai); |
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
|
|
|
done: |
|
audio_file_exit(sc, &sc_ref); |
return error; |
return error; |
} |
} |
|
|
|
|
audiobellwrite(audio_file_t *file, struct uio *uio) |
audiobellwrite(audio_file_t *file, struct uio *uio) |
{ |
{ |
struct audio_softc *sc; |
struct audio_softc *sc; |
|
struct psref sc_ref; |
int error; |
int error; |
|
|
sc = file->sc; |
sc = audio_file_enter(file, &sc_ref); |
|
if (sc == NULL) |
|
return EIO; |
|
|
error = audio_write(sc, uio, 0, file); |
error = audio_write(sc, uio, 0, file); |
|
|
|
audio_file_exit(sc, &sc_ref); |
return error; |
return error; |
} |
} |
|
|
Line 1844 audiobellwrite(audio_file_t *file, struc |
|
Line 2033 audiobellwrite(audio_file_t *file, struc |
|
/* |
/* |
* Audio driver |
* Audio driver |
*/ |
*/ |
|
|
|
/* |
|
* Must be called with sc_exlock held and without sc_lock held. |
|
*/ |
int |
int |
audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, |
audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, |
struct lwp *l, audio_file_t **bellfile) |
struct lwp *l, audio_file_t **bellfile) |
Line 1856 audio_open(dev_t dev, struct audio_softc |
|
Line 2049 audio_open(dev_t dev, struct audio_softc |
|
int fd; |
int fd; |
int error; |
int error; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
KASSERT(sc->sc_exlock); |
KASSERT(sc->sc_exlock); |
|
|
TRACE(1, "%sdev=%s flags=0x%x po=%d ro=%d", |
TRACE(1, "%sdev=%s flags=0x%x po=%d ro=%d", |
Line 1978 audio_open(dev_t dev, struct audio_softc |
|
Line 2170 audio_open(dev_t dev, struct audio_softc |
|
hwflags |= FREAD; |
hwflags |= FREAD; |
} |
} |
|
|
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_intr_lock); |
mutex_enter(sc->sc_intr_lock); |
error = sc->hw_if->open(sc->hw_hdl, hwflags); |
error = sc->hw_if->open(sc->hw_hdl, hwflags); |
mutex_exit(sc->sc_intr_lock); |
mutex_exit(sc->sc_intr_lock); |
|
mutex_exit(sc->sc_lock); |
if (error) |
if (error) |
goto bad2; |
goto bad2; |
} |
} |
Line 1997 audio_open(dev_t dev, struct audio_softc |
|
Line 2191 audio_open(dev_t dev, struct audio_softc |
|
} else { |
} else { |
on = 0; |
on = 0; |
} |
} |
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_intr_lock); |
mutex_enter(sc->sc_intr_lock); |
error = sc->hw_if->speaker_ctl(sc->hw_hdl, on); |
error = sc->hw_if->speaker_ctl(sc->hw_hdl, on); |
mutex_exit(sc->sc_intr_lock); |
mutex_exit(sc->sc_intr_lock); |
|
mutex_exit(sc->sc_lock); |
if (error) |
if (error) |
goto bad3; |
goto bad3; |
} |
} |
Line 2016 audio_open(dev_t dev, struct audio_softc |
|
Line 2212 audio_open(dev_t dev, struct audio_softc |
|
if (af->ptrack && sc->sc_popens == 0) { |
if (af->ptrack && sc->sc_popens == 0) { |
if (sc->hw_if->init_output) { |
if (sc->hw_if->init_output) { |
hwbuf = &sc->sc_pmixer->hwbuf; |
hwbuf = &sc->sc_pmixer->hwbuf; |
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_intr_lock); |
mutex_enter(sc->sc_intr_lock); |
error = sc->hw_if->init_output(sc->hw_hdl, |
error = sc->hw_if->init_output(sc->hw_hdl, |
hwbuf->mem, |
hwbuf->mem, |
hwbuf->capacity * |
hwbuf->capacity * |
hwbuf->fmt.channels * hwbuf->fmt.stride / NBBY); |
hwbuf->fmt.channels * hwbuf->fmt.stride / NBBY); |
mutex_exit(sc->sc_intr_lock); |
mutex_exit(sc->sc_intr_lock); |
|
mutex_exit(sc->sc_lock); |
if (error) |
if (error) |
goto bad3; |
goto bad3; |
} |
} |
} |
} |
/* Call init_input if this is the first recording open. */ |
/* |
|
* Call init_input and start rmixer, if this is the first recording |
|
* open. See pause consideration notes. |
|
*/ |
if (af->rtrack && sc->sc_ropens == 0) { |
if (af->rtrack && sc->sc_ropens == 0) { |
if (sc->hw_if->init_input) { |
if (sc->hw_if->init_input) { |
hwbuf = &sc->sc_rmixer->hwbuf; |
hwbuf = &sc->sc_rmixer->hwbuf; |
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_intr_lock); |
mutex_enter(sc->sc_intr_lock); |
error = sc->hw_if->init_input(sc->hw_hdl, |
error = sc->hw_if->init_input(sc->hw_hdl, |
hwbuf->mem, |
hwbuf->mem, |
hwbuf->capacity * |
hwbuf->capacity * |
hwbuf->fmt.channels * hwbuf->fmt.stride / NBBY); |
hwbuf->fmt.channels * hwbuf->fmt.stride / NBBY); |
mutex_exit(sc->sc_intr_lock); |
mutex_exit(sc->sc_intr_lock); |
|
mutex_exit(sc->sc_lock); |
if (error) |
if (error) |
goto bad3; |
goto bad3; |
} |
} |
|
|
|
mutex_enter(sc->sc_lock); |
|
audio_rmixer_start(sc); |
|
mutex_exit(sc->sc_lock); |
} |
} |
|
|
if (bellfile == NULL) { |
if (bellfile == NULL) { |
Line 2051 audio_open(dev_t dev, struct audio_softc |
|
Line 2258 audio_open(dev_t dev, struct audio_softc |
|
* Count up finally. |
* Count up finally. |
* Don't fail from here. |
* Don't fail from here. |
*/ |
*/ |
|
mutex_enter(sc->sc_lock); |
if (af->ptrack) |
if (af->ptrack) |
sc->sc_popens++; |
sc->sc_popens++; |
if (af->rtrack) |
if (af->rtrack) |
Line 2058 audio_open(dev_t dev, struct audio_softc |
|
Line 2266 audio_open(dev_t dev, struct audio_softc |
|
mutex_enter(sc->sc_intr_lock); |
mutex_enter(sc->sc_intr_lock); |
SLIST_INSERT_HEAD(&sc->sc_files, af, entry); |
SLIST_INSERT_HEAD(&sc->sc_files, af, entry); |
mutex_exit(sc->sc_intr_lock); |
mutex_exit(sc->sc_intr_lock); |
|
mutex_exit(sc->sc_lock); |
|
|
if (bellfile) { |
if (bellfile) { |
*bellfile = af; |
*bellfile = af; |
} else { |
} else { |
error = fd_clone(fp, fd, flags, &audio_fileops, af); |
error = fd_clone(fp, fd, flags, &audio_fileops, af); |
KASSERT(error == EMOVEFD); |
KASSERTMSG(error == EMOVEFD, "error=%d", error); |
} |
} |
|
|
TRACEF(3, af, "done"); |
TRACEF(3, af, "done"); |
Line 2076 audio_open(dev_t dev, struct audio_softc |
|
Line 2285 audio_open(dev_t dev, struct audio_softc |
|
bad3: |
bad3: |
if (sc->sc_popens + sc->sc_ropens == 0) { |
if (sc->sc_popens + sc->sc_ropens == 0) { |
if (sc->hw_if->close) { |
if (sc->hw_if->close) { |
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_intr_lock); |
mutex_enter(sc->sc_intr_lock); |
sc->hw_if->close(sc->hw_hdl); |
sc->hw_if->close(sc->hw_hdl); |
mutex_exit(sc->sc_intr_lock); |
mutex_exit(sc->sc_intr_lock); |
|
mutex_exit(sc->sc_lock); |
} |
} |
} |
} |
bad2: |
bad2: |
|
|
} |
} |
|
|
/* |
/* |
* Must NOT called with sc_lock nor sc_exlock held. |
* Must be called without sc_lock nor sc_exlock held. |
*/ |
*/ |
int |
int |
audio_close(struct audio_softc *sc, audio_file_t *file) |
audio_close(struct audio_softc *sc, audio_file_t *file) |
{ |
{ |
audio_track_t *oldtrack; |
|
|
/* Protect entering new fileops to this file */ |
|
atomic_store_relaxed(&file->dying, true); |
|
|
|
/* |
|
* Drain first. |
|
* It must be done before unlinking(acquiring exlock). |
|
*/ |
|
if (file->ptrack) { |
|
mutex_enter(sc->sc_lock); |
|
audio_track_drain(sc, file->ptrack); |
|
mutex_exit(sc->sc_lock); |
|
} |
|
|
|
return audio_unlink(sc, file); |
|
} |
|
|
|
/* |
|
* Unlink this file, but not freeing memory here. |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
|
int |
|
audio_unlink(struct audio_softc *sc, audio_file_t *file) |
|
{ |
int error; |
int error; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
mutex_enter(sc->sc_lock); |
|
|
TRACEF(1, file, "%spid=%d.%d po=%d ro=%d", |
TRACEF(1, file, "%spid=%d.%d po=%d ro=%d", |
(audiodebug >= 3) ? "start " : "", |
(audiodebug >= 3) ? "start " : "", |
Line 2115 audio_close(struct audio_softc *sc, audi |
|
Line 2349 audio_close(struct audio_softc *sc, audi |
|
sc->sc_popens, sc->sc_ropens); |
sc->sc_popens, sc->sc_ropens); |
|
|
/* |
/* |
* Drain first. |
* Acquire exlock to protect counters. |
* It must be done before acquiring exclusive lock. |
* Does not use audio_exlock_enter() due to sc_dying. |
*/ |
*/ |
if (file->ptrack) { |
while (__predict_false(sc->sc_exlock != 0)) { |
mutex_enter(sc->sc_lock); |
error = cv_timedwait_sig(&sc->sc_exlockcv, sc->sc_lock, |
audio_track_drain(sc, file->ptrack); |
mstohz(AUDIO_TIMEOUT)); |
mutex_exit(sc->sc_lock); |
/* XXX what should I do on error? */ |
|
if (error == EWOULDBLOCK) { |
|
mutex_exit(sc->sc_lock); |
|
device_printf(sc->sc_dev, |
|
"%s: cv_timedwait_sig failed %d", __func__, error); |
|
return error; |
|
} |
} |
} |
|
sc->sc_exlock = 1; |
|
|
/* Then, acquire exclusive lock to protect counters. */ |
device_active(sc->sc_dev, DVA_SYSTEM); |
/* XXX what should I do when an error occurs? */ |
|
error = audio_enter_exclusive(sc); |
mutex_enter(sc->sc_intr_lock); |
if (error) |
SLIST_REMOVE(&sc->sc_files, file, audio_file, entry); |
return error; |
mutex_exit(sc->sc_intr_lock); |
|
|
if (file->ptrack) { |
if (file->ptrack) { |
|
TRACET(3, file->ptrack, "dropframes=%" PRIu64, |
|
file->ptrack->dropframes); |
|
|
|
KASSERT(sc->sc_popens > 0); |
|
sc->sc_popens--; |
|
|
/* Call hw halt_output if this is the last playback track. */ |
/* Call hw halt_output if this is the last playback track. */ |
if (sc->sc_popens == 1 && sc->sc_pbusy) { |
if (sc->sc_popens == 0 && sc->sc_pbusy) { |
error = audio_pmixer_halt(sc); |
error = audio_pmixer_halt(sc); |
if (error) { |
if (error) { |
device_printf(sc->sc_dev, |
device_printf(sc->sc_dev, |
"halt_output failed with %d\n", error); |
"halt_output failed with %d (ignored)\n", |
|
error); |
} |
} |
} |
} |
|
|
/* Destroy the track. */ |
|
oldtrack = file->ptrack; |
|
mutex_enter(sc->sc_intr_lock); |
|
file->ptrack = NULL; |
|
mutex_exit(sc->sc_intr_lock); |
|
TRACET(3, oldtrack, "dropframes=%" PRIu64, |
|
oldtrack->dropframes); |
|
audio_track_destroy(oldtrack); |
|
|
|
KASSERT(sc->sc_popens > 0); |
|
sc->sc_popens--; |
|
|
|
/* Restore mixing volume if all tracks are gone. */ |
/* Restore mixing volume if all tracks are gone. */ |
if (sc->sc_popens == 0) { |
if (sc->sc_popens == 0) { |
|
/* intr_lock is not necessary, but just manners. */ |
mutex_enter(sc->sc_intr_lock); |
mutex_enter(sc->sc_intr_lock); |
sc->sc_pmixer->volume = 256; |
sc->sc_pmixer->volume = 256; |
sc->sc_pmixer->voltimer = 0; |
sc->sc_pmixer->voltimer = 0; |
Line 2161 audio_close(struct audio_softc *sc, audi |
|
Line 2398 audio_close(struct audio_softc *sc, audi |
|
} |
} |
} |
} |
if (file->rtrack) { |
if (file->rtrack) { |
|
TRACET(3, file->rtrack, "dropframes=%" PRIu64, |
|
file->rtrack->dropframes); |
|
|
|
KASSERT(sc->sc_ropens > 0); |
|
sc->sc_ropens--; |
|
|
/* Call hw halt_input if this is the last recording track. */ |
/* Call hw halt_input if this is the last recording track. */ |
if (sc->sc_ropens == 1 && sc->sc_rbusy) { |
if (sc->sc_ropens == 0 && sc->sc_rbusy) { |
error = audio_rmixer_halt(sc); |
error = audio_rmixer_halt(sc); |
if (error) { |
if (error) { |
device_printf(sc->sc_dev, |
device_printf(sc->sc_dev, |
"halt_input failed with %d\n", error); |
"halt_input failed with %d (ignored)\n", |
|
error); |
} |
} |
} |
} |
|
|
/* Destroy the track. */ |
|
oldtrack = file->rtrack; |
|
mutex_enter(sc->sc_intr_lock); |
|
file->rtrack = NULL; |
|
mutex_exit(sc->sc_intr_lock); |
|
TRACET(3, oldtrack, "dropframes=%" PRIu64, |
|
oldtrack->dropframes); |
|
audio_track_destroy(oldtrack); |
|
|
|
KASSERT(sc->sc_ropens > 0); |
|
sc->sc_ropens--; |
|
} |
} |
|
|
/* Call hw close if this is the last track. */ |
/* Call hw close if this is the last track. */ |
Line 2191 audio_close(struct audio_softc *sc, audi |
|
Line 2424 audio_close(struct audio_softc *sc, audi |
|
sc->hw_if->close(sc->hw_hdl); |
sc->hw_if->close(sc->hw_hdl); |
mutex_exit(sc->sc_intr_lock); |
mutex_exit(sc->sc_intr_lock); |
} |
} |
|
|
kauth_cred_free(sc->sc_cred); |
|
} |
} |
|
|
mutex_enter(sc->sc_intr_lock); |
mutex_exit(sc->sc_lock); |
SLIST_REMOVE(&sc->sc_files, file, audio_file, entry); |
if (sc->sc_popens + sc->sc_ropens == 0) |
mutex_exit(sc->sc_intr_lock); |
kauth_cred_free(sc->sc_cred); |
|
|
TRACE(3, "done"); |
TRACE(3, "done"); |
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
|
|
return 0; |
return 0; |
} |
} |
|
|
|
/* |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
int |
int |
audio_read(struct audio_softc *sc, struct uio *uio, int ioflag, |
audio_read(struct audio_softc *sc, struct uio *uio, int ioflag, |
audio_file_t *file) |
audio_file_t *file) |
Line 2213 audio_read(struct audio_softc *sc, struc |
|
Line 2448 audio_read(struct audio_softc *sc, struc |
|
audio_ring_t *input; |
audio_ring_t *input; |
int error; |
int error; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
/* |
/* |
* On half-duplex hardware, O_RDWR is treated as O_WRONLY. |
* On half-duplex hardware, O_RDWR is treated as O_WRONLY. |
* However read() system call itself can be called because it's |
* However read() system call itself can be called because it's |
Line 2232 audio_read(struct audio_softc *sc, struc |
|
Line 2465 audio_read(struct audio_softc *sc, struc |
|
TRACET(2, track, "resid=%zd", uio->uio_resid); |
TRACET(2, track, "resid=%zd", uio->uio_resid); |
|
|
#ifdef AUDIO_PM_IDLE |
#ifdef AUDIO_PM_IDLE |
mutex_enter(sc->sc_lock); |
error = audio_exlock_mutex_enter(sc); |
|
if (error) |
|
return error; |
|
|
if (device_is_active(&sc->sc_dev) || sc->sc_idle) |
if (device_is_active(&sc->sc_dev) || sc->sc_idle) |
device_active(&sc->sc_dev, DVA_SYSTEM); |
device_active(&sc->sc_dev, DVA_SYSTEM); |
mutex_exit(sc->sc_lock); |
|
|
/* In recording, unlike playback, read() never operates rmixer. */ |
|
|
|
audio_exlock_mutex_exit(sc); |
#endif |
#endif |
|
|
usrbuf = &track->usrbuf; |
usrbuf = &track->usrbuf; |
input = track->input; |
input = track->input; |
|
|
/* |
|
* The first read starts rmixer. |
|
*/ |
|
error = audio_enter_exclusive(sc); |
|
if (error) |
|
return error; |
|
if (sc->sc_rbusy == false) |
|
audio_rmixer_start(sc); |
|
audio_exit_exclusive(sc); |
|
|
|
error = 0; |
error = 0; |
|
|
while (uio->uio_resid > 0 && error == 0) { |
while (uio->uio_resid > 0 && error == 0) { |
int bytes; |
int bytes; |
|
|
Line 2331 audio_file_clear(struct audio_softc *sc, |
|
Line 2560 audio_file_clear(struct audio_softc *sc, |
|
audio_track_clear(sc, file->rtrack); |
audio_track_clear(sc, file->rtrack); |
} |
} |
|
|
|
/* |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
int |
int |
audio_write(struct audio_softc *sc, struct uio *uio, int ioflag, |
audio_write(struct audio_softc *sc, struct uio *uio, int ioflag, |
audio_file_t *file) |
audio_file_t *file) |
Line 2340 audio_write(struct audio_softc *sc, stru |
|
Line 2572 audio_write(struct audio_softc *sc, stru |
|
audio_ring_t *outbuf; |
audio_ring_t *outbuf; |
int error; |
int error; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
track = file->ptrack; |
track = file->ptrack; |
KASSERT(track); |
KASSERT(track); |
|
|
Line 2358 audio_write(struct audio_softc *sc, stru |
|
Line 2588 audio_write(struct audio_softc *sc, stru |
|
return 0; |
return 0; |
} |
} |
|
|
|
error = audio_exlock_mutex_enter(sc); |
|
if (error) |
|
return error; |
|
|
#ifdef AUDIO_PM_IDLE |
#ifdef AUDIO_PM_IDLE |
mutex_enter(sc->sc_lock); |
|
if (device_is_active(&sc->sc_dev) || sc->sc_idle) |
if (device_is_active(&sc->sc_dev) || sc->sc_idle) |
device_active(&sc->sc_dev, DVA_SYSTEM); |
device_active(&sc->sc_dev, DVA_SYSTEM); |
mutex_exit(sc->sc_lock); |
|
#endif |
#endif |
|
|
usrbuf = &track->usrbuf; |
|
outbuf = &track->outbuf; |
|
|
|
/* |
/* |
* The first write starts pmixer. |
* The first write starts pmixer. |
*/ |
*/ |
error = audio_enter_exclusive(sc); |
|
if (error) |
|
return error; |
|
if (sc->sc_pbusy == false) |
if (sc->sc_pbusy == false) |
audio_pmixer_start(sc, false); |
audio_pmixer_start(sc, false); |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
|
|
|
usrbuf = &track->usrbuf; |
|
outbuf = &track->outbuf; |
track->pstate = AUDIO_STATE_RUNNING; |
track->pstate = AUDIO_STATE_RUNNING; |
error = 0; |
error = 0; |
|
|
while (uio->uio_resid > 0 && error == 0) { |
while (uio->uio_resid > 0 && error == 0) { |
int bytes; |
int bytes; |
|
|
|
|
return error; |
return error; |
} |
} |
|
|
|
/* |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
int |
int |
audio_ioctl(dev_t dev, struct audio_softc *sc, u_long cmd, void *addr, int flag, |
audio_ioctl(dev_t dev, struct audio_softc *sc, u_long cmd, void *addr, int flag, |
struct lwp *l, audio_file_t *file) |
struct lwp *l, audio_file_t *file) |
Line 2468 audio_ioctl(dev_t dev, struct audio_soft |
|
Line 2700 audio_ioctl(dev_t dev, struct audio_soft |
|
int index; |
int index; |
int error; |
int error; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
#if defined(AUDIO_DEBUG) |
#if defined(AUDIO_DEBUG) |
const char *ioctlnames[] = { |
const char *ioctlnames[] = { |
" AUDIO_GETINFO", /* 21 */ |
" AUDIO_GETINFO", /* 21 */ |
Line 2607 audio_ioctl(dev_t dev, struct audio_soft |
|
Line 2837 audio_ioctl(dev_t dev, struct audio_soft |
|
break; |
break; |
|
|
case AUDIO_SETINFO: |
case AUDIO_SETINFO: |
error = audio_enter_exclusive(sc); |
error = audio_exlock_enter(sc); |
if (error) |
if (error) |
break; |
break; |
error = audio_file_setinfo(sc, file, (struct audio_info *)addr); |
error = audio_file_setinfo(sc, file, (struct audio_info *)addr); |
if (error) { |
if (error) { |
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
break; |
break; |
} |
} |
/* XXX TODO: update last_ai if /dev/sound ? */ |
/* XXX TODO: update last_ai if /dev/sound ? */ |
if (ISDEVSOUND(dev)) |
if (ISDEVSOUND(dev)) |
error = audiogetinfo(sc, &sc->sc_ai, 0, file); |
error = audiogetinfo(sc, &sc->sc_ai, 0, file); |
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
break; |
break; |
|
|
case AUDIO_GETINFO: |
case AUDIO_GETINFO: |
error = audio_enter_exclusive(sc); |
error = audio_exlock_enter(sc); |
if (error) |
if (error) |
break; |
break; |
error = audiogetinfo(sc, (struct audio_info *)addr, 1, file); |
error = audiogetinfo(sc, (struct audio_info *)addr, 1, file); |
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
break; |
break; |
|
|
case AUDIO_GETBUFINFO: |
case AUDIO_GETBUFINFO: |
mutex_enter(sc->sc_lock); |
error = audio_exlock_enter(sc); |
|
if (error) |
|
break; |
error = audiogetinfo(sc, (struct audio_info *)addr, 0, file); |
error = audiogetinfo(sc, (struct audio_info *)addr, 0, file); |
mutex_exit(sc->sc_lock); |
audio_exlock_exit(sc); |
break; |
break; |
|
|
case AUDIO_DRAIN: |
case AUDIO_DRAIN: |
Line 2675 audio_ioctl(dev_t dev, struct audio_soft |
|
Line 2907 audio_ioctl(dev_t dev, struct audio_soft |
|
* If HW has full duplex mode and there are two mixers, |
* If HW has full duplex mode and there are two mixers, |
* it is full duplex. Otherwise half duplex. |
* it is full duplex. Otherwise half duplex. |
*/ |
*/ |
mutex_enter(sc->sc_lock); |
error = audio_exlock_enter(sc); |
|
if (error) |
|
break; |
fd = (sc->sc_props & AUDIO_PROP_FULLDUPLEX) |
fd = (sc->sc_props & AUDIO_PROP_FULLDUPLEX) |
&& (sc->sc_pmixer && sc->sc_rmixer); |
&& (sc->sc_pmixer && sc->sc_rmixer); |
mutex_exit(sc->sc_lock); |
audio_exlock_exit(sc); |
*(int *)addr = fd; |
*(int *)addr = fd; |
break; |
break; |
|
|
Line 2688 audio_ioctl(dev_t dev, struct audio_soft |
|
Line 2922 audio_ioctl(dev_t dev, struct audio_soft |
|
|
|
case AUDIO_QUERYFORMAT: |
case AUDIO_QUERYFORMAT: |
query = (audio_format_query_t *)addr; |
query = (audio_format_query_t *)addr; |
if (sc->hw_if->query_format) { |
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_lock); |
error = sc->hw_if->query_format(sc->hw_hdl, query); |
error = sc->hw_if->query_format(sc->hw_hdl, query); |
mutex_exit(sc->sc_lock); |
mutex_exit(sc->sc_lock); |
/* Hide internal infomations */ |
/* Hide internal infomations */ |
query->fmt.driver_data = NULL; |
query->fmt.driver_data = NULL; |
|
} else { |
|
error = ENODEV; |
|
} |
|
break; |
break; |
|
|
case AUDIO_GETFORMAT: |
case AUDIO_GETFORMAT: |
|
error = audio_exlock_enter(sc); |
|
if (error) |
|
break; |
audio_mixers_get_format(sc, (struct audio_info *)addr); |
audio_mixers_get_format(sc, (struct audio_info *)addr); |
|
audio_exlock_exit(sc); |
break; |
break; |
|
|
case AUDIO_SETFORMAT: |
case AUDIO_SETFORMAT: |
mutex_enter(sc->sc_lock); |
error = audio_exlock_enter(sc); |
audio_mixers_get_format(sc, &ai); |
audio_mixers_get_format(sc, &ai); |
error = audio_mixers_set_format(sc, (struct audio_info *)addr); |
error = audio_mixers_set_format(sc, (struct audio_info *)addr); |
if (error) { |
if (error) { |
/* Rollback */ |
/* Rollback */ |
audio_mixers_set_format(sc, &ai); |
audio_mixers_set_format(sc, &ai); |
} |
} |
mutex_exit(sc->sc_lock); |
audio_exlock_exit(sc); |
break; |
break; |
|
|
case AUDIO_SETFD: |
case AUDIO_SETFD: |
Line 2722 audio_ioctl(dev_t dev, struct audio_soft |
|
Line 2956 audio_ioctl(dev_t dev, struct audio_soft |
|
|
|
default: |
default: |
if (sc->hw_if->dev_ioctl) { |
if (sc->hw_if->dev_ioctl) { |
error = audio_enter_exclusive(sc); |
mutex_enter(sc->sc_lock); |
if (error) |
|
break; |
|
error = sc->hw_if->dev_ioctl(sc->hw_hdl, |
error = sc->hw_if->dev_ioctl(sc->hw_hdl, |
cmd, addr, flag, l); |
cmd, addr, flag, l); |
audio_exit_exclusive(sc); |
mutex_exit(sc->sc_lock); |
} else { |
} else { |
TRACEF(2, file, "unknown ioctl"); |
TRACEF(2, file, "unknown ioctl"); |
error = EINVAL; |
error = EINVAL; |
Line 2761 audio_track_readablebytes(const audio_tr |
|
Line 2993 audio_track_readablebytes(const audio_tr |
|
return bytes; |
return bytes; |
} |
} |
|
|
|
/* |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
int |
int |
audio_poll(struct audio_softc *sc, int events, struct lwp *l, |
audio_poll(struct audio_softc *sc, int events, struct lwp *l, |
audio_file_t *file) |
audio_file_t *file) |
Line 2770 audio_poll(struct audio_softc *sc, int e |
|
Line 3005 audio_poll(struct audio_softc *sc, int e |
|
bool in_is_valid; |
bool in_is_valid; |
bool out_is_valid; |
bool out_is_valid; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
#if defined(AUDIO_DEBUG) |
#if defined(AUDIO_DEBUG) |
#define POLLEV_BITMAP "\177\020" \ |
#define POLLEV_BITMAP "\177\020" \ |
"b\10WRBAND\0" \ |
"b\10WRBAND\0" \ |
Line 2919 filt_audiowrite_event(struct knote *kn, |
|
Line 3152 filt_audiowrite_event(struct knote *kn, |
|
return (track->usrbuf.used < track->usrbuf_usedlow); |
return (track->usrbuf.used < track->usrbuf_usedlow); |
} |
} |
|
|
|
/* |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
int |
int |
audio_kqfilter(struct audio_softc *sc, audio_file_t *file, struct knote *kn) |
audio_kqfilter(struct audio_softc *sc, audio_file_t *file, struct knote *kn) |
{ |
{ |
struct klist *klist; |
struct klist *klist; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
TRACEF(3, file, "kn=%p kn_filter=%x", kn, (int)kn->kn_filter); |
TRACEF(3, file, "kn=%p kn_filter=%x", kn, (int)kn->kn_filter); |
|
|
|
mutex_enter(sc->sc_lock); |
switch (kn->kn_filter) { |
switch (kn->kn_filter) { |
case EVFILT_READ: |
case EVFILT_READ: |
klist = &sc->sc_rsel.sel_klist; |
klist = &sc->sc_rsel.sel_klist; |
Line 2940 audio_kqfilter(struct audio_softc *sc, a |
|
Line 3175 audio_kqfilter(struct audio_softc *sc, a |
|
break; |
break; |
|
|
default: |
default: |
|
mutex_exit(sc->sc_lock); |
return EINVAL; |
return EINVAL; |
} |
} |
|
|
kn->kn_hook = file; |
kn->kn_hook = file; |
|
|
mutex_enter(sc->sc_lock); |
|
SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
mutex_exit(sc->sc_lock); |
mutex_exit(sc->sc_lock); |
|
|
return 0; |
return 0; |
} |
} |
|
|
|
/* |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
int |
int |
audio_mmap(struct audio_softc *sc, off_t *offp, size_t len, int prot, |
audio_mmap(struct audio_softc *sc, off_t *offp, size_t len, int prot, |
int *flagsp, int *advicep, struct uvm_object **uobjp, int *maxprotp, |
int *flagsp, int *advicep, struct uvm_object **uobjp, int *maxprotp, |
Line 2961 audio_mmap(struct audio_softc *sc, off_t |
|
Line 3199 audio_mmap(struct audio_softc *sc, off_t |
|
vsize_t vsize; |
vsize_t vsize; |
int error; |
int error; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
TRACEF(2, file, "off=%lld, prot=%d", (long long)(*offp), prot); |
TRACEF(2, file, "off=%lld, prot=%d", (long long)(*offp), prot); |
|
|
if (*offp < 0) |
if (*offp < 0) |
Line 3005 audio_mmap(struct audio_softc *sc, off_t |
|
Line 3241 audio_mmap(struct audio_softc *sc, off_t |
|
track->mmapped = true; |
track->mmapped = true; |
|
|
if (!track->is_pause) { |
if (!track->is_pause) { |
error = audio_enter_exclusive(sc); |
error = audio_exlock_mutex_enter(sc); |
if (error) |
if (error) |
return error; |
return error; |
if (sc->sc_pbusy == false) |
if (sc->sc_pbusy == false) |
audio_pmixer_start(sc, true); |
audio_pmixer_start(sc, true); |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
} |
} |
/* XXX mmapping record buffer is not supported */ |
/* XXX mmapping record buffer is not supported */ |
} |
} |
Line 3029 audio_mmap(struct audio_softc *sc, off_t |
|
Line 3265 audio_mmap(struct audio_softc *sc, off_t |
|
/* |
/* |
* /dev/audioctl has to be able to open at any time without interference |
* /dev/audioctl has to be able to open at any time without interference |
* with any /dev/audio or /dev/sound. |
* with any /dev/audio or /dev/sound. |
|
* Must be called with sc_exlock held and without sc_lock held. |
*/ |
*/ |
static int |
static int |
audioctl_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, |
audioctl_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, |
Line 3039 audioctl_open(dev_t dev, struct audio_so |
|
Line 3276 audioctl_open(dev_t dev, struct audio_so |
|
int fd; |
int fd; |
int error; |
int error; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
KASSERT(sc->sc_exlock); |
KASSERT(sc->sc_exlock); |
|
|
TRACE(1, ""); |
TRACE(1, ""); |
Line 3055 audioctl_open(dev_t dev, struct audio_so |
|
Line 3291 audioctl_open(dev_t dev, struct audio_so |
|
/* Not necessary to insert sc_files. */ |
/* Not necessary to insert sc_files. */ |
|
|
error = fd_clone(fp, fd, flags, &audio_fileops, af); |
error = fd_clone(fp, fd, flags, &audio_fileops, af); |
KASSERT(error == EMOVEFD); |
KASSERTMSG(error == EMOVEFD, "error=%d", error); |
|
|
return error; |
return error; |
} |
} |
|
|
/* |
/* |
* Reallocate 'memblock' with specified 'bytes' if 'bytes' > 0. |
|
* Or free 'memblock' and return NULL if 'byte' is zero. |
|
*/ |
|
static void * |
|
audio_realloc(void *memblock, size_t bytes) |
|
{ |
|
|
|
if (memblock != NULL) { |
|
if (bytes != 0) { |
|
return kern_realloc(memblock, bytes, M_NOWAIT); |
|
} else { |
|
kern_free(memblock); |
|
return NULL; |
|
} |
|
} else { |
|
if (bytes != 0) { |
|
return kern_malloc(bytes, M_NOWAIT); |
|
} else { |
|
return NULL; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* Free 'mem' if available, and initialize the pointer. |
* Free 'mem' if available, and initialize the pointer. |
* For this reason, this is implemented as macro. |
* For this reason, this is implemented as macro. |
*/ |
*/ |
Line 3096 audio_realloc(void *memblock, size_t byt |
|
Line 3308 audio_realloc(void *memblock, size_t byt |
|
} while (0) |
} while (0) |
|
|
/* |
/* |
|
* (Re)allocate 'memblock' with specified 'bytes'. |
|
* bytes must not be 0. |
|
* This function never returns NULL. |
|
*/ |
|
static void * |
|
audio_realloc(void *memblock, size_t bytes) |
|
{ |
|
|
|
KASSERT(bytes != 0); |
|
audio_free(memblock); |
|
return kern_malloc(bytes, M_WAITOK); |
|
} |
|
|
|
/* |
* (Re)allocate usrbuf with 'newbufsize' bytes. |
* (Re)allocate usrbuf with 'newbufsize' bytes. |
* Use this function for usrbuf because only usrbuf can be mmapped. |
* Use this function for usrbuf because only usrbuf can be mmapped. |
* If successful, it updates track->usrbuf.mem, track->usrbuf.capacity and |
* If successful, it updates track->usrbuf.mem, track->usrbuf.capacity and |
Line 3210 audio_track_chvol(audio_filter_arg_t *ar |
|
Line 3436 audio_track_chvol(audio_filter_arg_t *ar |
|
u_int channels; |
u_int channels; |
|
|
DIAGNOSTIC_filter_arg(arg); |
DIAGNOSTIC_filter_arg(arg); |
KASSERT(arg->srcfmt->channels == arg->dstfmt->channels); |
KASSERTMSG(arg->srcfmt->channels == arg->dstfmt->channels, |
|
"arg->srcfmt->channels=%d, arg->dstfmt->channels=%d", |
|
arg->srcfmt->channels, arg->dstfmt->channels); |
KASSERT(arg->context != NULL); |
KASSERT(arg->context != NULL); |
KASSERT(arg->srcfmt->channels <= AUDIO_MAX_CHANNELS); |
KASSERTMSG(arg->srcfmt->channels <= AUDIO_MAX_CHANNELS, |
|
"arg->srcfmt->channels=%d", arg->srcfmt->channels); |
|
|
s = arg->src; |
s = arg->src; |
d = arg->dst; |
d = arg->dst; |
Line 3370 audio_track_freq_up(audio_filter_arg_t * |
|
Line 3599 audio_track_freq_up(audio_filter_arg_t * |
|
DIAGNOSTIC_ring(dst); |
DIAGNOSTIC_ring(dst); |
DIAGNOSTIC_ring(src); |
DIAGNOSTIC_ring(src); |
KASSERT(src->used > 0); |
KASSERT(src->used > 0); |
KASSERT(src->fmt.channels == dst->fmt.channels); |
KASSERTMSG(src->fmt.channels == dst->fmt.channels, |
KASSERT(src->head % track->mixer->frames_per_block == 0); |
"src->fmt.channels=%d dst->fmt.channels=%d", |
|
src->fmt.channels, dst->fmt.channels); |
|
KASSERTMSG(src->head % track->mixer->frames_per_block == 0, |
|
"src->head=%d track->mixer->frames_per_block=%d", |
|
src->head, track->mixer->frames_per_block); |
|
|
s = arg->src; |
s = arg->src; |
d = arg->dst; |
d = arg->dst; |
Line 3496 audio_track_freq_down(audio_filter_arg_t |
|
Line 3729 audio_track_freq_down(audio_filter_arg_t |
|
DIAGNOSTIC_ring(dst); |
DIAGNOSTIC_ring(dst); |
DIAGNOSTIC_ring(src); |
DIAGNOSTIC_ring(src); |
KASSERT(src->used > 0); |
KASSERT(src->used > 0); |
KASSERT(src->fmt.channels == dst->fmt.channels); |
KASSERTMSG(src->fmt.channels == dst->fmt.channels, |
|
"src->fmt.channels=%d dst->fmt.channels=%d", |
|
src->fmt.channels, dst->fmt.channels); |
KASSERTMSG(src->head % track->mixer->frames_per_block == 0, |
KASSERTMSG(src->head % track->mixer->frames_per_block == 0, |
"src->head=%d fpb=%d", |
"src->head=%d track->mixer->frames_per_block=%d", |
src->head, track->mixer->frames_per_block); |
src->head, track->mixer->frames_per_block); |
|
|
s0 = arg->src; |
s0 = arg->src; |
Line 3531 audio_track_freq_down(audio_filter_arg_t |
|
Line 3766 audio_track_freq_down(audio_filter_arg_t |
|
|
|
/* |
/* |
* Creates track and returns it. |
* Creates track and returns it. |
|
* Must be called without sc_lock held. |
*/ |
*/ |
audio_track_t * |
audio_track_t * |
audio_track_create(struct audio_softc *sc, audio_trackmixer_t *mixer) |
audio_track_create(struct audio_softc *sc, audio_trackmixer_t *mixer) |
|
|
static int |
static int |
audio_track_init_codec(audio_track_t *track, audio_ring_t **last_dstp) |
audio_track_init_codec(audio_track_t *track, audio_ring_t **last_dstp) |
{ |
{ |
struct audio_softc *sc; |
|
audio_ring_t *last_dst; |
audio_ring_t *last_dst; |
audio_ring_t *srcbuf; |
audio_ring_t *srcbuf; |
audio_format2_t *srcfmt; |
audio_format2_t *srcfmt; |
Line 3671 audio_track_init_codec(audio_track_t *tr |
|
Line 3906 audio_track_init_codec(audio_track_t *tr |
|
|
|
KASSERT(track); |
KASSERT(track); |
|
|
sc = track->mixer->sc; |
|
last_dst = *last_dstp; |
last_dst = *last_dstp; |
dstfmt = &last_dst->fmt; |
dstfmt = &last_dst->fmt; |
srcfmt = &track->inputfmt; |
srcfmt = &track->inputfmt; |
Line 3700 audio_track_init_codec(audio_track_t *tr |
|
Line 3934 audio_track_init_codec(audio_track_t *tr |
|
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
len = auring_bytelen(srcbuf); |
len = auring_bytelen(srcbuf); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
if (srcbuf->mem == NULL) { |
|
device_printf(sc->sc_dev, "%s: malloc(%d) failed\n", |
|
__func__, len); |
|
error = ENOMEM; |
|
goto abort; |
|
} |
|
|
|
arg = &track->codec.arg; |
arg = &track->codec.arg; |
arg->srcfmt = &srcbuf->fmt; |
arg->srcfmt = &srcbuf->fmt; |
|
|
static int |
static int |
audio_track_init_chvol(audio_track_t *track, audio_ring_t **last_dstp) |
audio_track_init_chvol(audio_track_t *track, audio_ring_t **last_dstp) |
{ |
{ |
struct audio_softc *sc; |
|
audio_ring_t *last_dst; |
audio_ring_t *last_dst; |
audio_ring_t *srcbuf; |
audio_ring_t *srcbuf; |
audio_format2_t *srcfmt; |
audio_format2_t *srcfmt; |
Line 3742 audio_track_init_chvol(audio_track_t *tr |
|
Line 3969 audio_track_init_chvol(audio_track_t *tr |
|
|
|
KASSERT(track); |
KASSERT(track); |
|
|
sc = track->mixer->sc; |
|
last_dst = *last_dstp; |
last_dst = *last_dstp; |
dstfmt = &last_dst->fmt; |
dstfmt = &last_dst->fmt; |
srcfmt = &track->inputfmt; |
srcfmt = &track->inputfmt; |
Line 3770 audio_track_init_chvol(audio_track_t *tr |
|
Line 3996 audio_track_init_chvol(audio_track_t *tr |
|
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
len = auring_bytelen(srcbuf); |
len = auring_bytelen(srcbuf); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
if (srcbuf->mem == NULL) { |
|
device_printf(sc->sc_dev, "%s: malloc(%d) failed\n", |
|
__func__, len); |
|
error = ENOMEM; |
|
goto abort; |
|
} |
|
|
|
arg = &track->chvol.arg; |
arg = &track->chvol.arg; |
arg->srcfmt = &srcbuf->fmt; |
arg->srcfmt = &srcbuf->fmt; |
Line 3786 audio_track_init_chvol(audio_track_t *tr |
|
Line 4006 audio_track_init_chvol(audio_track_t *tr |
|
return 0; |
return 0; |
} |
} |
|
|
abort: |
|
track->chvol.filter = NULL; |
track->chvol.filter = NULL; |
audio_free(srcbuf->mem); |
audio_free(srcbuf->mem); |
return error; |
return error; |
|
|
static int |
static int |
audio_track_init_chmix(audio_track_t *track, audio_ring_t **last_dstp) |
audio_track_init_chmix(audio_track_t *track, audio_ring_t **last_dstp) |
{ |
{ |
struct audio_softc *sc; |
|
audio_ring_t *last_dst; |
audio_ring_t *last_dst; |
audio_ring_t *srcbuf; |
audio_ring_t *srcbuf; |
audio_format2_t *srcfmt; |
audio_format2_t *srcfmt; |
Line 3814 audio_track_init_chmix(audio_track_t *tr |
|
Line 4032 audio_track_init_chmix(audio_track_t *tr |
|
|
|
KASSERT(track); |
KASSERT(track); |
|
|
sc = track->mixer->sc; |
|
last_dst = *last_dstp; |
last_dst = *last_dstp; |
dstfmt = &last_dst->fmt; |
dstfmt = &last_dst->fmt; |
srcfmt = &track->inputfmt; |
srcfmt = &track->inputfmt; |
Line 3845 audio_track_init_chmix(audio_track_t *tr |
|
Line 4062 audio_track_init_chmix(audio_track_t *tr |
|
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
len = auring_bytelen(srcbuf); |
len = auring_bytelen(srcbuf); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
if (srcbuf->mem == NULL) { |
|
device_printf(sc->sc_dev, "%s: malloc(%d) failed\n", |
|
__func__, len); |
|
error = ENOMEM; |
|
goto abort; |
|
} |
|
|
|
arg = &track->chmix.arg; |
arg = &track->chmix.arg; |
arg->srcfmt = &srcbuf->fmt; |
arg->srcfmt = &srcbuf->fmt; |
Line 3861 audio_track_init_chmix(audio_track_t *tr |
|
Line 4072 audio_track_init_chmix(audio_track_t *tr |
|
return 0; |
return 0; |
} |
} |
|
|
abort: |
|
track->chmix.filter = NULL; |
track->chmix.filter = NULL; |
audio_free(srcbuf->mem); |
audio_free(srcbuf->mem); |
return error; |
return error; |
|
|
static int |
static int |
audio_track_init_freq(audio_track_t *track, audio_ring_t **last_dstp) |
audio_track_init_freq(audio_track_t *track, audio_ring_t **last_dstp) |
{ |
{ |
struct audio_softc *sc; |
|
audio_ring_t *last_dst; |
audio_ring_t *last_dst; |
audio_ring_t *srcbuf; |
audio_ring_t *srcbuf; |
audio_format2_t *srcfmt; |
audio_format2_t *srcfmt; |
Line 3891 audio_track_init_freq(audio_track_t *tra |
|
Line 4100 audio_track_init_freq(audio_track_t *tra |
|
|
|
KASSERT(track); |
KASSERT(track); |
|
|
sc = track->mixer->sc; |
|
last_dst = *last_dstp; |
last_dst = *last_dstp; |
dstfmt = &last_dst->fmt; |
dstfmt = &last_dst->fmt; |
srcfmt = &track->inputfmt; |
srcfmt = &track->inputfmt; |
Line 3930 audio_track_init_freq(audio_track_t *tra |
|
Line 4138 audio_track_init_freq(audio_track_t *tra |
|
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
srcbuf->capacity = frame_per_block(track->mixer, &srcbuf->fmt); |
len = auring_bytelen(srcbuf); |
len = auring_bytelen(srcbuf); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
srcbuf->mem = audio_realloc(srcbuf->mem, len); |
if (srcbuf->mem == NULL) { |
|
device_printf(sc->sc_dev, "%s: malloc(%d) failed\n", |
|
__func__, len); |
|
error = ENOMEM; |
|
goto abort; |
|
} |
|
|
|
arg = &track->freq.arg; |
arg = &track->freq.arg; |
arg->srcfmt = &srcbuf->fmt; |
arg->srcfmt = &srcbuf->fmt; |
Line 3946 audio_track_init_freq(audio_track_t *tra |
|
Line 4148 audio_track_init_freq(audio_track_t *tra |
|
return 0; |
return 0; |
} |
} |
|
|
abort: |
|
track->freq.filter = NULL; |
track->freq.filter = NULL; |
audio_free(srcbuf->mem); |
audio_free(srcbuf->mem); |
return error; |
return error; |
Line 4128 audio_track_set_format(audio_track_t *tr |
|
Line 4329 audio_track_set_format(audio_track_t *tr |
|
frame_per_block(track->mixer, &track->input->fmt); |
frame_per_block(track->mixer, &track->input->fmt); |
len = auring_bytelen(track->input); |
len = auring_bytelen(track->input); |
track->input->mem = audio_realloc(track->input->mem, len); |
track->input->mem = audio_realloc(track->input->mem, len); |
if (track->input->mem == NULL) { |
|
device_printf(sc->sc_dev, "malloc input(%d) failed\n", |
|
len); |
|
error = ENOMEM; |
|
goto error; |
|
} |
|
} |
} |
|
|
/* |
/* |
Line 4230 audio_append_silence(audio_track_t *trac |
|
Line 4425 audio_append_silence(audio_track_t *trac |
|
|
|
n = (ring->capacity - ring->used) % fpb; |
n = (ring->capacity - ring->used) % fpb; |
|
|
KASSERT(auring_get_contig_free(ring) >= n); |
KASSERTMSG(auring_get_contig_free(ring) >= n, |
|
"auring_get_contig_free(ring)=%d n=%d", |
|
auring_get_contig_free(ring), n); |
|
|
memset(auring_tailptr_aint(ring), 0, |
memset(auring_tailptr_aint(ring), 0, |
n * ring->fmt.channels * sizeof(aint_t)); |
n * ring->fmt.channels * sizeof(aint_t)); |
Line 4263 audio_apply_stage(audio_track_t *track, |
|
Line 4460 audio_apply_stage(audio_track_t *track, |
|
dstcount = auring_get_contig_free(stage->dst); |
dstcount = auring_get_contig_free(stage->dst); |
|
|
if (isfreq) { |
if (isfreq) { |
KASSERTMSG(srccount > 0, "freq but srccount == %d", srccount); |
KASSERTMSG(srccount > 0, "freq but srccount=%d", srccount); |
count = uimin(dstcount, track->mixer->frames_per_block); |
count = uimin(dstcount, track->mixer->frames_per_block); |
} else { |
} else { |
count = uimin(srccount, dstcount); |
count = uimin(srccount, dstcount); |
Line 4348 audio_track_play(audio_track_t *track) |
|
Line 4545 audio_track_play(audio_track_t *track) |
|
int bytes2; |
int bytes2; |
|
|
bytes1 = auring_get_contig_used(usrbuf); |
bytes1 = auring_get_contig_used(usrbuf); |
KASSERT(bytes1 % framesize == 0); |
KASSERTMSG(bytes1 % framesize == 0, |
|
"bytes1=%d framesize=%d", bytes1, framesize); |
memcpy((uint8_t *)input->mem + auring_tail(input) * framesize, |
memcpy((uint8_t *)input->mem + auring_tail(input) * framesize, |
(uint8_t *)usrbuf->mem + usrbuf->head, |
(uint8_t *)usrbuf->mem + usrbuf->head, |
bytes1); |
bytes1); |
Line 4510 audio_track_record(audio_track_t *track) |
|
Line 4708 audio_track_record(audio_track_t *track) |
|
int bytes1; |
int bytes1; |
int bytes2; |
int bytes2; |
|
|
bytes1 = auring_get_contig_used(usrbuf); |
bytes1 = auring_get_contig_free(usrbuf); |
KASSERT(bytes1 % framesize == 0); |
KASSERTMSG(bytes1 % framesize == 0, |
|
"bytes1=%d framesize=%d", bytes1, framesize); |
memcpy((uint8_t *)usrbuf->mem + auring_tail(usrbuf), |
memcpy((uint8_t *)usrbuf->mem + auring_tail(usrbuf), |
(uint8_t *)outbuf->mem + outbuf->head * framesize, |
(uint8_t *)outbuf->mem + outbuf->head * framesize, |
bytes1); |
bytes1); |
Line 4540 audio_track_record(audio_track_t *track) |
|
Line 4739 audio_track_record(audio_track_t *track) |
|
|
|
/* |
/* |
* Calcurate blktime [msec] from mixer(.hwbuf.fmt). |
* Calcurate blktime [msec] from mixer(.hwbuf.fmt). |
* Must be called with sc_lock held. |
* Must be called with sc_exlock held. |
*/ |
*/ |
static u_int |
static u_int |
audio_mixer_calc_blktime(struct audio_softc *sc, audio_trackmixer_t *mixer) |
audio_mixer_calc_blktime(struct audio_softc *sc, audio_trackmixer_t *mixer) |
Line 4549 audio_mixer_calc_blktime(struct audio_so |
|
Line 4748 audio_mixer_calc_blktime(struct audio_so |
|
u_int blktime; |
u_int blktime; |
u_int frames_per_block; |
u_int frames_per_block; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock); |
|
|
fmt = &mixer->hwbuf.fmt; |
fmt = &mixer->hwbuf.fmt; |
blktime = sc->sc_blk_ms; |
blktime = sc->sc_blk_ms; |
Line 4577 audio_mixer_calc_blktime(struct audio_so |
|
Line 4776 audio_mixer_calc_blktime(struct audio_so |
|
* Set AUMODE_PLAY to the 'mode' for playback or AUMODE_RECORD for recording. |
* Set AUMODE_PLAY to the 'mode' for playback or AUMODE_RECORD for recording. |
* sc->sc_[pr]mixer (corresponding to the 'mode') must be zero-filled. |
* sc->sc_[pr]mixer (corresponding to the 'mode') must be zero-filled. |
* This function returns 0 on sucessful. Otherwise returns errno. |
* This function returns 0 on sucessful. Otherwise returns errno. |
* Must be called with sc_lock held. |
* Must be called with sc_exlock held and without sc_lock held. |
*/ |
*/ |
static int |
static int |
audio_mixer_init(struct audio_softc *sc, int mode, |
audio_mixer_init(struct audio_softc *sc, int mode, |
const audio_format2_t *hwfmt, const audio_filter_reg_t *reg) |
const audio_format2_t *hwfmt, const audio_filter_reg_t *reg) |
{ |
{ |
char codecbuf[64]; |
char codecbuf[64]; |
|
char blkdmsbuf[8]; |
audio_trackmixer_t *mixer; |
audio_trackmixer_t *mixer; |
void (*softint_handler)(void *); |
void (*softint_handler)(void *); |
int len; |
int len; |
Line 4592 audio_mixer_init(struct audio_softc *sc, |
|
Line 4792 audio_mixer_init(struct audio_softc *sc, |
|
size_t bufsize; |
size_t bufsize; |
int hwblks; |
int hwblks; |
int blkms; |
int blkms; |
|
int blkdms; |
int error; |
int error; |
|
|
KASSERT(hwfmt != NULL); |
KASSERT(hwfmt != NULL); |
KASSERT(reg != NULL); |
KASSERT(reg != NULL); |
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock); |
|
|
error = 0; |
error = 0; |
if (mode == AUMODE_PLAY) |
if (mode == AUMODE_PLAY) |
Line 4619 audio_mixer_init(struct audio_softc *sc, |
|
Line 4820 audio_mixer_init(struct audio_softc *sc, |
|
if (sc->hw_if->round_blocksize) { |
if (sc->hw_if->round_blocksize) { |
int rounded; |
int rounded; |
audio_params_t p = format2_to_params(&mixer->hwbuf.fmt); |
audio_params_t p = format2_to_params(&mixer->hwbuf.fmt); |
|
mutex_enter(sc->sc_lock); |
rounded = sc->hw_if->round_blocksize(sc->hw_hdl, blksize, |
rounded = sc->hw_if->round_blocksize(sc->hw_hdl, blksize, |
mode, &p); |
mode, &p); |
TRACE(2, "round_blocksize %d -> %d", blksize, rounded); |
mutex_exit(sc->sc_lock); |
|
TRACE(1, "round_blocksize %d -> %d", blksize, rounded); |
if (rounded != blksize) { |
if (rounded != blksize) { |
if ((rounded * NBBY) % (mixer->hwbuf.fmt.stride * |
if ((rounded * NBBY) % (mixer->hwbuf.fmt.stride * |
mixer->hwbuf.fmt.channels) != 0) { |
mixer->hwbuf.fmt.channels) != 0) { |
device_printf(sc->sc_dev, |
device_printf(sc->sc_dev, |
"blksize not configured %d -> %d\n", |
"round_blocksize must return blocksize " |
blksize, rounded); |
"divisible by framesize: " |
|
"blksize=%d rounded=%d " |
|
"stride=%ubit channels=%u\n", |
|
blksize, rounded, |
|
mixer->hwbuf.fmt.stride, |
|
mixer->hwbuf.fmt.channels); |
return EINVAL; |
return EINVAL; |
} |
} |
/* Recalculation */ |
/* Recalculation */ |
Line 4644 audio_mixer_init(struct audio_softc *sc, |
|
Line 4852 audio_mixer_init(struct audio_softc *sc, |
|
bufsize = frametobyte(&mixer->hwbuf.fmt, capacity); |
bufsize = frametobyte(&mixer->hwbuf.fmt, capacity); |
if (sc->hw_if->round_buffersize) { |
if (sc->hw_if->round_buffersize) { |
size_t rounded; |
size_t rounded; |
|
mutex_enter(sc->sc_lock); |
rounded = sc->hw_if->round_buffersize(sc->hw_hdl, mode, |
rounded = sc->hw_if->round_buffersize(sc->hw_hdl, mode, |
bufsize); |
bufsize); |
TRACE(2, "round_buffersize %zd -> %zd", bufsize, rounded); |
mutex_exit(sc->sc_lock); |
|
TRACE(1, "round_buffersize %zd -> %zd", bufsize, rounded); |
if (rounded < bufsize) { |
if (rounded < bufsize) { |
/* buffersize needs NBLKHW blocks at least. */ |
/* buffersize needs NBLKHW blocks at least. */ |
device_printf(sc->sc_dev, |
device_printf(sc->sc_dev, |
Line 4669 audio_mixer_init(struct audio_softc *sc, |
|
Line 4879 audio_mixer_init(struct audio_softc *sc, |
|
capacity = mixer->frames_per_block * hwblks; |
capacity = mixer->frames_per_block * hwblks; |
} |
} |
} |
} |
TRACE(2, "buffersize for %s = %zu", |
TRACE(1, "buffersize for %s = %zu", |
(mode == AUMODE_PLAY) ? "playback" : "recording", |
(mode == AUMODE_PLAY) ? "playback" : "recording", |
bufsize); |
bufsize); |
mixer->hwbuf.capacity = capacity; |
mixer->hwbuf.capacity = capacity; |
|
|
/* |
|
* XXX need to release sc_lock for compatibility? |
|
*/ |
|
if (sc->hw_if->allocm) { |
if (sc->hw_if->allocm) { |
|
/* sc_lock is not necessary for allocm */ |
mixer->hwbuf.mem = sc->hw_if->allocm(sc->hw_hdl, mode, bufsize); |
mixer->hwbuf.mem = sc->hw_if->allocm(sc->hw_hdl, mode, bufsize); |
if (mixer->hwbuf.mem == NULL) { |
if (mixer->hwbuf.mem == NULL) { |
device_printf(sc->sc_dev, "%s: allocm(%zu) failed\n", |
device_printf(sc->sc_dev, "%s: allocm(%zu) failed\n", |
Line 4728 audio_mixer_init(struct audio_softc *sc, |
|
Line 4936 audio_mixer_init(struct audio_softc *sc, |
|
len = mixer->frames_per_block * mixer->mixfmt.channels * |
len = mixer->frames_per_block * mixer->mixfmt.channels * |
mixer->mixfmt.stride / NBBY; |
mixer->mixfmt.stride / NBBY; |
mixer->mixsample = audio_realloc(mixer->mixsample, len); |
mixer->mixsample = audio_realloc(mixer->mixsample, len); |
if (mixer->mixsample == NULL) { |
|
device_printf(sc->sc_dev, |
|
"%s: malloc mixsample(%d) failed\n", |
|
__func__, len); |
|
error = ENOMEM; |
|
goto abort; |
|
} |
|
} else { |
} else { |
/* No mixing buffer for recording */ |
/* No mixing buffer for recording */ |
} |
} |
Line 4771 audio_mixer_init(struct audio_softc *sc, |
|
Line 4972 audio_mixer_init(struct audio_softc *sc, |
|
mixer->hwbuf.fmt.precision); |
mixer->hwbuf.fmt.precision); |
} |
} |
blkms = mixer->blktime_n * 1000 / mixer->blktime_d; |
blkms = mixer->blktime_n * 1000 / mixer->blktime_d; |
aprint_normal_dev(sc->sc_dev, "%s:%d%s %dch %dHz, blk %dms for %s\n", |
blkdms = (mixer->blktime_n * 10000 / mixer->blktime_d) % 10; |
|
blkdmsbuf[0] = '\0'; |
|
if (blkdms != 0) { |
|
snprintf(blkdmsbuf, sizeof(blkdmsbuf), ".%1d", blkdms); |
|
} |
|
aprint_normal_dev(sc->sc_dev, |
|
"%s:%d%s %dch %dHz, blk %d bytes (%d%sms) for %s\n", |
audio_encoding_name(mixer->track_fmt.encoding), |
audio_encoding_name(mixer->track_fmt.encoding), |
mixer->track_fmt.precision, |
mixer->track_fmt.precision, |
codecbuf, |
codecbuf, |
mixer->track_fmt.channels, |
mixer->track_fmt.channels, |
mixer->track_fmt.sample_rate, |
mixer->track_fmt.sample_rate, |
blkms, |
blksize, |
|
blkms, blkdmsbuf, |
(mode == AUMODE_PLAY) ? "playback" : "recording"); |
(mode == AUMODE_PLAY) ? "playback" : "recording"); |
|
|
return 0; |
return 0; |
|
|
/* |
/* |
* Releases all resources of 'mixer'. |
* Releases all resources of 'mixer'. |
* Note that it does not release the memory area of 'mixer' itself. |
* Note that it does not release the memory area of 'mixer' itself. |
* Must be called with sc_lock held. |
* Must be called with sc_exlock held and without sc_lock held. |
*/ |
*/ |
static void |
static void |
audio_mixer_destroy(struct audio_softc *sc, audio_trackmixer_t *mixer) |
audio_mixer_destroy(struct audio_softc *sc, audio_trackmixer_t *mixer) |
{ |
{ |
int bufsize; |
int bufsize; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock == 1); |
|
|
bufsize = frametobyte(&mixer->hwbuf.fmt, mixer->hwbuf.capacity); |
bufsize = frametobyte(&mixer->hwbuf.fmt, mixer->hwbuf.capacity); |
|
|
if (mixer->hwbuf.mem != NULL) { |
if (mixer->hwbuf.mem != NULL) { |
if (sc->hw_if->freem) { |
if (sc->hw_if->freem) { |
|
/* sc_lock is not necessary for freem */ |
sc->hw_if->freem(sc->hw_hdl, mixer->hwbuf.mem, bufsize); |
sc->hw_if->freem(sc->hw_hdl, mixer->hwbuf.mem, bufsize); |
} else { |
} else { |
kmem_free(mixer->hwbuf.mem, bufsize); |
kmem_free(mixer->hwbuf.mem, bufsize); |
Line 4914 audio_pmixer_process(struct audio_softc |
|
Line 5123 audio_pmixer_process(struct audio_softc |
|
mixer = sc->sc_pmixer; |
mixer = sc->sc_pmixer; |
|
|
frame_count = mixer->frames_per_block; |
frame_count = mixer->frames_per_block; |
KASSERT(auring_get_contig_free(&mixer->hwbuf) >= frame_count); |
KASSERTMSG(auring_get_contig_free(&mixer->hwbuf) >= frame_count, |
|
"auring_get_contig_free()=%d frame_count=%d", |
|
auring_get_contig_free(&mixer->hwbuf), frame_count); |
sample_count = frame_count * mixer->mixfmt.channels; |
sample_count = frame_count * mixer->mixfmt.channels; |
|
|
mixer->mixseq++; |
mixer->mixseq++; |
Line 5205 audio_pmixer_output(struct audio_softc * |
|
Line 5416 audio_pmixer_output(struct audio_softc * |
|
TRACE(4, "pbusy=%d hwbuf=%d/%d/%d", |
TRACE(4, "pbusy=%d hwbuf=%d/%d/%d", |
sc->sc_pbusy, |
sc->sc_pbusy, |
mixer->hwbuf.head, mixer->hwbuf.used, mixer->hwbuf.capacity); |
mixer->hwbuf.head, mixer->hwbuf.used, mixer->hwbuf.capacity); |
KASSERT(mixer->hwbuf.used >= mixer->frames_per_block); |
KASSERTMSG(mixer->hwbuf.used >= mixer->frames_per_block, |
|
"mixer->hwbuf.used=%d mixer->frames_per_block=%d", |
|
mixer->hwbuf.used, mixer->frames_per_block); |
|
|
blksize = frametobyte(&mixer->hwbuf.fmt, mixer->frames_per_block); |
blksize = frametobyte(&mixer->hwbuf.fmt, mixer->frames_per_block); |
|
|
Line 5256 audio_pintr(void *arg) |
|
Line 5469 audio_pintr(void *arg) |
|
|
|
if (sc->sc_dying) |
if (sc->sc_dying) |
return; |
return; |
#if defined(DIAGNOSTIC) |
|
if (sc->sc_pbusy == false) { |
if (sc->sc_pbusy == false) { |
device_printf(sc->sc_dev, "stray interrupt\n"); |
#if defined(DIAGNOSTIC) |
|
device_printf(sc->sc_dev, |
|
"DIAGNOSTIC: %s raised stray interrupt\n", |
|
device_xname(sc->hw_dev)); |
|
#endif |
return; |
return; |
} |
} |
#endif |
|
|
|
mixer = sc->sc_pmixer; |
mixer = sc->sc_pmixer; |
mixer->hw_complete_counter += mixer->frames_per_block; |
mixer->hw_complete_counter += mixer->frames_per_block; |
Line 5448 audio_rmixer_process(struct audio_softc |
|
Line 5663 audio_rmixer_process(struct audio_softc |
|
input->head, input->used, input->capacity); |
input->head, input->used, input->capacity); |
auring_take(input, drops); |
auring_take(input, drops); |
} |
} |
KASSERT(input->used % mixer->frames_per_block == 0); |
KASSERTMSG(input->used % mixer->frames_per_block == 0, |
|
"input->used=%d mixer->frames_per_block=%d", |
|
input->used, mixer->frames_per_block); |
|
|
memcpy(auring_tailptr_aint(input), |
memcpy(auring_tailptr_aint(input), |
auring_headptr_aint(mixersrc), |
auring_headptr_aint(mixersrc), |
Line 5527 audio_rintr(void *arg) |
|
Line 5744 audio_rintr(void *arg) |
|
|
|
if (sc->sc_dying) |
if (sc->sc_dying) |
return; |
return; |
#if defined(DIAGNOSTIC) |
|
if (sc->sc_rbusy == false) { |
if (sc->sc_rbusy == false) { |
device_printf(sc->sc_dev, "stray interrupt\n"); |
#if defined(DIAGNOSTIC) |
|
device_printf(sc->sc_dev, |
|
"DIAGNOSTIC: %s raised stray interrupt\n", |
|
device_xname(sc->hw_dev)); |
|
#endif |
return; |
return; |
} |
} |
#endif |
|
|
|
mixer = sc->sc_rmixer; |
mixer = sc->sc_rmixer; |
mixer->hw_complete_counter += mixer->frames_per_block; |
mixer->hw_complete_counter += mixer->frames_per_block; |
Line 5730 audio_track_drain(struct audio_softc *sc |
|
Line 5949 audio_track_drain(struct audio_softc *sc |
|
} |
} |
|
|
/* |
/* |
|
* Send signal to process. |
|
* This is intended to be called only from audio_softintr_{rd,wr}. |
|
* Must be called without sc_intr_lock held. |
|
*/ |
|
static inline void |
|
audio_psignal(struct audio_softc *sc, pid_t pid, int signum) |
|
{ |
|
proc_t *p; |
|
|
|
KASSERT(pid != 0); |
|
|
|
/* |
|
* psignal() must be called without spin lock held. |
|
*/ |
|
|
|
mutex_enter(proc_lock); |
|
p = proc_find(pid); |
|
if (p) |
|
psignal(p, signum); |
|
mutex_exit(proc_lock); |
|
} |
|
|
|
/* |
* This is software interrupt handler for record. |
* This is software interrupt handler for record. |
* It is called from recording hardware interrupt everytime. |
* It is called from recording hardware interrupt everytime. |
* It does: |
* It does: |
Line 5747 audio_softintr_rd(void *cookie) |
|
Line 5989 audio_softintr_rd(void *cookie) |
|
{ |
{ |
struct audio_softc *sc = cookie; |
struct audio_softc *sc = cookie; |
audio_file_t *f; |
audio_file_t *f; |
proc_t *p; |
|
pid_t pid; |
pid_t pid; |
|
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_intr_lock); |
|
|
|
SLIST_FOREACH(f, &sc->sc_files, entry) { |
SLIST_FOREACH(f, &sc->sc_files, entry) { |
audio_track_t *track = f->rtrack; |
audio_track_t *track = f->rtrack; |
Line 5767 audio_softintr_rd(void *cookie) |
|
Line 6007 audio_softintr_rd(void *cookie) |
|
pid = f->async_audio; |
pid = f->async_audio; |
if (pid != 0) { |
if (pid != 0) { |
TRACEF(4, f, "sending SIGIO %d", pid); |
TRACEF(4, f, "sending SIGIO %d", pid); |
mutex_enter(proc_lock); |
audio_psignal(sc, pid, SIGIO); |
if ((p = proc_find(pid)) != NULL) |
|
psignal(p, SIGIO); |
|
mutex_exit(proc_lock); |
|
} |
} |
} |
} |
mutex_exit(sc->sc_intr_lock); |
|
|
|
/* Notify that data has arrived. */ |
/* Notify that data has arrived. */ |
selnotify(&sc->sc_rsel, 0, NOTE_SUBMIT); |
selnotify(&sc->sc_rsel, 0, NOTE_SUBMIT); |
Line 5799 audio_softintr_wr(void *cookie) |
|
Line 6035 audio_softintr_wr(void *cookie) |
|
struct audio_softc *sc = cookie; |
struct audio_softc *sc = cookie; |
audio_file_t *f; |
audio_file_t *f; |
bool found; |
bool found; |
proc_t *p; |
|
pid_t pid; |
pid_t pid; |
|
|
TRACE(4, "called"); |
TRACE(4, "called"); |
found = false; |
found = false; |
|
|
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_lock); |
mutex_enter(sc->sc_intr_lock); |
|
|
|
SLIST_FOREACH(f, &sc->sc_files, entry) { |
SLIST_FOREACH(f, &sc->sc_files, entry) { |
audio_track_t *track = f->ptrack; |
audio_track_t *track = f->ptrack; |
Line 5826 audio_softintr_wr(void *cookie) |
|
Line 6060 audio_softintr_wr(void *cookie) |
|
*/ |
*/ |
if (track->usrbuf.used <= track->usrbuf_usedlow && |
if (track->usrbuf.used <= track->usrbuf_usedlow && |
!track->is_pause) { |
!track->is_pause) { |
|
/* For selnotify */ |
found = true; |
found = true; |
|
/* For SIGIO */ |
pid = f->async_audio; |
pid = f->async_audio; |
if (pid != 0) { |
if (pid != 0) { |
TRACEF(4, f, "sending SIGIO %d", pid); |
TRACEF(4, f, "sending SIGIO %d", pid); |
mutex_enter(proc_lock); |
audio_psignal(sc, pid, SIGIO); |
if ((p = proc_find(pid)) != NULL) |
|
psignal(p, SIGIO); |
|
mutex_exit(proc_lock); |
|
} |
} |
} |
} |
} |
} |
mutex_exit(sc->sc_intr_lock); |
|
|
|
/* |
/* |
* Notify for select/poll when someone become writable. |
* Notify for select/poll when someone become writable. |
Line 5941 audio_check_params(audio_format2_t *p) |
|
Line 6173 audio_check_params(audio_format2_t *p) |
|
* phwfmt and rhwfmt indicate the hardware format. pfil and rfil indicate |
* phwfmt and rhwfmt indicate the hardware format. pfil and rfil indicate |
* the filter registration information. These four must not be NULL. |
* the filter registration information. These four must not be NULL. |
* If successful returns 0. Otherwise returns errno. |
* If successful returns 0. Otherwise returns errno. |
* Must be called with sc_lock held. |
* Must be called with sc_exlock held and without sc_lock held. |
* Must not be called if there are any tracks. |
* Must not be called if there are any tracks. |
* Caller should check that the initialization succeed by whether |
* Caller should check that the initialization succeed by whether |
* sc_[pr]mixer is not NULL. |
* sc_[pr]mixer is not NULL. |
Line 5957 audio_mixers_init(struct audio_softc *sc |
|
Line 6189 audio_mixers_init(struct audio_softc *sc |
|
KASSERT(rhwfmt != NULL); |
KASSERT(rhwfmt != NULL); |
KASSERT(pfil != NULL); |
KASSERT(pfil != NULL); |
KASSERT(rfil != NULL); |
KASSERT(rfil != NULL); |
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock); |
|
|
if ((mode & AUMODE_PLAY)) { |
if ((mode & AUMODE_PLAY)) { |
if (sc->sc_pmixer == NULL) { |
if (sc->sc_pmixer == NULL) { |
Line 6044 audio_select_freq(const struct audio_for |
|
Line 6276 audio_select_freq(const struct audio_for |
|
} |
} |
|
|
/* |
/* |
* Probe playback and/or recording format (depending on *modep). |
|
* *modep is an in-out parameter. It indicates the direction to configure |
|
* as an argument, and the direction configured is written back as out |
|
* parameter. |
|
* If successful, probed hardware format is stored into *phwfmt, *rhwfmt |
|
* depending on *modep, and return 0. Otherwise it returns errno. |
|
* Must be called with sc_lock held. |
|
*/ |
|
static int |
|
audio_hw_probe(struct audio_softc *sc, int is_indep, int *modep, |
|
audio_format2_t *phwfmt, audio_format2_t *rhwfmt) |
|
{ |
|
audio_format2_t fmt; |
|
int mode; |
|
int error = 0; |
|
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
|
|
mode = *modep; |
|
KASSERTMSG((mode & (AUMODE_PLAY | AUMODE_RECORD)) != 0, |
|
"invalid mode = %x", mode); |
|
|
|
if (is_indep) { |
|
int errorp = 0, errorr = 0; |
|
|
|
/* On independent devices, probe separately. */ |
|
if ((mode & AUMODE_PLAY) != 0) { |
|
errorp = audio_hw_probe_fmt(sc, phwfmt, AUMODE_PLAY); |
|
if (errorp) |
|
mode &= ~AUMODE_PLAY; |
|
} |
|
if ((mode & AUMODE_RECORD) != 0) { |
|
errorr = audio_hw_probe_fmt(sc, rhwfmt, AUMODE_RECORD); |
|
if (errorr) |
|
mode &= ~AUMODE_RECORD; |
|
} |
|
|
|
/* Return error if both play and record probes failed. */ |
|
if (errorp && errorr) |
|
error = errorp; |
|
} else { |
|
/* On non independent devices, probe simultaneously. */ |
|
error = audio_hw_probe_fmt(sc, &fmt, mode); |
|
if (error) { |
|
mode = 0; |
|
} else { |
|
*phwfmt = fmt; |
|
*rhwfmt = fmt; |
|
} |
|
} |
|
|
|
*modep = mode; |
|
return error; |
|
} |
|
|
|
/* |
|
* Choose the most preferred hardware format. |
* Choose the most preferred hardware format. |
* If successful, it will store the chosen format into *cand and return 0. |
* If successful, it will store the chosen format into *cand and return 0. |
* Otherwise, return errno. |
* Otherwise, return errno. |
* Must be called with sc_lock held. |
* Must be called without sc_lock held. |
*/ |
*/ |
static int |
static int |
audio_hw_probe_fmt(struct audio_softc *sc, audio_format2_t *cand, int mode) |
audio_hw_probe(struct audio_softc *sc, audio_format2_t *cand, int mode) |
{ |
{ |
audio_format_query_t query; |
audio_format_query_t query; |
int cand_score; |
int cand_score; |
Line 6114 audio_hw_probe_fmt(struct audio_softc *s |
|
Line 6290 audio_hw_probe_fmt(struct audio_softc *s |
|
int i; |
int i; |
int error; |
int error; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
|
|
/* |
/* |
* Score each formats and choose the highest one. |
* Score each formats and choose the highest one. |
* |
* |
Line 6130 audio_hw_probe_fmt(struct audio_softc *s |
|
Line 6304 audio_hw_probe_fmt(struct audio_softc *s |
|
memset(&query, 0, sizeof(query)); |
memset(&query, 0, sizeof(query)); |
query.index = i; |
query.index = i; |
|
|
|
mutex_enter(sc->sc_lock); |
error = sc->hw_if->query_format(sc->hw_hdl, &query); |
error = sc->hw_if->query_format(sc->hw_hdl, &query); |
|
mutex_exit(sc->sc_lock); |
if (error == EINVAL) |
if (error == EINVAL) |
break; |
break; |
if (error) |
if (error) |
Line 6218 audio_hw_probe_fmt(struct audio_softc *s |
|
Line 6394 audio_hw_probe_fmt(struct audio_softc *s |
|
* Validate fmt with query_format. |
* Validate fmt with query_format. |
* If fmt is included in the result of query_format, returns 0. |
* If fmt is included in the result of query_format, returns 0. |
* Otherwise returns EINVAL. |
* Otherwise returns EINVAL. |
* Must be called with sc_lock held. |
* Must be called without sc_lock held. |
*/ |
*/ |
static int |
static int |
audio_hw_validate_format(struct audio_softc *sc, int mode, |
audio_hw_validate_format(struct audio_softc *sc, int mode, |
Line 6230 audio_hw_validate_format(struct audio_so |
|
Line 6406 audio_hw_validate_format(struct audio_so |
|
int error; |
int error; |
int j; |
int j; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
|
|
/* |
|
* If query_format is not supported by hardware driver, |
|
* a rough check instead will be performed. |
|
* XXX This will gone in the future. |
|
*/ |
|
if (sc->hw_if->query_format == NULL) { |
|
if (fmt->encoding != AUDIO_ENCODING_SLINEAR_NE) |
|
return EINVAL; |
|
if (fmt->precision != AUDIO_INTERNAL_BITS) |
|
return EINVAL; |
|
if (fmt->stride != AUDIO_INTERNAL_BITS) |
|
return EINVAL; |
|
return 0; |
|
} |
|
|
|
for (index = 0; ; index++) { |
for (index = 0; ; index++) { |
query.index = index; |
query.index = index; |
|
mutex_enter(sc->sc_lock); |
error = sc->hw_if->query_format(sc->hw_hdl, &query); |
error = sc->hw_if->query_format(sc->hw_hdl, &query); |
|
mutex_exit(sc->sc_lock); |
if (error == EINVAL) |
if (error == EINVAL) |
break; |
break; |
if (error) |
if (error) |
Line 6306 audio_hw_validate_format(struct audio_so |
|
Line 6467 audio_hw_validate_format(struct audio_so |
|
* All other fields in ai are ignored. |
* All other fields in ai are ignored. |
* If successful returns 0. Otherwise returns errno. |
* If successful returns 0. Otherwise returns errno. |
* This function does not roll back even if it fails. |
* This function does not roll back even if it fails. |
* Must be called with sc_lock held. |
* Must be called with sc_exlock held and without sc_lock held. |
*/ |
*/ |
static int |
static int |
audio_mixers_set_format(struct audio_softc *sc, const struct audio_info *ai) |
audio_mixers_set_format(struct audio_softc *sc, const struct audio_info *ai) |
Line 6318 audio_mixers_set_format(struct audio_sof |
|
Line 6479 audio_mixers_set_format(struct audio_sof |
|
int mode; |
int mode; |
int error; |
int error; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock); |
|
|
/* |
/* |
* Even when setting either one of playback and recording, |
* Even when setting either one of playback and recording, |
Line 6406 audio_mixers_set_format(struct audio_sof |
|
Line 6567 audio_mixers_set_format(struct audio_sof |
|
if (error) |
if (error) |
return error; |
return error; |
|
|
|
/* |
|
* Reinitialize the sticky parameters for /dev/sound. |
|
* If the number of the hardware channels becomes less than the number |
|
* of channels that sticky parameters remember, subsequent /dev/sound |
|
* open will fail. To prevent this, reinitialize the sticky |
|
* parameters whenever the hardware format is changed. |
|
*/ |
|
sc->sc_sound_pparams = params_to_format2(&audio_default); |
|
sc->sc_sound_rparams = params_to_format2(&audio_default); |
|
sc->sc_sound_ppause = false; |
|
sc->sc_sound_rpause = false; |
|
|
return 0; |
return 0; |
} |
} |
|
|
/* |
/* |
* Store current mixers format into *ai. |
* Store current mixers format into *ai. |
|
* Must be called with sc_exlock held. |
*/ |
*/ |
static void |
static void |
audio_mixers_get_format(struct audio_softc *sc, struct audio_info *ai) |
audio_mixers_get_format(struct audio_softc *sc, struct audio_info *ai) |
{ |
{ |
|
|
|
KASSERT(sc->sc_exlock); |
|
|
/* |
/* |
* There is no stride information in audio_info but it doesn't matter. |
* There is no stride information in audio_info but it doesn't matter. |
* trackmixer always treats stride and precision as the same. |
* trackmixer always treats stride and precision as the same. |
Line 6517 audio_mixers_get_format(struct audio_sof |
|
Line 6694 audio_mixers_get_format(struct audio_sof |
|
/* |
/* |
* Pause consideration: |
* Pause consideration: |
* |
* |
* The introduction of these two behavior makes pause/unpause operation |
* Pausing/unpausing never affect [pr]mixer. This single rule makes |
* simple. |
* operation simple. Note that playback and recording are asymmetric. |
* 1. The first read/write access of the first track makes mixer start. |
* |
* 2. A pause of the last track doesn't make mixer stop. |
* For playback, |
|
* 1. Any playback open doesn't start pmixer regardless of initial pause |
|
* state of this track. |
|
* 2. The first write access among playback tracks only starts pmixer |
|
* regardless of this track's pause state. |
|
* 3. Even a pause of the last playback track doesn't stop pmixer. |
|
* 4. The last close of all playback tracks only stops pmixer. |
|
* |
|
* For recording, |
|
* 1. The first recording open only starts rmixer regardless of initial |
|
* pause state of this track. |
|
* 2. Even a pause of the last track doesn't stop rmixer. |
|
* 3. The last close of all recording tracks only stops rmixer. |
*/ |
*/ |
|
|
/* |
/* |
* Set both track's parameters within a file depending on ai. |
* Set both track's parameters within a file depending on ai. |
* Update sc_sound_[pr]* if set. |
* Update sc_sound_[pr]* if set. |
* Must be called with sc_lock and sc_exlock held. |
* Must be called with sc_exlock held and without sc_lock held. |
*/ |
*/ |
static int |
static int |
audio_file_setinfo(struct audio_softc *sc, audio_file_t *file, |
audio_file_setinfo(struct audio_softc *sc, audio_file_t *file, |
Line 6546 audio_file_setinfo(struct audio_softc *s |
|
Line 6735 audio_file_setinfo(struct audio_softc *s |
|
audio_format2_t saved_rfmt; |
audio_format2_t saved_rfmt; |
int error; |
int error; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
KASSERT(sc->sc_exlock); |
KASSERT(sc->sc_exlock); |
|
|
pi = &ai->play; |
pi = &ai->play; |
Line 6627 audio_file_setinfo(struct audio_softc *s |
|
Line 6815 audio_file_setinfo(struct audio_softc *s |
|
memset(&saved_pfmt, 0, sizeof(saved_pfmt)); |
memset(&saved_pfmt, 0, sizeof(saved_pfmt)); |
memset(&saved_rfmt, 0, sizeof(saved_rfmt)); |
memset(&saved_rfmt, 0, sizeof(saved_rfmt)); |
|
|
/* Set default value and save current parameters */ |
/* |
|
* Set default value and save current parameters. |
|
* For backward compatibility, use sticky parameters for nonexistent |
|
* track. |
|
*/ |
if (ptrack) { |
if (ptrack) { |
pfmt = ptrack->usrbuf.fmt; |
pfmt = ptrack->usrbuf.fmt; |
saved_pfmt = ptrack->usrbuf.fmt; |
saved_pfmt = ptrack->usrbuf.fmt; |
saved_ai.play.pause = ptrack->is_pause; |
saved_ai.play.pause = ptrack->is_pause; |
|
} else { |
|
pfmt = sc->sc_sound_pparams; |
} |
} |
if (rtrack) { |
if (rtrack) { |
rfmt = rtrack->usrbuf.fmt; |
rfmt = rtrack->usrbuf.fmt; |
saved_rfmt = rtrack->usrbuf.fmt; |
saved_rfmt = rtrack->usrbuf.fmt; |
saved_ai.record.pause = rtrack->is_pause; |
saved_ai.record.pause = rtrack->is_pause; |
|
} else { |
|
rfmt = sc->sc_sound_rparams; |
} |
} |
saved_ai.mode = file->mode; |
saved_ai.mode = file->mode; |
|
|
/* Overwrite if specified */ |
/* |
|
* Overwrite if specified. |
|
*/ |
mode = file->mode; |
mode = file->mode; |
if (SPECIFIED(ai->mode)) { |
if (SPECIFIED(ai->mode)) { |
/* |
/* |
Line 6659 audio_file_setinfo(struct audio_softc *s |
|
Line 6857 audio_file_setinfo(struct audio_softc *s |
|
} |
} |
} |
} |
|
|
if (ptrack) { |
pchanges = audio_track_setinfo_check(ptrack, &pfmt, pi); |
pchanges = audio_track_setinfo_check(&pfmt, pi); |
if (pchanges == -1) { |
if (pchanges == -1) { |
|
#if defined(AUDIO_DEBUG) |
#if defined(AUDIO_DEBUG) |
char fmtbuf[64]; |
TRACEF(1, file, "check play.params failed: " |
audio_format2_tostr(fmtbuf, sizeof(fmtbuf), &pfmt); |
"%s %ubit %uch %uHz", |
TRACET(1, ptrack, "check play.params failed: %s", |
audio_encoding_name(pi->encoding), |
fmtbuf); |
pi->precision, |
|
pi->channels, |
|
pi->sample_rate); |
#endif |
#endif |
return EINVAL; |
return EINVAL; |
} |
|
if (SPECIFIED(ai->mode)) |
|
pchanges = 1; |
|
} |
} |
if (rtrack) { |
|
rchanges = audio_track_setinfo_check(&rfmt, ri); |
rchanges = audio_track_setinfo_check(rtrack, &rfmt, ri); |
if (rchanges == -1) { |
if (rchanges == -1) { |
#if defined(AUDIO_DEBUG) |
#if defined(AUDIO_DEBUG) |
char fmtbuf[64]; |
TRACEF(1, file, "check record.params failed: " |
audio_format2_tostr(fmtbuf, sizeof(fmtbuf), &rfmt); |
"%s %ubit %uch %uHz", |
TRACET(1, rtrack, "check record.params failed: %s", |
audio_encoding_name(ri->encoding), |
fmtbuf); |
ri->precision, |
|
ri->channels, |
|
ri->sample_rate); |
#endif |
#endif |
return EINVAL; |
return EINVAL; |
} |
} |
if (SPECIFIED(ai->mode)) |
|
rchanges = 1; |
if (SPECIFIED(ai->mode)) { |
|
pchanges = 1; |
|
rchanges = 1; |
} |
} |
|
|
/* |
/* |
Line 6695 audio_file_setinfo(struct audio_softc *s |
|
Line 6895 audio_file_setinfo(struct audio_softc *s |
|
if (pchanges || rchanges) { |
if (pchanges || rchanges) { |
audio_file_clear(sc, file); |
audio_file_clear(sc, file); |
#if defined(AUDIO_DEBUG) |
#if defined(AUDIO_DEBUG) |
|
char nbuf[16]; |
char fmtbuf[64]; |
char fmtbuf[64]; |
if (pchanges) { |
if (pchanges) { |
|
if (ptrack) { |
|
snprintf(nbuf, sizeof(nbuf), "%d", ptrack->id); |
|
} else { |
|
snprintf(nbuf, sizeof(nbuf), "-"); |
|
} |
audio_format2_tostr(fmtbuf, sizeof(fmtbuf), &pfmt); |
audio_format2_tostr(fmtbuf, sizeof(fmtbuf), &pfmt); |
DPRINTF(1, "audio track#%d play mode: %s\n", |
DPRINTF(1, "audio track#%s play mode: %s\n", |
ptrack->id, fmtbuf); |
nbuf, fmtbuf); |
} |
} |
if (rchanges) { |
if (rchanges) { |
|
if (rtrack) { |
|
snprintf(nbuf, sizeof(nbuf), "%d", rtrack->id); |
|
} else { |
|
snprintf(nbuf, sizeof(nbuf), "-"); |
|
} |
audio_format2_tostr(fmtbuf, sizeof(fmtbuf), &rfmt); |
audio_format2_tostr(fmtbuf, sizeof(fmtbuf), &rfmt); |
DPRINTF(1, "audio track#%d rec mode: %s\n", |
DPRINTF(1, "audio track#%s rec mode: %s\n", |
rtrack->id, fmtbuf); |
nbuf, fmtbuf); |
} |
} |
#endif |
#endif |
} |
} |
|
|
/* Set mixer parameters */ |
/* Set mixer parameters */ |
|
mutex_enter(sc->sc_lock); |
error = audio_hw_setinfo(sc, ai, &saved_ai); |
error = audio_hw_setinfo(sc, ai, &saved_ai); |
|
mutex_exit(sc->sc_lock); |
if (error) |
if (error) |
goto abort1; |
goto abort1; |
|
|
/* Set to track and update sticky parameters */ |
/* |
|
* Set to track and update sticky parameters. |
|
*/ |
error = 0; |
error = 0; |
file->mode = mode; |
file->mode = mode; |
if (ptrack) { |
|
if (SPECIFIED_CH(pi->pause)) { |
if (SPECIFIED_CH(pi->pause)) { |
|
if (ptrack) |
ptrack->is_pause = pi->pause; |
ptrack->is_pause = pi->pause; |
sc->sc_sound_ppause = pi->pause; |
sc->sc_sound_ppause = pi->pause; |
} |
} |
if (pchanges) { |
if (pchanges) { |
|
if (ptrack) { |
audio_track_lock_enter(ptrack); |
audio_track_lock_enter(ptrack); |
error = audio_track_set_format(ptrack, &pfmt); |
error = audio_track_set_format(ptrack, &pfmt); |
audio_track_lock_exit(ptrack); |
audio_track_lock_exit(ptrack); |
Line 6730 audio_file_setinfo(struct audio_softc *s |
|
Line 6947 audio_file_setinfo(struct audio_softc *s |
|
TRACET(1, ptrack, "set play.params failed"); |
TRACET(1, ptrack, "set play.params failed"); |
goto abort2; |
goto abort2; |
} |
} |
sc->sc_sound_pparams = pfmt; |
|
} |
} |
/* Change water marks after initializing the buffers. */ |
sc->sc_sound_pparams = pfmt; |
if (SPECIFIED(ai->hiwat) || SPECIFIED(ai->lowat)) |
} |
|
/* Change water marks after initializing the buffers. */ |
|
if (SPECIFIED(ai->hiwat) || SPECIFIED(ai->lowat)) { |
|
if (ptrack) |
audio_track_setinfo_water(ptrack, ai); |
audio_track_setinfo_water(ptrack, ai); |
} |
} |
if (rtrack) { |
|
if (SPECIFIED_CH(ri->pause)) { |
if (SPECIFIED_CH(ri->pause)) { |
|
if (rtrack) |
rtrack->is_pause = ri->pause; |
rtrack->is_pause = ri->pause; |
sc->sc_sound_rpause = ri->pause; |
sc->sc_sound_rpause = ri->pause; |
} |
} |
if (rchanges) { |
if (rchanges) { |
|
if (rtrack) { |
audio_track_lock_enter(rtrack); |
audio_track_lock_enter(rtrack); |
error = audio_track_set_format(rtrack, &rfmt); |
error = audio_track_set_format(rtrack, &rfmt); |
audio_track_lock_exit(rtrack); |
audio_track_lock_exit(rtrack); |
Line 6749 audio_file_setinfo(struct audio_softc *s |
|
Line 6970 audio_file_setinfo(struct audio_softc *s |
|
TRACET(1, rtrack, "set record.params failed"); |
TRACET(1, rtrack, "set record.params failed"); |
goto abort3; |
goto abort3; |
} |
} |
sc->sc_sound_rparams = rfmt; |
|
} |
} |
|
sc->sc_sound_rparams = rfmt; |
} |
} |
|
|
return 0; |
return 0; |
|
|
audio_track_set_format(rtrack, &saved_rfmt); |
audio_track_set_format(rtrack, &saved_rfmt); |
audio_track_lock_exit(rtrack); |
audio_track_lock_exit(rtrack); |
} |
} |
|
sc->sc_sound_rpause = saved_ai.record.pause; |
|
sc->sc_sound_rparams = saved_rfmt; |
abort2: |
abort2: |
if (ptrack && error != ENOMEM) { |
if (ptrack && error != ENOMEM) { |
ptrack->is_pause = saved_ai.play.pause; |
ptrack->is_pause = saved_ai.play.pause; |
audio_track_lock_enter(ptrack); |
audio_track_lock_enter(ptrack); |
audio_track_set_format(ptrack, &saved_pfmt); |
audio_track_set_format(ptrack, &saved_pfmt); |
audio_track_lock_exit(ptrack); |
audio_track_lock_exit(ptrack); |
sc->sc_sound_pparams = saved_pfmt; |
|
sc->sc_sound_ppause = saved_ai.play.pause; |
|
} |
} |
|
sc->sc_sound_ppause = saved_ai.play.pause; |
|
sc->sc_sound_pparams = saved_pfmt; |
file->mode = saved_ai.mode; |
file->mode = saved_ai.mode; |
abort1: |
abort1: |
|
mutex_enter(sc->sc_lock); |
audio_hw_setinfo(sc, &saved_ai, NULL); |
audio_hw_setinfo(sc, &saved_ai, NULL); |
|
mutex_exit(sc->sc_lock); |
|
|
return error; |
return error; |
} |
} |
|
|
/* |
/* |
* Write SPECIFIED() parameters within info back to fmt. |
* Write SPECIFIED() parameters within info back to fmt. |
|
* Note that track can be NULL here. |
* Return value of 1 indicates that fmt is modified. |
* Return value of 1 indicates that fmt is modified. |
* Return value of 0 indicates that fmt is not modified. |
* Return value of 0 indicates that fmt is not modified. |
* Return value of -1 indicates that error EINVAL has occurred. |
* Return value of -1 indicates that error EINVAL has occurred. |
*/ |
*/ |
static int |
static int |
audio_track_setinfo_check(audio_format2_t *fmt, const struct audio_prinfo *info) |
audio_track_setinfo_check(audio_track_t *track, |
|
audio_format2_t *fmt, const struct audio_prinfo *info) |
{ |
{ |
|
const audio_format2_t *hwfmt; |
int changes; |
int changes; |
|
|
changes = 0; |
changes = 0; |
Line 6810 audio_track_setinfo_check(audio_format2_ |
|
Line 7038 audio_track_setinfo_check(audio_format2_ |
|
changes = 1; |
changes = 1; |
} |
} |
if (SPECIFIED(info->channels)) { |
if (SPECIFIED(info->channels)) { |
|
/* |
|
* We can convert between monaural and stereo each other. |
|
* We can reduce than the number of channels that the hardware |
|
* supports. |
|
*/ |
|
if (info->channels > 2) { |
|
if (track) { |
|
hwfmt = &track->mixer->hwbuf.fmt; |
|
if (info->channels > hwfmt->channels) |
|
return -1; |
|
} else { |
|
/* |
|
* This should never happen. |
|
* If track == NULL, channels should be <= 2. |
|
*/ |
|
return -1; |
|
} |
|
} |
fmt->channels = info->channels; |
fmt->channels = info->channels; |
changes = 1; |
changes = 1; |
} |
} |
Line 6864 audio_track_setinfo_water(audio_track_t |
|
Line 7110 audio_track_setinfo_water(audio_track_t |
|
* The parameters handled here are *.port, *.gain, *.balance and monitor_gain. |
* The parameters handled here are *.port, *.gain, *.balance and monitor_gain. |
* If oldai is specified, previous parameters are stored. |
* If oldai is specified, previous parameters are stored. |
* This function itself does not roll back if error occurred. |
* This function itself does not roll back if error occurred. |
* Must be called with sc_lock and sc_exlock held. |
* Must be called with sc_lock && sc_exlock held. |
*/ |
*/ |
static int |
static int |
audio_hw_setinfo(struct audio_softc *sc, const struct audio_info *newai, |
audio_hw_setinfo(struct audio_softc *sc, const struct audio_info *newai, |
|
|
* - pfil, rfil will be filled with filter information specified by the |
* - pfil, rfil will be filled with filter information specified by the |
* hardware driver. |
* hardware driver. |
* and then returns 0. Otherwise returns errno. |
* and then returns 0. Otherwise returns errno. |
* Must be called with sc_lock held. |
* Must be called without sc_lock held. |
*/ |
*/ |
static int |
static int |
audio_hw_set_format(struct audio_softc *sc, int setmode, |
audio_hw_set_format(struct audio_softc *sc, int setmode, |
Line 7024 audio_hw_set_format(struct audio_softc * |
|
Line 7270 audio_hw_set_format(struct audio_softc * |
|
audio_params_t pp, rp; |
audio_params_t pp, rp; |
int error; |
int error; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
KASSERT(phwfmt != NULL); |
KASSERT(phwfmt != NULL); |
KASSERT(rhwfmt != NULL); |
KASSERT(rhwfmt != NULL); |
|
|
pp = format2_to_params(phwfmt); |
pp = format2_to_params(phwfmt); |
rp = format2_to_params(rhwfmt); |
rp = format2_to_params(rhwfmt); |
|
|
|
mutex_enter(sc->sc_lock); |
error = sc->hw_if->set_format(sc->hw_hdl, setmode, |
error = sc->hw_if->set_format(sc->hw_hdl, setmode, |
&pp, &rp, pfil, rfil); |
&pp, &rp, pfil, rfil); |
if (error) { |
if (error) { |
|
mutex_exit(sc->sc_lock); |
device_printf(sc->sc_dev, |
device_printf(sc->sc_dev, |
"set_format failed with %d\n", error); |
"set_format failed with %d\n", error); |
return error; |
return error; |
Line 7042 audio_hw_set_format(struct audio_softc * |
|
Line 7289 audio_hw_set_format(struct audio_softc * |
|
if (sc->hw_if->commit_settings) { |
if (sc->hw_if->commit_settings) { |
error = sc->hw_if->commit_settings(sc->hw_hdl); |
error = sc->hw_if->commit_settings(sc->hw_hdl); |
if (error) { |
if (error) { |
|
mutex_exit(sc->sc_lock); |
device_printf(sc->sc_dev, |
device_printf(sc->sc_dev, |
"commit_settings failed with %d\n", error); |
"commit_settings failed with %d\n", error); |
return error; |
return error; |
} |
} |
} |
} |
|
mutex_exit(sc->sc_lock); |
|
|
return 0; |
return 0; |
} |
} |
Line 7054 audio_hw_set_format(struct audio_softc * |
|
Line 7303 audio_hw_set_format(struct audio_softc * |
|
/* |
/* |
* Fill audio_info structure. If need_mixerinfo is true, it will also |
* Fill audio_info structure. If need_mixerinfo is true, it will also |
* fill the hardware mixer information. |
* fill the hardware mixer information. |
* Must be called with sc_lock held. |
* Must be called with sc_exlock held and without sc_lock held. |
* Must be called with sc_exlock held, in addition, if need_mixerinfo is |
|
* true. |
|
*/ |
*/ |
static int |
static int |
audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int need_mixerinfo, |
audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int need_mixerinfo, |
Line 7068 audiogetinfo(struct audio_softc *sc, str |
|
Line 7315 audiogetinfo(struct audio_softc *sc, str |
|
audio_track_t *rtrack; |
audio_track_t *rtrack; |
int gain; |
int gain; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock); |
|
|
ri = &ai->record; |
ri = &ai->record; |
pi = &ai->play; |
pi = &ai->play; |
Line 7082 audiogetinfo(struct audio_softc *sc, str |
|
Line 7329 audiogetinfo(struct audio_softc *sc, str |
|
pi->channels = ptrack->usrbuf.fmt.channels; |
pi->channels = ptrack->usrbuf.fmt.channels; |
pi->precision = ptrack->usrbuf.fmt.precision; |
pi->precision = ptrack->usrbuf.fmt.precision; |
pi->encoding = ptrack->usrbuf.fmt.encoding; |
pi->encoding = ptrack->usrbuf.fmt.encoding; |
|
pi->pause = ptrack->is_pause; |
} else { |
} else { |
/* Set default parameters if the track is not available. */ |
/* Use sticky parameters if the track is not available. */ |
if (ISDEVAUDIO(file->dev)) { |
pi->sample_rate = sc->sc_sound_pparams.sample_rate; |
pi->sample_rate = audio_default.sample_rate; |
pi->channels = sc->sc_sound_pparams.channels; |
pi->channels = audio_default.channels; |
pi->precision = sc->sc_sound_pparams.precision; |
pi->precision = audio_default.precision; |
pi->encoding = sc->sc_sound_pparams.encoding; |
pi->encoding = audio_default.encoding; |
pi->pause = sc->sc_sound_ppause; |
} else { |
|
pi->sample_rate = sc->sc_sound_pparams.sample_rate; |
|
pi->channels = sc->sc_sound_pparams.channels; |
|
pi->precision = sc->sc_sound_pparams.precision; |
|
pi->encoding = sc->sc_sound_pparams.encoding; |
|
} |
|
} |
} |
if (rtrack) { |
if (rtrack) { |
ri->sample_rate = rtrack->usrbuf.fmt.sample_rate; |
ri->sample_rate = rtrack->usrbuf.fmt.sample_rate; |
ri->channels = rtrack->usrbuf.fmt.channels; |
ri->channels = rtrack->usrbuf.fmt.channels; |
ri->precision = rtrack->usrbuf.fmt.precision; |
ri->precision = rtrack->usrbuf.fmt.precision; |
ri->encoding = rtrack->usrbuf.fmt.encoding; |
ri->encoding = rtrack->usrbuf.fmt.encoding; |
|
ri->pause = rtrack->is_pause; |
} else { |
} else { |
/* Set default parameters if the track is not available. */ |
/* Use sticky parameters if the track is not available. */ |
if (ISDEVAUDIO(file->dev)) { |
ri->sample_rate = sc->sc_sound_rparams.sample_rate; |
ri->sample_rate = audio_default.sample_rate; |
ri->channels = sc->sc_sound_rparams.channels; |
ri->channels = audio_default.channels; |
ri->precision = sc->sc_sound_rparams.precision; |
ri->precision = audio_default.precision; |
ri->encoding = sc->sc_sound_rparams.encoding; |
ri->encoding = audio_default.encoding; |
ri->pause = sc->sc_sound_rpause; |
} else { |
|
ri->sample_rate = sc->sc_sound_rparams.sample_rate; |
|
ri->channels = sc->sc_sound_rparams.channels; |
|
ri->precision = sc->sc_sound_rparams.precision; |
|
ri->encoding = sc->sc_sound_rparams.encoding; |
|
} |
|
} |
} |
|
|
if (ptrack) { |
if (ptrack) { |
pi->seek = ptrack->usrbuf.used; |
pi->seek = ptrack->usrbuf.used; |
pi->samples = ptrack->usrbuf_stamp; |
pi->samples = ptrack->usrbuf_stamp; |
pi->eof = ptrack->eofcounter; |
pi->eof = ptrack->eofcounter; |
pi->pause = ptrack->is_pause; |
|
pi->error = (ptrack->dropframes != 0) ? 1 : 0; |
pi->error = (ptrack->dropframes != 0) ? 1 : 0; |
pi->waiting = 0; /* open never hangs */ |
|
pi->open = 1; |
pi->open = 1; |
pi->active = sc->sc_pbusy; |
|
pi->buffer_size = ptrack->usrbuf.capacity; |
pi->buffer_size = ptrack->usrbuf.capacity; |
} |
} |
|
pi->waiting = 0; /* open never hangs */ |
|
pi->active = sc->sc_pbusy; |
|
|
if (rtrack) { |
if (rtrack) { |
ri->seek = rtrack->usrbuf.used; |
ri->seek = rtrack->usrbuf.used; |
ri->samples = rtrack->usrbuf_stamp; |
ri->samples = rtrack->usrbuf_stamp; |
ri->eof = 0; |
ri->eof = 0; |
ri->pause = rtrack->is_pause; |
|
ri->error = (rtrack->dropframes != 0) ? 1 : 0; |
ri->error = (rtrack->dropframes != 0) ? 1 : 0; |
ri->waiting = 0; /* open never hangs */ |
|
ri->open = 1; |
ri->open = 1; |
ri->active = sc->sc_rbusy; |
|
ri->buffer_size = rtrack->usrbuf.capacity; |
ri->buffer_size = rtrack->usrbuf.capacity; |
} |
} |
|
ri->waiting = 0; /* open never hangs */ |
|
ri->active = sc->sc_rbusy; |
|
|
/* |
/* |
* XXX There may be different number of channels between playback |
* XXX There may be different number of channels between playback |
Line 7157 audiogetinfo(struct audio_softc *sc, str |
|
Line 7393 audiogetinfo(struct audio_softc *sc, str |
|
} |
} |
ai->mode = file->mode; |
ai->mode = file->mode; |
|
|
|
/* |
|
* For backward compatibility, we have to pad these five fields |
|
* a fake non-zero value even if there are no tracks. |
|
*/ |
|
if (ptrack == NULL) |
|
pi->buffer_size = 65536; |
|
if (rtrack == NULL) |
|
ri->buffer_size = 65536; |
|
if (ptrack == NULL && rtrack == NULL) { |
|
ai->blocksize = 2048; |
|
ai->hiwat = ai->play.buffer_size / ai->blocksize; |
|
ai->lowat = ai->hiwat * 3 / 4; |
|
} |
|
|
if (need_mixerinfo) { |
if (need_mixerinfo) { |
KASSERT(sc->sc_exlock); |
mutex_enter(sc->sc_lock); |
|
|
pi->port = au_get_port(sc, &sc->sc_outports); |
pi->port = au_get_port(sc, &sc->sc_outports); |
ri->port = au_get_port(sc, &sc->sc_inports); |
ri->port = au_get_port(sc, &sc->sc_inports); |
Line 7174 audiogetinfo(struct audio_softc *sc, str |
|
Line 7424 audiogetinfo(struct audio_softc *sc, str |
|
if (gain != -1) |
if (gain != -1) |
ai->monitor_gain = gain; |
ai->monitor_gain = gain; |
} |
} |
|
mutex_exit(sc->sc_lock); |
} |
} |
|
|
return 0; |
return 0; |
Line 7312 audio_sysctl_blk_ms(SYSCTLFN_ARGS) |
|
Line 7563 audio_sysctl_blk_ms(SYSCTLFN_ARGS) |
|
node = *rnode; |
node = *rnode; |
sc = node.sysctl_data; |
sc = node.sysctl_data; |
|
|
mutex_enter(sc->sc_lock); |
error = audio_exlock_enter(sc); |
|
if (error) |
|
return error; |
|
|
old_blk_ms = sc->sc_blk_ms; |
old_blk_ms = sc->sc_blk_ms; |
t = old_blk_ms; |
t = old_blk_ms; |
Line 7359 audio_sysctl_blk_ms(SYSCTLFN_ARGS) |
|
Line 7612 audio_sysctl_blk_ms(SYSCTLFN_ARGS) |
|
} |
} |
error = 0; |
error = 0; |
abort: |
abort: |
mutex_exit(sc->sc_lock); |
audio_exlock_exit(sc); |
return error; |
return error; |
} |
} |
|
|
Line 7377 audio_sysctl_multiuser(SYSCTLFN_ARGS) |
|
Line 7630 audio_sysctl_multiuser(SYSCTLFN_ARGS) |
|
node = *rnode; |
node = *rnode; |
sc = node.sysctl_data; |
sc = node.sysctl_data; |
|
|
mutex_enter(sc->sc_lock); |
error = audio_exlock_enter(sc); |
|
if (error) |
|
return error; |
|
|
t = sc->sc_multiuser; |
t = sc->sc_multiuser; |
node.sysctl_data = &t; |
node.sysctl_data = &t; |
Line 7388 audio_sysctl_multiuser(SYSCTLFN_ARGS) |
|
Line 7643 audio_sysctl_multiuser(SYSCTLFN_ARGS) |
|
sc->sc_multiuser = t; |
sc->sc_multiuser = t; |
error = 0; |
error = 0; |
abort: |
abort: |
mutex_exit(sc->sc_lock); |
audio_exlock_exit(sc); |
return error; |
return error; |
} |
} |
|
|
Line 7468 audio_suspend(device_t dv, const pmf_qua |
|
Line 7723 audio_suspend(device_t dv, const pmf_qua |
|
struct audio_softc *sc = device_private(dv); |
struct audio_softc *sc = device_private(dv); |
int error; |
int error; |
|
|
error = audio_enter_exclusive(sc); |
error = audio_exlock_mutex_enter(sc); |
if (error) |
if (error) |
return error; |
return error; |
audio_mixer_capture(sc); |
audio_mixer_capture(sc); |
Line 7486 audio_suspend(device_t dv, const pmf_qua |
|
Line 7741 audio_suspend(device_t dv, const pmf_qua |
|
#ifdef AUDIO_PM_IDLE |
#ifdef AUDIO_PM_IDLE |
callout_halt(&sc->sc_idle_counter, sc->sc_lock); |
callout_halt(&sc->sc_idle_counter, sc->sc_lock); |
#endif |
#endif |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
|
|
return true; |
return true; |
} |
} |
Line 7498 audio_resume(device_t dv, const pmf_qual |
|
Line 7753 audio_resume(device_t dv, const pmf_qual |
|
struct audio_info ai; |
struct audio_info ai; |
int error; |
int error; |
|
|
error = audio_enter_exclusive(sc); |
error = audio_exlock_mutex_enter(sc); |
if (error) |
if (error) |
return error; |
return error; |
|
|
Line 7512 audio_resume(device_t dv, const pmf_qual |
|
Line 7767 audio_resume(device_t dv, const pmf_qual |
|
if (sc->sc_rbusy) |
if (sc->sc_rbusy) |
audio_rmixer_start(sc); |
audio_rmixer_start(sc); |
|
|
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
|
|
return true; |
return true; |
} |
} |
Line 7551 audio_print_format2(const char *s, const |
|
Line 7806 audio_print_format2(const char *s, const |
|
|
|
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
void |
void |
audio_diagnostic_format2(const char *func, const audio_format2_t *fmt) |
audio_diagnostic_format2(const char *where, const audio_format2_t *fmt) |
{ |
{ |
|
|
KASSERTMSG(fmt, "%s: fmt == NULL", func); |
KASSERTMSG(fmt, "called from %s", where); |
|
|
/* XXX MSM6258 vs(4) only has 4bit stride format. */ |
/* XXX MSM6258 vs(4) only has 4bit stride format. */ |
if (fmt->encoding == AUDIO_ENCODING_ADPCM) { |
if (fmt->encoding == AUDIO_ENCODING_ADPCM) { |
KASSERTMSG(fmt->stride == 4 || fmt->stride == 8, |
KASSERTMSG(fmt->stride == 4 || fmt->stride == 8, |
"%s: stride(%d) is invalid", func, fmt->stride); |
"called from %s: fmt->stride=%d", where, fmt->stride); |
} else { |
} else { |
KASSERTMSG(fmt->stride % NBBY == 0, |
KASSERTMSG(fmt->stride % NBBY == 0, |
"%s: stride(%d) is invalid", func, fmt->stride); |
"called from %s: fmt->stride=%d", where, fmt->stride); |
} |
} |
KASSERTMSG(fmt->precision <= fmt->stride, |
KASSERTMSG(fmt->precision <= fmt->stride, |
"%s: precision(%d) <= stride(%d)", |
"called from %s: fmt->precision=%d fmt->stride=%d", |
func, fmt->precision, fmt->stride); |
where, fmt->precision, fmt->stride); |
KASSERTMSG(1 <= fmt->channels && fmt->channels <= AUDIO_MAX_CHANNELS, |
KASSERTMSG(1 <= fmt->channels && fmt->channels <= AUDIO_MAX_CHANNELS, |
"%s: channels(%d) is out of range", |
"called from %s: fmt->channels=%d", where, fmt->channels); |
func, fmt->channels); |
|
|
|
/* XXX No check for encodings? */ |
/* XXX No check for encodings? */ |
} |
} |
|
|
void |
void |
audio_diagnostic_filter_arg(const char *func, const audio_filter_arg_t *arg) |
audio_diagnostic_filter_arg(const char *where, const audio_filter_arg_t *arg) |
{ |
{ |
|
|
KASSERT(arg != NULL); |
KASSERT(arg != NULL); |
KASSERT(arg->src != NULL); |
KASSERT(arg->src != NULL); |
KASSERT(arg->dst != NULL); |
KASSERT(arg->dst != NULL); |
DIAGNOSTIC_format2(arg->srcfmt); |
audio_diagnostic_format2(where, arg->srcfmt); |
DIAGNOSTIC_format2(arg->dstfmt); |
audio_diagnostic_format2(where, arg->dstfmt); |
KASSERTMSG(arg->count > 0, |
KASSERT(arg->count > 0); |
"%s: count(%d) is out of range", func, arg->count); |
|
} |
} |
|
|
void |
void |
audio_diagnostic_ring(const char *func, const audio_ring_t *ring) |
audio_diagnostic_ring(const char *where, const audio_ring_t *ring) |
{ |
{ |
|
|
KASSERTMSG(ring, "%s: ring == NULL", func); |
KASSERTMSG(ring, "called from %s", where); |
DIAGNOSTIC_format2(&ring->fmt); |
audio_diagnostic_format2(where, &ring->fmt); |
KASSERTMSG(0 <= ring->capacity && ring->capacity < INT_MAX / 2, |
KASSERTMSG(0 <= ring->capacity && ring->capacity < INT_MAX / 2, |
"%s: capacity(%d) is out of range", func, ring->capacity); |
"called from %s: ring->capacity=%d", where, ring->capacity); |
KASSERTMSG(0 <= ring->used && ring->used <= ring->capacity, |
KASSERTMSG(0 <= ring->used && ring->used <= ring->capacity, |
"%s: used(%d) is out of range (capacity:%d)", |
"called from %s: ring->used=%d ring->capacity=%d", |
func, ring->used, ring->capacity); |
where, ring->used, ring->capacity); |
if (ring->capacity == 0) { |
if (ring->capacity == 0) { |
KASSERTMSG(ring->mem == NULL, |
KASSERTMSG(ring->mem == NULL, |
"%s: capacity == 0 but mem != NULL", func); |
"called from %s: capacity == 0 but mem != NULL", where); |
} else { |
} else { |
KASSERTMSG(ring->mem != NULL, |
KASSERTMSG(ring->mem != NULL, |
"%s: capacity != 0 but mem == NULL", func); |
"called from %s: capacity != 0 but mem == NULL", where); |
KASSERTMSG(0 <= ring->head && ring->head < ring->capacity, |
KASSERTMSG(0 <= ring->head && ring->head < ring->capacity, |
"%s: head(%d) is out of range (capacity:%d)", |
"called from %s: ring->head=%d ring->capacity=%d", |
func, ring->head, ring->capacity); |
where, ring->head, ring->capacity); |
} |
} |
} |
} |
#endif /* DIAGNOSTIC */ |
#endif /* DIAGNOSTIC */ |
Line 7615 audio_diagnostic_ring(const char *func, |
|
Line 7868 audio_diagnostic_ring(const char *func, |
|
/* |
/* |
* Mixer driver |
* Mixer driver |
*/ |
*/ |
|
|
|
/* |
|
* Must be called without sc_lock held. |
|
*/ |
int |
int |
mixer_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, |
mixer_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt, |
struct lwp *l) |
struct lwp *l) |
Line 7623 mixer_open(dev_t dev, struct audio_softc |
|
Line 7880 mixer_open(dev_t dev, struct audio_softc |
|
audio_file_t *af; |
audio_file_t *af; |
int error, fd; |
int error, fd; |
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
|
|
|
TRACE(1, "flags=0x%x", flags); |
TRACE(1, "flags=0x%x", flags); |
|
|
error = fd_allocfile(&fp, &fd); |
error = fd_allocfile(&fp, &fd); |
Line 7642 mixer_open(dev_t dev, struct audio_softc |
|
Line 7897 mixer_open(dev_t dev, struct audio_softc |
|
} |
} |
|
|
/* |
/* |
|
* Add a process to those to be signalled on mixer activity. |
|
* If the process has already been added, do nothing. |
|
* Must be called with sc_exlock held and without sc_lock held. |
|
*/ |
|
static void |
|
mixer_async_add(struct audio_softc *sc, pid_t pid) |
|
{ |
|
int i; |
|
|
|
KASSERT(sc->sc_exlock); |
|
|
|
/* If already exists, returns without doing anything. */ |
|
for (i = 0; i < sc->sc_am_used; i++) { |
|
if (sc->sc_am[i] == pid) |
|
return; |
|
} |
|
|
|
/* Extend array if necessary. */ |
|
if (sc->sc_am_used >= sc->sc_am_capacity) { |
|
sc->sc_am_capacity += AM_CAPACITY; |
|
sc->sc_am = kern_realloc(sc->sc_am, |
|
sc->sc_am_capacity * sizeof(pid_t), M_WAITOK); |
|
TRACE(2, "realloc am_capacity=%d", sc->sc_am_capacity); |
|
} |
|
|
|
TRACE(2, "am[%d]=%d", sc->sc_am_used, (int)pid); |
|
sc->sc_am[sc->sc_am_used++] = pid; |
|
} |
|
|
|
/* |
* Remove a process from those to be signalled on mixer activity. |
* Remove a process from those to be signalled on mixer activity. |
* Must be called with sc_lock held. |
* If the process has not been added, do nothing. |
|
* Must be called with sc_exlock held and without sc_lock held. |
*/ |
*/ |
static void |
static void |
mixer_remove(struct audio_softc *sc) |
mixer_async_remove(struct audio_softc *sc, pid_t pid) |
{ |
{ |
struct mixer_asyncs **pm, *m; |
int i; |
pid_t pid; |
|
|
|
KASSERT(mutex_owned(sc->sc_lock)); |
KASSERT(sc->sc_exlock); |
|
|
pid = curproc->p_pid; |
for (i = 0; i < sc->sc_am_used; i++) { |
for (pm = &sc->sc_async_mixer; *pm; pm = &(*pm)->next) { |
if (sc->sc_am[i] == pid) { |
if ((*pm)->pid == pid) { |
sc->sc_am[i] = sc->sc_am[--sc->sc_am_used]; |
m = *pm; |
TRACE(2, "am[%d](%d) removed, used=%d", |
*pm = m->next; |
i, (int)pid, sc->sc_am_used); |
kmem_free(m, sizeof(*m)); |
|
|
/* Empty array if no longer necessary. */ |
|
if (sc->sc_am_used == 0) { |
|
kern_free(sc->sc_am); |
|
sc->sc_am = NULL; |
|
sc->sc_am_capacity = 0; |
|
TRACE(2, "released"); |
|
} |
return; |
return; |
} |
} |
} |
} |
Line 7666 mixer_remove(struct audio_softc *sc) |
|
Line 7958 mixer_remove(struct audio_softc *sc) |
|
|
|
/* |
/* |
* Signal all processes waiting for the mixer. |
* Signal all processes waiting for the mixer. |
* Must be called with sc_lock held. |
* Must be called with sc_exlock held. |
*/ |
*/ |
static void |
static void |
mixer_signal(struct audio_softc *sc) |
mixer_signal(struct audio_softc *sc) |
{ |
{ |
struct mixer_asyncs *m; |
|
proc_t *p; |
proc_t *p; |
|
int i; |
|
|
|
KASSERT(sc->sc_exlock); |
|
|
for (m = sc->sc_async_mixer; m; m = m->next) { |
for (i = 0; i < sc->sc_am_used; i++) { |
mutex_enter(proc_lock); |
mutex_enter(proc_lock); |
if ((p = proc_find(m->pid)) != NULL) |
p = proc_find(sc->sc_am[i]); |
|
if (p) |
psignal(p, SIGIO); |
psignal(p, SIGIO); |
mutex_exit(proc_lock); |
mutex_exit(proc_lock); |
} |
} |
Line 7688 mixer_signal(struct audio_softc *sc) |
|
Line 7983 mixer_signal(struct audio_softc *sc) |
|
int |
int |
mixer_close(struct audio_softc *sc, audio_file_t *file) |
mixer_close(struct audio_softc *sc, audio_file_t *file) |
{ |
{ |
|
int error; |
|
|
mutex_enter(sc->sc_lock); |
error = audio_exlock_enter(sc); |
|
if (error) |
|
return error; |
TRACE(1, ""); |
TRACE(1, ""); |
mixer_remove(sc); |
mixer_async_remove(sc, curproc->p_pid); |
mutex_exit(sc->sc_lock); |
audio_exlock_exit(sc); |
|
|
return 0; |
return 0; |
} |
} |
|
|
|
/* |
|
* Must be called without sc_lock nor sc_exlock held. |
|
*/ |
int |
int |
mixer_ioctl(struct audio_softc *sc, u_long cmd, void *addr, int flag, |
mixer_ioctl(struct audio_softc *sc, u_long cmd, void *addr, int flag, |
struct lwp *l) |
struct lwp *l) |
{ |
{ |
struct mixer_asyncs *ma; |
|
mixer_devinfo_t *mi; |
mixer_devinfo_t *mi; |
mixer_ctrl_t *mc; |
mixer_ctrl_t *mc; |
int error; |
int error; |
|
|
KASSERT(!mutex_owned(sc->sc_lock)); |
|
|
|
TRACE(2, "(%lu,'%c',%lu)", |
TRACE(2, "(%lu,'%c',%lu)", |
IOCPARM_LEN(cmd), (char)IOCGROUP(cmd), cmd & 0xff); |
IOCPARM_LEN(cmd), (char)IOCGROUP(cmd), cmd & 0xff); |
error = EINVAL; |
error = EINVAL; |
Line 7721 mixer_ioctl(struct audio_softc *sc, u_lo |
|
Line 8019 mixer_ioctl(struct audio_softc *sc, u_lo |
|
|
|
switch (cmd) { |
switch (cmd) { |
case FIOASYNC: |
case FIOASYNC: |
|
error = audio_exlock_enter(sc); |
|
if (error) |
|
break; |
if (*(int *)addr) { |
if (*(int *)addr) { |
ma = kmem_alloc(sizeof(struct mixer_asyncs), KM_SLEEP); |
mixer_async_add(sc, curproc->p_pid); |
} else { |
} else { |
ma = NULL; |
mixer_async_remove(sc, curproc->p_pid); |
} |
|
mixer_remove(sc); /* remove old entry */ |
|
if (ma != NULL) { |
|
ma->next = sc->sc_async_mixer; |
|
ma->pid = curproc->p_pid; |
|
sc->sc_async_mixer = ma; |
|
} |
} |
error = 0; |
audio_exlock_exit(sc); |
break; |
break; |
|
|
case AUDIO_GETDEV: |
case AUDIO_GETDEV: |
TRACE(2, "AUDIO_GETDEV"); |
TRACE(2, "AUDIO_GETDEV"); |
error = audio_enter_exclusive(sc); |
mutex_enter(sc->sc_lock); |
if (error) |
|
break; |
|
error = sc->hw_if->getdev(sc->hw_hdl, (audio_device_t *)addr); |
error = sc->hw_if->getdev(sc->hw_hdl, (audio_device_t *)addr); |
audio_exit_exclusive(sc); |
mutex_exit(sc->sc_lock); |
break; |
break; |
|
|
case AUDIO_MIXER_DEVINFO: |
case AUDIO_MIXER_DEVINFO: |
Line 7758 mixer_ioctl(struct audio_softc *sc, u_lo |
|
Line 8051 mixer_ioctl(struct audio_softc *sc, u_lo |
|
TRACE(2, "AUDIO_MIXER_READ"); |
TRACE(2, "AUDIO_MIXER_READ"); |
mc = (mixer_ctrl_t *)addr; |
mc = (mixer_ctrl_t *)addr; |
|
|
error = audio_enter_exclusive(sc); |
error = audio_exlock_mutex_enter(sc); |
if (error) |
if (error) |
break; |
break; |
if (device_is_active(sc->hw_dev)) |
if (device_is_active(sc->hw_dev)) |
Line 7771 mixer_ioctl(struct audio_softc *sc, u_lo |
|
Line 8064 mixer_ioctl(struct audio_softc *sc, u_lo |
|
sizeof(mixer_ctrl_t)); |
sizeof(mixer_ctrl_t)); |
error = 0; |
error = 0; |
} |
} |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
break; |
break; |
|
|
case AUDIO_MIXER_WRITE: |
case AUDIO_MIXER_WRITE: |
TRACE(2, "AUDIO_MIXER_WRITE"); |
TRACE(2, "AUDIO_MIXER_WRITE"); |
error = audio_enter_exclusive(sc); |
error = audio_exlock_mutex_enter(sc); |
if (error) |
if (error) |
break; |
break; |
error = audio_set_port(sc, (mixer_ctrl_t *)addr); |
error = audio_set_port(sc, (mixer_ctrl_t *)addr); |
if (error) { |
if (error) { |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
break; |
break; |
} |
} |
|
|
if (sc->hw_if->commit_settings) { |
if (sc->hw_if->commit_settings) { |
error = sc->hw_if->commit_settings(sc->hw_hdl); |
error = sc->hw_if->commit_settings(sc->hw_hdl); |
if (error) { |
if (error) { |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
break; |
break; |
} |
} |
} |
} |
|
mutex_exit(sc->sc_lock); |
mixer_signal(sc); |
mixer_signal(sc); |
audio_exit_exclusive(sc); |
audio_exlock_exit(sc); |
break; |
break; |
|
|
default: |
default: |
if (sc->hw_if->dev_ioctl) { |
if (sc->hw_if->dev_ioctl) { |
error = audio_enter_exclusive(sc); |
mutex_enter(sc->sc_lock); |
if (error) |
|
break; |
|
error = sc->hw_if->dev_ioctl(sc->hw_hdl, |
error = sc->hw_if->dev_ioctl(sc->hw_hdl, |
cmd, addr, flag, l); |
cmd, addr, flag, l); |
audio_exit_exclusive(sc); |
mutex_exit(sc->sc_lock); |
} else |
} else |
error = EINVAL; |
error = EINVAL; |
break; |
break; |
Line 8324 audio_volume_down(device_t dv) |
|
Line 8616 audio_volume_down(device_t dv) |
|
u_int gain; |
u_int gain; |
u_char balance; |
u_char balance; |
|
|
if (audio_enter_exclusive(sc) != 0) |
if (audio_exlock_mutex_enter(sc) != 0) |
return; |
return; |
if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) { |
if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) { |
mi.index = sc->sc_outports.master; |
mi.index = sc->sc_outports.master; |
Line 8337 audio_volume_down(device_t dv) |
|
Line 8629 audio_volume_down(device_t dv) |
|
au_set_gain(sc, &sc->sc_outports, newgain, balance); |
au_set_gain(sc, &sc->sc_outports, newgain, balance); |
} |
} |
} |
} |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
} |
} |
|
|
static void |
static void |
Line 8348 audio_volume_up(device_t dv) |
|
Line 8640 audio_volume_up(device_t dv) |
|
u_int gain, newgain; |
u_int gain, newgain; |
u_char balance; |
u_char balance; |
|
|
if (audio_enter_exclusive(sc) != 0) |
if (audio_exlock_mutex_enter(sc) != 0) |
return; |
return; |
if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) { |
if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) { |
mi.index = sc->sc_outports.master; |
mi.index = sc->sc_outports.master; |
Line 8361 audio_volume_up(device_t dv) |
|
Line 8653 audio_volume_up(device_t dv) |
|
au_set_gain(sc, &sc->sc_outports, newgain, balance); |
au_set_gain(sc, &sc->sc_outports, newgain, balance); |
} |
} |
} |
} |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
} |
} |
|
|
static void |
static void |
Line 8371 audio_volume_toggle(device_t dv) |
|
Line 8663 audio_volume_toggle(device_t dv) |
|
u_int gain, newgain; |
u_int gain, newgain; |
u_char balance; |
u_char balance; |
|
|
if (audio_enter_exclusive(sc) != 0) |
if (audio_exlock_mutex_enter(sc) != 0) |
return; |
return; |
au_get_gain(sc, &sc->sc_outports, &gain, &balance); |
au_get_gain(sc, &sc->sc_outports, &gain, &balance); |
if (gain != 0) { |
if (gain != 0) { |
Line 8380 audio_volume_toggle(device_t dv) |
|
Line 8672 audio_volume_toggle(device_t dv) |
|
} else |
} else |
newgain = sc->sc_lastgain; |
newgain = sc->sc_lastgain; |
au_set_gain(sc, &sc->sc_outports, newgain, balance); |
au_set_gain(sc, &sc->sc_outports, newgain, balance); |
audio_exit_exclusive(sc); |
audio_exlock_mutex_exit(sc); |
} |
} |
|
|
|
/* |
|
* Must be called with sc_lock held. |
|
*/ |
static int |
static int |
audio_query_devinfo(struct audio_softc *sc, mixer_devinfo_t *di) |
audio_query_devinfo(struct audio_softc *sc, mixer_devinfo_t *di) |
{ |
{ |
Line 8449 audio_modcmd(modcmd_t cmd, void *arg) |
|
Line 8744 audio_modcmd(modcmd_t cmd, void *arg) |
|
{ |
{ |
int error = 0; |
int error = 0; |
|
|
#ifdef _MODULE |
|
switch (cmd) { |
switch (cmd) { |
case MODULE_CMD_INIT: |
case MODULE_CMD_INIT: |
|
/* XXX interrupt level? */ |
|
audio_psref_class = psref_class_create("audio", IPL_SOFTSERIAL); |
|
#ifdef _MODULE |
error = devsw_attach(audio_cd.cd_name, NULL, &audio_bmajor, |
error = devsw_attach(audio_cd.cd_name, NULL, &audio_bmajor, |
&audio_cdevsw, &audio_cmajor); |
&audio_cdevsw, &audio_cmajor); |
if (error) |
if (error) |
Line 8462 audio_modcmd(modcmd_t cmd, void *arg) |
|
Line 8759 audio_modcmd(modcmd_t cmd, void *arg) |
|
if (error) { |
if (error) { |
devsw_detach(NULL, &audio_cdevsw); |
devsw_detach(NULL, &audio_cdevsw); |
} |
} |
|
#endif |
break; |
break; |
case MODULE_CMD_FINI: |
case MODULE_CMD_FINI: |
|
#ifdef _MODULE |
devsw_detach(NULL, &audio_cdevsw); |
devsw_detach(NULL, &audio_cdevsw); |
error = config_fini_component(cfdriver_ioconf_audio, |
error = config_fini_component(cfdriver_ioconf_audio, |
cfattach_ioconf_audio, cfdata_ioconf_audio); |
cfattach_ioconf_audio, cfdata_ioconf_audio); |
if (error) |
if (error) |
devsw_attach(audio_cd.cd_name, NULL, &audio_bmajor, |
devsw_attach(audio_cd.cd_name, NULL, &audio_bmajor, |
&audio_cdevsw, &audio_cmajor); |
&audio_cdevsw, &audio_cmajor); |
|
#endif |
|
psref_class_destroy(audio_psref_class); |
break; |
break; |
default: |
default: |
error = ENOTTY; |
error = ENOTTY; |
break; |
break; |
} |
} |
#endif |
|
|
|
return error; |
return error; |
} |
} |