162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hardware dependent layer 462306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/major.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/time.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/sched/signal.h> 1462306a36Sopenharmony_ci#include <sound/core.h> 1562306a36Sopenharmony_ci#include <sound/control.h> 1662306a36Sopenharmony_ci#include <sound/minors.h> 1762306a36Sopenharmony_ci#include <sound/hwdep.h> 1862306a36Sopenharmony_ci#include <sound/info.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 2162306a36Sopenharmony_ciMODULE_DESCRIPTION("Hardware dependent layer"); 2262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic LIST_HEAD(snd_hwdep_devices); 2562306a36Sopenharmony_cistatic DEFINE_MUTEX(register_mutex); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int snd_hwdep_dev_free(struct snd_device *device); 2862306a36Sopenharmony_cistatic int snd_hwdep_dev_register(struct snd_device *device); 2962306a36Sopenharmony_cistatic int snd_hwdep_dev_disconnect(struct snd_device *device); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct snd_hwdep *hwdep; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci list_for_each_entry(hwdep, &snd_hwdep_devices, list) 3762306a36Sopenharmony_ci if (hwdep->card == card && hwdep->device == device) 3862306a36Sopenharmony_ci return hwdep; 3962306a36Sopenharmony_ci return NULL; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 4562306a36Sopenharmony_ci if (hw->ops.llseek) 4662306a36Sopenharmony_ci return hw->ops.llseek(hw, file, offset, orig); 4762306a36Sopenharmony_ci return -ENXIO; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic ssize_t snd_hwdep_read(struct file * file, char __user *buf, 5162306a36Sopenharmony_ci size_t count, loff_t *offset) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 5462306a36Sopenharmony_ci if (hw->ops.read) 5562306a36Sopenharmony_ci return hw->ops.read(hw, buf, count, offset); 5662306a36Sopenharmony_ci return -ENXIO; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic ssize_t snd_hwdep_write(struct file * file, const char __user *buf, 6062306a36Sopenharmony_ci size_t count, loff_t *offset) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 6362306a36Sopenharmony_ci if (hw->ops.write) 6462306a36Sopenharmony_ci return hw->ops.write(hw, buf, count, offset); 6562306a36Sopenharmony_ci return -ENXIO; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int snd_hwdep_open(struct inode *inode, struct file * file) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int major = imajor(inode); 7162306a36Sopenharmony_ci struct snd_hwdep *hw; 7262306a36Sopenharmony_ci int err; 7362306a36Sopenharmony_ci wait_queue_entry_t wait; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (major == snd_major) { 7662306a36Sopenharmony_ci hw = snd_lookup_minor_data(iminor(inode), 7762306a36Sopenharmony_ci SNDRV_DEVICE_TYPE_HWDEP); 7862306a36Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 7962306a36Sopenharmony_ci } else if (major == SOUND_MAJOR) { 8062306a36Sopenharmony_ci hw = snd_lookup_oss_minor_data(iminor(inode), 8162306a36Sopenharmony_ci SNDRV_OSS_DEVICE_TYPE_DMFM); 8262306a36Sopenharmony_ci#endif 8362306a36Sopenharmony_ci } else 8462306a36Sopenharmony_ci return -ENXIO; 8562306a36Sopenharmony_ci if (hw == NULL) 8662306a36Sopenharmony_ci return -ENODEV; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!try_module_get(hw->card->module)) { 8962306a36Sopenharmony_ci snd_card_unref(hw->card); 9062306a36Sopenharmony_ci return -EFAULT; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci init_waitqueue_entry(&wait, current); 9462306a36Sopenharmony_ci add_wait_queue(&hw->open_wait, &wait); 9562306a36Sopenharmony_ci mutex_lock(&hw->open_mutex); 9662306a36Sopenharmony_ci while (1) { 9762306a36Sopenharmony_ci if (hw->exclusive && hw->used > 0) { 9862306a36Sopenharmony_ci err = -EBUSY; 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (!hw->ops.open) { 10262306a36Sopenharmony_ci err = 0; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci err = hw->ops.open(hw, file); 10662306a36Sopenharmony_ci if (err >= 0) 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci if (err == -EAGAIN) { 10962306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 11062306a36Sopenharmony_ci err = -EBUSY; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci } else 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 11662306a36Sopenharmony_ci mutex_unlock(&hw->open_mutex); 11762306a36Sopenharmony_ci schedule(); 11862306a36Sopenharmony_ci mutex_lock(&hw->open_mutex); 11962306a36Sopenharmony_ci if (hw->card->shutdown) { 12062306a36Sopenharmony_ci err = -ENODEV; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci if (signal_pending(current)) { 12462306a36Sopenharmony_ci err = -ERESTARTSYS; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci remove_wait_queue(&hw->open_wait, &wait); 12962306a36Sopenharmony_ci if (err >= 0) { 13062306a36Sopenharmony_ci err = snd_card_file_add(hw->card, file); 13162306a36Sopenharmony_ci if (err >= 0) { 13262306a36Sopenharmony_ci file->private_data = hw; 13362306a36Sopenharmony_ci hw->used++; 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci if (hw->ops.release) 13662306a36Sopenharmony_ci hw->ops.release(hw, file); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci mutex_unlock(&hw->open_mutex); 14062306a36Sopenharmony_ci if (err < 0) 14162306a36Sopenharmony_ci module_put(hw->card->module); 14262306a36Sopenharmony_ci snd_card_unref(hw->card); 14362306a36Sopenharmony_ci return err; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int snd_hwdep_release(struct inode *inode, struct file * file) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int err = 0; 14962306a36Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 15062306a36Sopenharmony_ci struct module *mod = hw->card->module; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci mutex_lock(&hw->open_mutex); 15362306a36Sopenharmony_ci if (hw->ops.release) 15462306a36Sopenharmony_ci err = hw->ops.release(hw, file); 15562306a36Sopenharmony_ci if (hw->used > 0) 15662306a36Sopenharmony_ci hw->used--; 15762306a36Sopenharmony_ci mutex_unlock(&hw->open_mutex); 15862306a36Sopenharmony_ci wake_up(&hw->open_wait); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci snd_card_file_remove(hw->card, file); 16162306a36Sopenharmony_ci module_put(mod); 16262306a36Sopenharmony_ci return err; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic __poll_t snd_hwdep_poll(struct file * file, poll_table * wait) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 16862306a36Sopenharmony_ci if (hw->ops.poll) 16962306a36Sopenharmony_ci return hw->ops.poll(hw, file, wait); 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int snd_hwdep_info(struct snd_hwdep *hw, 17462306a36Sopenharmony_ci struct snd_hwdep_info __user *_info) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct snd_hwdep_info info; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 17962306a36Sopenharmony_ci info.card = hw->card->number; 18062306a36Sopenharmony_ci strscpy(info.id, hw->id, sizeof(info.id)); 18162306a36Sopenharmony_ci strscpy(info.name, hw->name, sizeof(info.name)); 18262306a36Sopenharmony_ci info.iface = hw->iface; 18362306a36Sopenharmony_ci if (copy_to_user(_info, &info, sizeof(info))) 18462306a36Sopenharmony_ci return -EFAULT; 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int snd_hwdep_dsp_status(struct snd_hwdep *hw, 18962306a36Sopenharmony_ci struct snd_hwdep_dsp_status __user *_info) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct snd_hwdep_dsp_status info; 19262306a36Sopenharmony_ci int err; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (! hw->ops.dsp_status) 19562306a36Sopenharmony_ci return -ENXIO; 19662306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 19762306a36Sopenharmony_ci info.dsp_loaded = hw->dsp_loaded; 19862306a36Sopenharmony_ci err = hw->ops.dsp_status(hw, &info); 19962306a36Sopenharmony_ci if (err < 0) 20062306a36Sopenharmony_ci return err; 20162306a36Sopenharmony_ci if (copy_to_user(_info, &info, sizeof(info))) 20262306a36Sopenharmony_ci return -EFAULT; 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int snd_hwdep_dsp_load(struct snd_hwdep *hw, 20762306a36Sopenharmony_ci struct snd_hwdep_dsp_image *info) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int err; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (! hw->ops.dsp_load) 21262306a36Sopenharmony_ci return -ENXIO; 21362306a36Sopenharmony_ci if (info->index >= 32) 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci /* check whether the dsp was already loaded */ 21662306a36Sopenharmony_ci if (hw->dsp_loaded & (1u << info->index)) 21762306a36Sopenharmony_ci return -EBUSY; 21862306a36Sopenharmony_ci err = hw->ops.dsp_load(hw, info); 21962306a36Sopenharmony_ci if (err < 0) 22062306a36Sopenharmony_ci return err; 22162306a36Sopenharmony_ci hw->dsp_loaded |= (1u << info->index); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int snd_hwdep_dsp_load_user(struct snd_hwdep *hw, 22662306a36Sopenharmony_ci struct snd_hwdep_dsp_image __user *_info) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct snd_hwdep_dsp_image info = {}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (copy_from_user(&info, _info, sizeof(info))) 23162306a36Sopenharmony_ci return -EFAULT; 23262306a36Sopenharmony_ci return snd_hwdep_dsp_load(hw, &info); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic long snd_hwdep_ioctl(struct file * file, unsigned int cmd, 23762306a36Sopenharmony_ci unsigned long arg) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 24062306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 24162306a36Sopenharmony_ci switch (cmd) { 24262306a36Sopenharmony_ci case SNDRV_HWDEP_IOCTL_PVERSION: 24362306a36Sopenharmony_ci return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp); 24462306a36Sopenharmony_ci case SNDRV_HWDEP_IOCTL_INFO: 24562306a36Sopenharmony_ci return snd_hwdep_info(hw, argp); 24662306a36Sopenharmony_ci case SNDRV_HWDEP_IOCTL_DSP_STATUS: 24762306a36Sopenharmony_ci return snd_hwdep_dsp_status(hw, argp); 24862306a36Sopenharmony_ci case SNDRV_HWDEP_IOCTL_DSP_LOAD: 24962306a36Sopenharmony_ci return snd_hwdep_dsp_load_user(hw, argp); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci if (hw->ops.ioctl) 25262306a36Sopenharmony_ci return hw->ops.ioctl(hw, file, cmd, arg); 25362306a36Sopenharmony_ci return -ENOTTY; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 25962306a36Sopenharmony_ci if (hw->ops.mmap) 26062306a36Sopenharmony_ci return hw->ops.mmap(hw, file, vma); 26162306a36Sopenharmony_ci return -ENXIO; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int snd_hwdep_control_ioctl(struct snd_card *card, 26562306a36Sopenharmony_ci struct snd_ctl_file * control, 26662306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci switch (cmd) { 26962306a36Sopenharmony_ci case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE: 27062306a36Sopenharmony_ci { 27162306a36Sopenharmony_ci int device; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (get_user(device, (int __user *)arg)) 27462306a36Sopenharmony_ci return -EFAULT; 27562306a36Sopenharmony_ci mutex_lock(®ister_mutex); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (device < 0) 27862306a36Sopenharmony_ci device = 0; 27962306a36Sopenharmony_ci else if (device < SNDRV_MINOR_HWDEPS) 28062306a36Sopenharmony_ci device++; 28162306a36Sopenharmony_ci else 28262306a36Sopenharmony_ci device = SNDRV_MINOR_HWDEPS; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci while (device < SNDRV_MINOR_HWDEPS) { 28562306a36Sopenharmony_ci if (snd_hwdep_search(card, device)) 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci device++; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci if (device >= SNDRV_MINOR_HWDEPS) 29062306a36Sopenharmony_ci device = -1; 29162306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 29262306a36Sopenharmony_ci if (put_user(device, (int __user *)arg)) 29362306a36Sopenharmony_ci return -EFAULT; 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci case SNDRV_CTL_IOCTL_HWDEP_INFO: 29762306a36Sopenharmony_ci { 29862306a36Sopenharmony_ci struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg; 29962306a36Sopenharmony_ci int device, err; 30062306a36Sopenharmony_ci struct snd_hwdep *hwdep; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (get_user(device, &info->device)) 30362306a36Sopenharmony_ci return -EFAULT; 30462306a36Sopenharmony_ci mutex_lock(®ister_mutex); 30562306a36Sopenharmony_ci hwdep = snd_hwdep_search(card, device); 30662306a36Sopenharmony_ci if (hwdep) 30762306a36Sopenharmony_ci err = snd_hwdep_info(hwdep, info); 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci err = -ENXIO; 31062306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 31162306a36Sopenharmony_ci return err; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci return -ENOIOCTLCMD; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 31862306a36Sopenharmony_ci#include "hwdep_compat.c" 31962306a36Sopenharmony_ci#else 32062306a36Sopenharmony_ci#define snd_hwdep_ioctl_compat NULL 32162306a36Sopenharmony_ci#endif 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/* 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic const struct file_operations snd_hwdep_f_ops = 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci .owner = THIS_MODULE, 33062306a36Sopenharmony_ci .llseek = snd_hwdep_llseek, 33162306a36Sopenharmony_ci .read = snd_hwdep_read, 33262306a36Sopenharmony_ci .write = snd_hwdep_write, 33362306a36Sopenharmony_ci .open = snd_hwdep_open, 33462306a36Sopenharmony_ci .release = snd_hwdep_release, 33562306a36Sopenharmony_ci .poll = snd_hwdep_poll, 33662306a36Sopenharmony_ci .unlocked_ioctl = snd_hwdep_ioctl, 33762306a36Sopenharmony_ci .compat_ioctl = snd_hwdep_ioctl_compat, 33862306a36Sopenharmony_ci .mmap = snd_hwdep_mmap, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void snd_hwdep_free(struct snd_hwdep *hwdep) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci if (!hwdep) 34462306a36Sopenharmony_ci return; 34562306a36Sopenharmony_ci if (hwdep->private_free) 34662306a36Sopenharmony_ci hwdep->private_free(hwdep); 34762306a36Sopenharmony_ci put_device(hwdep->dev); 34862306a36Sopenharmony_ci kfree(hwdep); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/** 35262306a36Sopenharmony_ci * snd_hwdep_new - create a new hwdep instance 35362306a36Sopenharmony_ci * @card: the card instance 35462306a36Sopenharmony_ci * @id: the id string 35562306a36Sopenharmony_ci * @device: the device index (zero-based) 35662306a36Sopenharmony_ci * @rhwdep: the pointer to store the new hwdep instance 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * Creates a new hwdep instance with the given index on the card. 35962306a36Sopenharmony_ci * The callbacks (hwdep->ops) must be set on the returned instance 36062306a36Sopenharmony_ci * after this call manually by the caller. 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ciint snd_hwdep_new(struct snd_card *card, char *id, int device, 36562306a36Sopenharmony_ci struct snd_hwdep **rhwdep) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct snd_hwdep *hwdep; 36862306a36Sopenharmony_ci int err; 36962306a36Sopenharmony_ci static const struct snd_device_ops ops = { 37062306a36Sopenharmony_ci .dev_free = snd_hwdep_dev_free, 37162306a36Sopenharmony_ci .dev_register = snd_hwdep_dev_register, 37262306a36Sopenharmony_ci .dev_disconnect = snd_hwdep_dev_disconnect, 37362306a36Sopenharmony_ci }; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 37662306a36Sopenharmony_ci return -ENXIO; 37762306a36Sopenharmony_ci if (rhwdep) 37862306a36Sopenharmony_ci *rhwdep = NULL; 37962306a36Sopenharmony_ci hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL); 38062306a36Sopenharmony_ci if (!hwdep) 38162306a36Sopenharmony_ci return -ENOMEM; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci init_waitqueue_head(&hwdep->open_wait); 38462306a36Sopenharmony_ci mutex_init(&hwdep->open_mutex); 38562306a36Sopenharmony_ci hwdep->card = card; 38662306a36Sopenharmony_ci hwdep->device = device; 38762306a36Sopenharmony_ci if (id) 38862306a36Sopenharmony_ci strscpy(hwdep->id, id, sizeof(hwdep->id)); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci err = snd_device_alloc(&hwdep->dev, card); 39162306a36Sopenharmony_ci if (err < 0) { 39262306a36Sopenharmony_ci snd_hwdep_free(hwdep); 39362306a36Sopenharmony_ci return err; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dev_set_name(hwdep->dev, "hwC%iD%i", card->number, device); 39762306a36Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 39862306a36Sopenharmony_ci hwdep->oss_type = -1; 39962306a36Sopenharmony_ci#endif 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops); 40262306a36Sopenharmony_ci if (err < 0) { 40362306a36Sopenharmony_ci snd_hwdep_free(hwdep); 40462306a36Sopenharmony_ci return err; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (rhwdep) 40862306a36Sopenharmony_ci *rhwdep = hwdep; 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_hwdep_new); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int snd_hwdep_dev_free(struct snd_device *device) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci snd_hwdep_free(device->device_data); 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int snd_hwdep_dev_register(struct snd_device *device) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct snd_hwdep *hwdep = device->device_data; 42262306a36Sopenharmony_ci struct snd_card *card = hwdep->card; 42362306a36Sopenharmony_ci int err; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci mutex_lock(®ister_mutex); 42662306a36Sopenharmony_ci if (snd_hwdep_search(card, hwdep->device)) { 42762306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 42862306a36Sopenharmony_ci return -EBUSY; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci list_add_tail(&hwdep->list, &snd_hwdep_devices); 43162306a36Sopenharmony_ci err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, 43262306a36Sopenharmony_ci hwdep->card, hwdep->device, 43362306a36Sopenharmony_ci &snd_hwdep_f_ops, hwdep, hwdep->dev); 43462306a36Sopenharmony_ci if (err < 0) { 43562306a36Sopenharmony_ci dev_err(hwdep->dev, "unable to register\n"); 43662306a36Sopenharmony_ci list_del(&hwdep->list); 43762306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 43862306a36Sopenharmony_ci return err; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 44262306a36Sopenharmony_ci hwdep->ossreg = 0; 44362306a36Sopenharmony_ci if (hwdep->oss_type >= 0) { 44462306a36Sopenharmony_ci if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM && 44562306a36Sopenharmony_ci hwdep->device) 44662306a36Sopenharmony_ci dev_warn(hwdep->dev, 44762306a36Sopenharmony_ci "only hwdep device 0 can be registered as OSS direct FM device!\n"); 44862306a36Sopenharmony_ci else if (snd_register_oss_device(hwdep->oss_type, 44962306a36Sopenharmony_ci card, hwdep->device, 45062306a36Sopenharmony_ci &snd_hwdep_f_ops, hwdep) < 0) 45162306a36Sopenharmony_ci dev_warn(hwdep->dev, 45262306a36Sopenharmony_ci "unable to register OSS compatibility device\n"); 45362306a36Sopenharmony_ci else 45462306a36Sopenharmony_ci hwdep->ossreg = 1; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci#endif 45762306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int snd_hwdep_dev_disconnect(struct snd_device *device) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct snd_hwdep *hwdep = device->device_data; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (snd_BUG_ON(!hwdep)) 46662306a36Sopenharmony_ci return -ENXIO; 46762306a36Sopenharmony_ci mutex_lock(®ister_mutex); 46862306a36Sopenharmony_ci if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) { 46962306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 47062306a36Sopenharmony_ci return -EINVAL; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci mutex_lock(&hwdep->open_mutex); 47362306a36Sopenharmony_ci wake_up(&hwdep->open_wait); 47462306a36Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 47562306a36Sopenharmony_ci if (hwdep->ossreg) 47662306a36Sopenharmony_ci snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); 47762306a36Sopenharmony_ci#endif 47862306a36Sopenharmony_ci snd_unregister_device(hwdep->dev); 47962306a36Sopenharmony_ci list_del_init(&hwdep->list); 48062306a36Sopenharmony_ci mutex_unlock(&hwdep->open_mutex); 48162306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 48662306a36Sopenharmony_ci/* 48762306a36Sopenharmony_ci * Info interface 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void snd_hwdep_proc_read(struct snd_info_entry *entry, 49162306a36Sopenharmony_ci struct snd_info_buffer *buffer) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct snd_hwdep *hwdep; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci mutex_lock(®ister_mutex); 49662306a36Sopenharmony_ci list_for_each_entry(hwdep, &snd_hwdep_devices, list) 49762306a36Sopenharmony_ci snd_iprintf(buffer, "%02i-%02i: %s\n", 49862306a36Sopenharmony_ci hwdep->card->number, hwdep->device, hwdep->name); 49962306a36Sopenharmony_ci mutex_unlock(®ister_mutex); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic struct snd_info_entry *snd_hwdep_proc_entry; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic void __init snd_hwdep_proc_init(void) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct snd_info_entry *entry; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL); 50962306a36Sopenharmony_ci if (entry) { 51062306a36Sopenharmony_ci entry->c.text.read = snd_hwdep_proc_read; 51162306a36Sopenharmony_ci if (snd_info_register(entry) < 0) { 51262306a36Sopenharmony_ci snd_info_free_entry(entry); 51362306a36Sopenharmony_ci entry = NULL; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci snd_hwdep_proc_entry = entry; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void __exit snd_hwdep_proc_done(void) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci snd_info_free_entry(snd_hwdep_proc_entry); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci#else /* !CONFIG_SND_PROC_FS */ 52462306a36Sopenharmony_ci#define snd_hwdep_proc_init() 52562306a36Sopenharmony_ci#define snd_hwdep_proc_done() 52662306a36Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */ 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* 53062306a36Sopenharmony_ci * ENTRY functions 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int __init alsa_hwdep_init(void) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci snd_hwdep_proc_init(); 53662306a36Sopenharmony_ci snd_ctl_register_ioctl(snd_hwdep_control_ioctl); 53762306a36Sopenharmony_ci snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl); 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic void __exit alsa_hwdep_exit(void) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl); 54462306a36Sopenharmony_ci snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl); 54562306a36Sopenharmony_ci snd_hwdep_proc_done(); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cimodule_init(alsa_hwdep_init) 54962306a36Sopenharmony_cimodule_exit(alsa_hwdep_exit) 550