ALSA: Add a reference counter to card instance
For more strict protection for wild disconnections, a refcount is introduced to the card instance, and let it up/down when an object is referred via snd_lookup_*() in the open ops. The free-after-last-close check is also changed to check this refcount instead of the empty list, too. Reported-by: Matthieu CASTET <matthieu.castet@parrot.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Родитель
888ea7d5ac
Коммит
a0830dbd4e
|
@ -132,6 +132,7 @@ struct snd_card {
|
||||||
int shutdown; /* this card is going down */
|
int shutdown; /* this card is going down */
|
||||||
int free_on_last_close; /* free in context of file_release */
|
int free_on_last_close; /* free in context of file_release */
|
||||||
wait_queue_head_t shutdown_sleep;
|
wait_queue_head_t shutdown_sleep;
|
||||||
|
atomic_t refcount; /* refcount for disconnection */
|
||||||
struct device *dev; /* device assigned to this card */
|
struct device *dev; /* device assigned to this card */
|
||||||
struct device *card_dev; /* cardX object for sysfs */
|
struct device *card_dev; /* cardX object for sysfs */
|
||||||
|
|
||||||
|
@ -189,6 +190,7 @@ struct snd_minor {
|
||||||
const struct file_operations *f_ops; /* file operations */
|
const struct file_operations *f_ops; /* file operations */
|
||||||
void *private_data; /* private data for f_ops->open */
|
void *private_data; /* private data for f_ops->open */
|
||||||
struct device *dev; /* device for sysfs */
|
struct device *dev; /* device for sysfs */
|
||||||
|
struct snd_card *card_ptr; /* assigned card instance */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* return a device pointer linked to each sound device as a parent */
|
/* return a device pointer linked to each sound device as a parent */
|
||||||
|
@ -295,6 +297,7 @@ int snd_card_info_done(void);
|
||||||
int snd_component_add(struct snd_card *card, const char *component);
|
int snd_component_add(struct snd_card *card, const char *component);
|
||||||
int snd_card_file_add(struct snd_card *card, struct file *file);
|
int snd_card_file_add(struct snd_card *card, struct file *file);
|
||||||
int snd_card_file_remove(struct snd_card *card, struct file *file);
|
int snd_card_file_remove(struct snd_card *card, struct file *file);
|
||||||
|
void snd_card_unref(struct snd_card *card);
|
||||||
|
|
||||||
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
|
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
|
||||||
|
|
||||||
|
|
|
@ -100,12 +100,15 @@ static int snd_compr_open(struct inode *inode, struct file *f)
|
||||||
|
|
||||||
if (dirn != compr->direction) {
|
if (dirn != compr->direction) {
|
||||||
pr_err("this device doesn't support this direction\n");
|
pr_err("this device doesn't support this direction\n");
|
||||||
|
snd_card_unref(compr->card);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data) {
|
||||||
|
snd_card_unref(compr->card);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
data->stream.ops = compr->ops;
|
data->stream.ops = compr->ops;
|
||||||
data->stream.direction = dirn;
|
data->stream.direction = dirn;
|
||||||
data->stream.private_data = compr->private_data;
|
data->stream.private_data = compr->private_data;
|
||||||
|
@ -113,6 +116,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
|
||||||
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
|
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
|
||||||
if (!runtime) {
|
if (!runtime) {
|
||||||
kfree(data);
|
kfree(data);
|
||||||
|
snd_card_unref(compr->card);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
runtime->state = SNDRV_PCM_STATE_OPEN;
|
runtime->state = SNDRV_PCM_STATE_OPEN;
|
||||||
|
@ -126,7 +130,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
|
||||||
kfree(runtime);
|
kfree(runtime);
|
||||||
kfree(data);
|
kfree(data);
|
||||||
}
|
}
|
||||||
return ret;
|
snd_card_unref(compr->card);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_compr_free(struct inode *inode, struct file *f)
|
static int snd_compr_free(struct inode *inode, struct file *f)
|
||||||
|
|
|
@ -86,6 +86,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
|
||||||
write_lock_irqsave(&card->ctl_files_rwlock, flags);
|
write_lock_irqsave(&card->ctl_files_rwlock, flags);
|
||||||
list_add_tail(&ctl->list, &card->ctl_files);
|
list_add_tail(&ctl->list, &card->ctl_files);
|
||||||
write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
|
write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
|
||||||
|
snd_card_unref(card);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
__error:
|
__error:
|
||||||
|
@ -93,6 +94,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
|
||||||
__error2:
|
__error2:
|
||||||
snd_card_file_remove(card, file);
|
snd_card_file_remove(card, file);
|
||||||
__error1:
|
__error1:
|
||||||
|
if (card)
|
||||||
|
snd_card_unref(card);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,8 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
|
||||||
if (hw == NULL)
|
if (hw == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!try_module_get(hw->card->module))
|
if (!try_module_get(hw->card->module)) {
|
||||||
|
snd_card_unref(hw->card);
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
init_waitqueue_entry(&wait, current);
|
init_waitqueue_entry(&wait, current);
|
||||||
add_wait_queue(&hw->open_wait, &wait);
|
add_wait_queue(&hw->open_wait, &wait);
|
||||||
|
@ -148,6 +150,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
|
||||||
mutex_unlock(&hw->open_mutex);
|
mutex_unlock(&hw->open_mutex);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
module_put(hw->card->module);
|
module_put(hw->card->module);
|
||||||
|
snd_card_unref(hw->card);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,7 @@ int snd_card_create(int idx, const char *xid,
|
||||||
spin_lock_init(&card->files_lock);
|
spin_lock_init(&card->files_lock);
|
||||||
INIT_LIST_HEAD(&card->files_list);
|
INIT_LIST_HEAD(&card->files_list);
|
||||||
init_waitqueue_head(&card->shutdown_sleep);
|
init_waitqueue_head(&card->shutdown_sleep);
|
||||||
|
atomic_set(&card->refcount, 0);
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
mutex_init(&card->power_lock);
|
mutex_init(&card->power_lock);
|
||||||
init_waitqueue_head(&card->power_sleep);
|
init_waitqueue_head(&card->power_sleep);
|
||||||
|
@ -446,21 +447,36 @@ static int snd_card_do_free(struct snd_card *card)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* snd_card_unref - release the reference counter
|
||||||
|
* @card: the card instance
|
||||||
|
*
|
||||||
|
* Decrements the reference counter. When it reaches to zero, wake up
|
||||||
|
* the sleeper and call the destructor if needed.
|
||||||
|
*/
|
||||||
|
void snd_card_unref(struct snd_card *card)
|
||||||
|
{
|
||||||
|
if (atomic_dec_and_test(&card->refcount)) {
|
||||||
|
wake_up(&card->shutdown_sleep);
|
||||||
|
if (card->free_on_last_close)
|
||||||
|
snd_card_do_free(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(snd_card_unref);
|
||||||
|
|
||||||
int snd_card_free_when_closed(struct snd_card *card)
|
int snd_card_free_when_closed(struct snd_card *card)
|
||||||
{
|
{
|
||||||
int free_now = 0;
|
int ret;
|
||||||
int ret = snd_card_disconnect(card);
|
|
||||||
if (ret)
|
atomic_inc(&card->refcount);
|
||||||
|
ret = snd_card_disconnect(card);
|
||||||
|
if (ret) {
|
||||||
|
atomic_dec(&card->refcount);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock(&card->files_lock);
|
|
||||||
if (list_empty(&card->files_list))
|
|
||||||
free_now = 1;
|
|
||||||
else
|
|
||||||
card->free_on_last_close = 1;
|
card->free_on_last_close = 1;
|
||||||
spin_unlock(&card->files_lock);
|
if (atomic_dec_and_test(&card->refcount))
|
||||||
|
|
||||||
if (free_now)
|
|
||||||
snd_card_do_free(card);
|
snd_card_do_free(card);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -474,7 +490,7 @@ int snd_card_free(struct snd_card *card)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* wait, until all devices are ready for the free operation */
|
/* wait, until all devices are ready for the free operation */
|
||||||
wait_event(card->shutdown_sleep, list_empty(&card->files_list));
|
wait_event(card->shutdown_sleep, !atomic_read(&card->refcount));
|
||||||
snd_card_do_free(card);
|
snd_card_do_free(card);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -886,6 +902,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
list_add(&mfile->list, &card->files_list);
|
list_add(&mfile->list, &card->files_list);
|
||||||
|
atomic_inc(&card->refcount);
|
||||||
spin_unlock(&card->files_lock);
|
spin_unlock(&card->files_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -908,7 +925,6 @@ EXPORT_SYMBOL(snd_card_file_add);
|
||||||
int snd_card_file_remove(struct snd_card *card, struct file *file)
|
int snd_card_file_remove(struct snd_card *card, struct file *file)
|
||||||
{
|
{
|
||||||
struct snd_monitor_file *mfile, *found = NULL;
|
struct snd_monitor_file *mfile, *found = NULL;
|
||||||
int last_close = 0;
|
|
||||||
|
|
||||||
spin_lock(&card->files_lock);
|
spin_lock(&card->files_lock);
|
||||||
list_for_each_entry(mfile, &card->files_list, list) {
|
list_for_each_entry(mfile, &card->files_list, list) {
|
||||||
|
@ -923,19 +939,13 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (list_empty(&card->files_list))
|
|
||||||
last_close = 1;
|
|
||||||
spin_unlock(&card->files_lock);
|
spin_unlock(&card->files_lock);
|
||||||
if (last_close) {
|
|
||||||
wake_up(&card->shutdown_sleep);
|
|
||||||
if (card->free_on_last_close)
|
|
||||||
snd_card_do_free(card);
|
|
||||||
}
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
|
snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
kfree(found);
|
kfree(found);
|
||||||
|
snd_card_unref(card);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,14 +52,19 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
|
||||||
SNDRV_OSS_DEVICE_TYPE_MIXER);
|
SNDRV_OSS_DEVICE_TYPE_MIXER);
|
||||||
if (card == NULL)
|
if (card == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
if (card->mixer_oss == NULL)
|
if (card->mixer_oss == NULL) {
|
||||||
|
snd_card_unref(card);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
}
|
||||||
err = snd_card_file_add(card, file);
|
err = snd_card_file_add(card, file);
|
||||||
if (err < 0)
|
if (err < 0) {
|
||||||
|
snd_card_unref(card);
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
|
fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
|
||||||
if (fmixer == NULL) {
|
if (fmixer == NULL) {
|
||||||
snd_card_file_remove(card, file);
|
snd_card_file_remove(card, file);
|
||||||
|
snd_card_unref(card);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
fmixer->card = card;
|
fmixer->card = card;
|
||||||
|
@ -68,6 +73,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
|
||||||
if (!try_module_get(card->module)) {
|
if (!try_module_get(card->module)) {
|
||||||
kfree(fmixer);
|
kfree(fmixer);
|
||||||
snd_card_file_remove(card, file);
|
snd_card_file_remove(card, file);
|
||||||
|
snd_card_unref(card);
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -2457,6 +2457,8 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
|
||||||
__error2:
|
__error2:
|
||||||
snd_card_file_remove(pcm->card, file);
|
snd_card_file_remove(pcm->card, file);
|
||||||
__error1:
|
__error1:
|
||||||
|
if (pcm)
|
||||||
|
snd_card_unref(pcm->card);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1642,6 +1642,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
|
||||||
write_unlock_irq(&snd_pcm_link_rwlock);
|
write_unlock_irq(&snd_pcm_link_rwlock);
|
||||||
up_write(&snd_pcm_link_rwsem);
|
up_write(&snd_pcm_link_rwsem);
|
||||||
_nolock:
|
_nolock:
|
||||||
|
snd_card_unref(substream1->pcm->card);
|
||||||
fput_light(file, fput_needed);
|
fput_light(file, fput_needed);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
kfree(group);
|
kfree(group);
|
||||||
|
@ -2116,7 +2117,9 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file)
|
||||||
return err;
|
return err;
|
||||||
pcm = snd_lookup_minor_data(iminor(inode),
|
pcm = snd_lookup_minor_data(iminor(inode),
|
||||||
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
|
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
|
||||||
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
|
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
|
||||||
|
snd_card_unref(pcm->card);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
|
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
|
||||||
|
@ -2127,7 +2130,9 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file)
|
||||||
return err;
|
return err;
|
||||||
pcm = snd_lookup_minor_data(iminor(inode),
|
pcm = snd_lookup_minor_data(iminor(inode),
|
||||||
SNDRV_DEVICE_TYPE_PCM_CAPTURE);
|
SNDRV_DEVICE_TYPE_PCM_CAPTURE);
|
||||||
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
|
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
|
||||||
|
snd_card_unref(pcm->card);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
|
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
|
||||||
|
|
|
@ -379,8 +379,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
|
||||||
if (rmidi == NULL)
|
if (rmidi == NULL)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!try_module_get(rmidi->card->module))
|
if (!try_module_get(rmidi->card->module)) {
|
||||||
|
snd_card_unref(rmidi->card);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&rmidi->open_mutex);
|
mutex_lock(&rmidi->open_mutex);
|
||||||
card = rmidi->card;
|
card = rmidi->card;
|
||||||
|
@ -440,6 +442,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
|
||||||
#endif
|
#endif
|
||||||
file->private_data = rawmidi_file;
|
file->private_data = rawmidi_file;
|
||||||
mutex_unlock(&rmidi->open_mutex);
|
mutex_unlock(&rmidi->open_mutex);
|
||||||
|
snd_card_unref(rmidi->card);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
__error:
|
__error:
|
||||||
|
@ -447,6 +450,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
|
||||||
__error_card:
|
__error_card:
|
||||||
mutex_unlock(&rmidi->open_mutex);
|
mutex_unlock(&rmidi->open_mutex);
|
||||||
module_put(rmidi->card->module);
|
module_put(rmidi->card->module);
|
||||||
|
snd_card_unref(rmidi->card);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,10 @@ static void snd_request_other(int minor)
|
||||||
*
|
*
|
||||||
* Checks that a minor device with the specified type is registered, and returns
|
* Checks that a minor device with the specified type is registered, and returns
|
||||||
* its user data pointer.
|
* its user data pointer.
|
||||||
|
*
|
||||||
|
* This function increments the reference counter of the card instance
|
||||||
|
* if an associated instance with the given minor number and type is found.
|
||||||
|
* The caller must call snd_card_unref() appropriately later.
|
||||||
*/
|
*/
|
||||||
void *snd_lookup_minor_data(unsigned int minor, int type)
|
void *snd_lookup_minor_data(unsigned int minor, int type)
|
||||||
{
|
{
|
||||||
|
@ -108,9 +112,11 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
|
||||||
return NULL;
|
return NULL;
|
||||||
mutex_lock(&sound_mutex);
|
mutex_lock(&sound_mutex);
|
||||||
mreg = snd_minors[minor];
|
mreg = snd_minors[minor];
|
||||||
if (mreg && mreg->type == type)
|
if (mreg && mreg->type == type) {
|
||||||
private_data = mreg->private_data;
|
private_data = mreg->private_data;
|
||||||
else
|
if (mreg->card_ptr)
|
||||||
|
atomic_inc(&mreg->card_ptr->refcount);
|
||||||
|
} else
|
||||||
private_data = NULL;
|
private_data = NULL;
|
||||||
mutex_unlock(&sound_mutex);
|
mutex_unlock(&sound_mutex);
|
||||||
return private_data;
|
return private_data;
|
||||||
|
@ -275,6 +281,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
|
||||||
preg->device = dev;
|
preg->device = dev;
|
||||||
preg->f_ops = f_ops;
|
preg->f_ops = f_ops;
|
||||||
preg->private_data = private_data;
|
preg->private_data = private_data;
|
||||||
|
preg->card_ptr = card;
|
||||||
mutex_lock(&sound_mutex);
|
mutex_lock(&sound_mutex);
|
||||||
#ifdef CONFIG_SND_DYNAMIC_MINORS
|
#ifdef CONFIG_SND_DYNAMIC_MINORS
|
||||||
minor = snd_find_free_minor(type);
|
minor = snd_find_free_minor(type);
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
|
static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
|
||||||
static DEFINE_MUTEX(sound_oss_mutex);
|
static DEFINE_MUTEX(sound_oss_mutex);
|
||||||
|
|
||||||
|
/* NOTE: This function increments the refcount of the associated card like
|
||||||
|
* snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately
|
||||||
|
*/
|
||||||
void *snd_lookup_oss_minor_data(unsigned int minor, int type)
|
void *snd_lookup_oss_minor_data(unsigned int minor, int type)
|
||||||
{
|
{
|
||||||
struct snd_minor *mreg;
|
struct snd_minor *mreg;
|
||||||
|
@ -49,9 +52,11 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type)
|
||||||
return NULL;
|
return NULL;
|
||||||
mutex_lock(&sound_oss_mutex);
|
mutex_lock(&sound_oss_mutex);
|
||||||
mreg = snd_oss_minors[minor];
|
mreg = snd_oss_minors[minor];
|
||||||
if (mreg && mreg->type == type)
|
if (mreg && mreg->type == type) {
|
||||||
private_data = mreg->private_data;
|
private_data = mreg->private_data;
|
||||||
else
|
if (mreg->card_ptr)
|
||||||
|
atomic_inc(&mreg->card_ptr->refcount);
|
||||||
|
} else
|
||||||
private_data = NULL;
|
private_data = NULL;
|
||||||
mutex_unlock(&sound_oss_mutex);
|
mutex_unlock(&sound_oss_mutex);
|
||||||
return private_data;
|
return private_data;
|
||||||
|
@ -123,6 +128,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
|
||||||
preg->device = dev;
|
preg->device = dev;
|
||||||
preg->f_ops = f_ops;
|
preg->f_ops = f_ops;
|
||||||
preg->private_data = private_data;
|
preg->private_data = private_data;
|
||||||
|
preg->card_ptr = card;
|
||||||
mutex_lock(&sound_oss_mutex);
|
mutex_lock(&sound_oss_mutex);
|
||||||
snd_oss_minors[minor] = preg;
|
snd_oss_minors[minor] = preg;
|
||||||
minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
|
minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче