18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hardware dependent layer 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/major.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/time.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/control.h> 168c2ecf20Sopenharmony_ci#include <sound/minors.h> 178c2ecf20Sopenharmony_ci#include <sound/hwdep.h> 188c2ecf20Sopenharmony_ci#include <sound/info.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hardware dependent layer"); 228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic LIST_HEAD(snd_hwdep_devices); 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(register_mutex); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int snd_hwdep_dev_free(struct snd_device *device); 288c2ecf20Sopenharmony_cistatic int snd_hwdep_dev_register(struct snd_device *device); 298c2ecf20Sopenharmony_cistatic int snd_hwdep_dev_disconnect(struct snd_device *device); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci list_for_each_entry(hwdep, &snd_hwdep_devices, list) 378c2ecf20Sopenharmony_ci if (hwdep->card == card && hwdep->device == device) 388c2ecf20Sopenharmony_ci return hwdep; 398c2ecf20Sopenharmony_ci return NULL; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 458c2ecf20Sopenharmony_ci if (hw->ops.llseek) 468c2ecf20Sopenharmony_ci return hw->ops.llseek(hw, file, offset, orig); 478c2ecf20Sopenharmony_ci return -ENXIO; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic ssize_t snd_hwdep_read(struct file * file, char __user *buf, 518c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 548c2ecf20Sopenharmony_ci if (hw->ops.read) 558c2ecf20Sopenharmony_ci return hw->ops.read(hw, buf, count, offset); 568c2ecf20Sopenharmony_ci return -ENXIO; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic ssize_t snd_hwdep_write(struct file * file, const char __user *buf, 608c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 638c2ecf20Sopenharmony_ci if (hw->ops.write) 648c2ecf20Sopenharmony_ci return hw->ops.write(hw, buf, count, offset); 658c2ecf20Sopenharmony_ci return -ENXIO; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int snd_hwdep_open(struct inode *inode, struct file * file) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int major = imajor(inode); 718c2ecf20Sopenharmony_ci struct snd_hwdep *hw; 728c2ecf20Sopenharmony_ci int err; 738c2ecf20Sopenharmony_ci wait_queue_entry_t wait; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (major == snd_major) { 768c2ecf20Sopenharmony_ci hw = snd_lookup_minor_data(iminor(inode), 778c2ecf20Sopenharmony_ci SNDRV_DEVICE_TYPE_HWDEP); 788c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 798c2ecf20Sopenharmony_ci } else if (major == SOUND_MAJOR) { 808c2ecf20Sopenharmony_ci hw = snd_lookup_oss_minor_data(iminor(inode), 818c2ecf20Sopenharmony_ci SNDRV_OSS_DEVICE_TYPE_DMFM); 828c2ecf20Sopenharmony_ci#endif 838c2ecf20Sopenharmony_ci } else 848c2ecf20Sopenharmony_ci return -ENXIO; 858c2ecf20Sopenharmony_ci if (hw == NULL) 868c2ecf20Sopenharmony_ci return -ENODEV; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!try_module_get(hw->card->module)) { 898c2ecf20Sopenharmony_ci snd_card_unref(hw->card); 908c2ecf20Sopenharmony_ci return -EFAULT; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci init_waitqueue_entry(&wait, current); 948c2ecf20Sopenharmony_ci add_wait_queue(&hw->open_wait, &wait); 958c2ecf20Sopenharmony_ci mutex_lock(&hw->open_mutex); 968c2ecf20Sopenharmony_ci while (1) { 978c2ecf20Sopenharmony_ci if (hw->exclusive && hw->used > 0) { 988c2ecf20Sopenharmony_ci err = -EBUSY; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci if (!hw->ops.open) { 1028c2ecf20Sopenharmony_ci err = 0; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci err = hw->ops.open(hw, file); 1068c2ecf20Sopenharmony_ci if (err >= 0) 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci if (err == -EAGAIN) { 1098c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 1108c2ecf20Sopenharmony_ci err = -EBUSY; 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } else 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 1168c2ecf20Sopenharmony_ci mutex_unlock(&hw->open_mutex); 1178c2ecf20Sopenharmony_ci schedule(); 1188c2ecf20Sopenharmony_ci mutex_lock(&hw->open_mutex); 1198c2ecf20Sopenharmony_ci if (hw->card->shutdown) { 1208c2ecf20Sopenharmony_ci err = -ENODEV; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci if (signal_pending(current)) { 1248c2ecf20Sopenharmony_ci err = -ERESTARTSYS; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci remove_wait_queue(&hw->open_wait, &wait); 1298c2ecf20Sopenharmony_ci if (err >= 0) { 1308c2ecf20Sopenharmony_ci err = snd_card_file_add(hw->card, file); 1318c2ecf20Sopenharmony_ci if (err >= 0) { 1328c2ecf20Sopenharmony_ci file->private_data = hw; 1338c2ecf20Sopenharmony_ci hw->used++; 1348c2ecf20Sopenharmony_ci } else { 1358c2ecf20Sopenharmony_ci if (hw->ops.release) 1368c2ecf20Sopenharmony_ci hw->ops.release(hw, file); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci mutex_unlock(&hw->open_mutex); 1408c2ecf20Sopenharmony_ci if (err < 0) 1418c2ecf20Sopenharmony_ci module_put(hw->card->module); 1428c2ecf20Sopenharmony_ci snd_card_unref(hw->card); 1438c2ecf20Sopenharmony_ci return err; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int snd_hwdep_release(struct inode *inode, struct file * file) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int err = 0; 1498c2ecf20Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 1508c2ecf20Sopenharmony_ci struct module *mod = hw->card->module; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci mutex_lock(&hw->open_mutex); 1538c2ecf20Sopenharmony_ci if (hw->ops.release) 1548c2ecf20Sopenharmony_ci err = hw->ops.release(hw, file); 1558c2ecf20Sopenharmony_ci if (hw->used > 0) 1568c2ecf20Sopenharmony_ci hw->used--; 1578c2ecf20Sopenharmony_ci mutex_unlock(&hw->open_mutex); 1588c2ecf20Sopenharmony_ci wake_up(&hw->open_wait); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci snd_card_file_remove(hw->card, file); 1618c2ecf20Sopenharmony_ci module_put(mod); 1628c2ecf20Sopenharmony_ci return err; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic __poll_t snd_hwdep_poll(struct file * file, poll_table * wait) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 1688c2ecf20Sopenharmony_ci if (hw->ops.poll) 1698c2ecf20Sopenharmony_ci return hw->ops.poll(hw, file, wait); 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int snd_hwdep_info(struct snd_hwdep *hw, 1748c2ecf20Sopenharmony_ci struct snd_hwdep_info __user *_info) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct snd_hwdep_info info; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 1798c2ecf20Sopenharmony_ci info.card = hw->card->number; 1808c2ecf20Sopenharmony_ci strlcpy(info.id, hw->id, sizeof(info.id)); 1818c2ecf20Sopenharmony_ci strlcpy(info.name, hw->name, sizeof(info.name)); 1828c2ecf20Sopenharmony_ci info.iface = hw->iface; 1838c2ecf20Sopenharmony_ci if (copy_to_user(_info, &info, sizeof(info))) 1848c2ecf20Sopenharmony_ci return -EFAULT; 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int snd_hwdep_dsp_status(struct snd_hwdep *hw, 1898c2ecf20Sopenharmony_ci struct snd_hwdep_dsp_status __user *_info) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct snd_hwdep_dsp_status info; 1928c2ecf20Sopenharmony_ci int err; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (! hw->ops.dsp_status) 1958c2ecf20Sopenharmony_ci return -ENXIO; 1968c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 1978c2ecf20Sopenharmony_ci info.dsp_loaded = hw->dsp_loaded; 1988c2ecf20Sopenharmony_ci if ((err = hw->ops.dsp_status(hw, &info)) < 0) 1998c2ecf20Sopenharmony_ci return err; 2008c2ecf20Sopenharmony_ci if (copy_to_user(_info, &info, sizeof(info))) 2018c2ecf20Sopenharmony_ci return -EFAULT; 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int snd_hwdep_dsp_load(struct snd_hwdep *hw, 2068c2ecf20Sopenharmony_ci struct snd_hwdep_dsp_image *info) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int err; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (! hw->ops.dsp_load) 2118c2ecf20Sopenharmony_ci return -ENXIO; 2128c2ecf20Sopenharmony_ci if (info->index >= 32) 2138c2ecf20Sopenharmony_ci return -EINVAL; 2148c2ecf20Sopenharmony_ci /* check whether the dsp was already loaded */ 2158c2ecf20Sopenharmony_ci if (hw->dsp_loaded & (1u << info->index)) 2168c2ecf20Sopenharmony_ci return -EBUSY; 2178c2ecf20Sopenharmony_ci err = hw->ops.dsp_load(hw, info); 2188c2ecf20Sopenharmony_ci if (err < 0) 2198c2ecf20Sopenharmony_ci return err; 2208c2ecf20Sopenharmony_ci hw->dsp_loaded |= (1u << info->index); 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int snd_hwdep_dsp_load_user(struct snd_hwdep *hw, 2258c2ecf20Sopenharmony_ci struct snd_hwdep_dsp_image __user *_info) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct snd_hwdep_dsp_image info = {}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (copy_from_user(&info, _info, sizeof(info))) 2308c2ecf20Sopenharmony_ci return -EFAULT; 2318c2ecf20Sopenharmony_ci return snd_hwdep_dsp_load(hw, &info); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic long snd_hwdep_ioctl(struct file * file, unsigned int cmd, 2368c2ecf20Sopenharmony_ci unsigned long arg) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 2398c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 2408c2ecf20Sopenharmony_ci switch (cmd) { 2418c2ecf20Sopenharmony_ci case SNDRV_HWDEP_IOCTL_PVERSION: 2428c2ecf20Sopenharmony_ci return put_user(SNDRV_HWDEP_VERSION, (int __user *)argp); 2438c2ecf20Sopenharmony_ci case SNDRV_HWDEP_IOCTL_INFO: 2448c2ecf20Sopenharmony_ci return snd_hwdep_info(hw, argp); 2458c2ecf20Sopenharmony_ci case SNDRV_HWDEP_IOCTL_DSP_STATUS: 2468c2ecf20Sopenharmony_ci return snd_hwdep_dsp_status(hw, argp); 2478c2ecf20Sopenharmony_ci case SNDRV_HWDEP_IOCTL_DSP_LOAD: 2488c2ecf20Sopenharmony_ci return snd_hwdep_dsp_load_user(hw, argp); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci if (hw->ops.ioctl) 2518c2ecf20Sopenharmony_ci return hw->ops.ioctl(hw, file, cmd, arg); 2528c2ecf20Sopenharmony_ci return -ENOTTY; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct snd_hwdep *hw = file->private_data; 2588c2ecf20Sopenharmony_ci if (hw->ops.mmap) 2598c2ecf20Sopenharmony_ci return hw->ops.mmap(hw, file, vma); 2608c2ecf20Sopenharmony_ci return -ENXIO; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int snd_hwdep_control_ioctl(struct snd_card *card, 2648c2ecf20Sopenharmony_ci struct snd_ctl_file * control, 2658c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci switch (cmd) { 2688c2ecf20Sopenharmony_ci case SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE: 2698c2ecf20Sopenharmony_ci { 2708c2ecf20Sopenharmony_ci int device; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (get_user(device, (int __user *)arg)) 2738c2ecf20Sopenharmony_ci return -EFAULT; 2748c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (device < 0) 2778c2ecf20Sopenharmony_ci device = 0; 2788c2ecf20Sopenharmony_ci else if (device < SNDRV_MINOR_HWDEPS) 2798c2ecf20Sopenharmony_ci device++; 2808c2ecf20Sopenharmony_ci else 2818c2ecf20Sopenharmony_ci device = SNDRV_MINOR_HWDEPS; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci while (device < SNDRV_MINOR_HWDEPS) { 2848c2ecf20Sopenharmony_ci if (snd_hwdep_search(card, device)) 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci device++; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci if (device >= SNDRV_MINOR_HWDEPS) 2898c2ecf20Sopenharmony_ci device = -1; 2908c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 2918c2ecf20Sopenharmony_ci if (put_user(device, (int __user *)arg)) 2928c2ecf20Sopenharmony_ci return -EFAULT; 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci case SNDRV_CTL_IOCTL_HWDEP_INFO: 2968c2ecf20Sopenharmony_ci { 2978c2ecf20Sopenharmony_ci struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg; 2988c2ecf20Sopenharmony_ci int device, err; 2998c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (get_user(device, &info->device)) 3028c2ecf20Sopenharmony_ci return -EFAULT; 3038c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 3048c2ecf20Sopenharmony_ci hwdep = snd_hwdep_search(card, device); 3058c2ecf20Sopenharmony_ci if (hwdep) 3068c2ecf20Sopenharmony_ci err = snd_hwdep_info(hwdep, info); 3078c2ecf20Sopenharmony_ci else 3088c2ecf20Sopenharmony_ci err = -ENXIO; 3098c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 3108c2ecf20Sopenharmony_ci return err; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 3178c2ecf20Sopenharmony_ci#include "hwdep_compat.c" 3188c2ecf20Sopenharmony_ci#else 3198c2ecf20Sopenharmony_ci#define snd_hwdep_ioctl_compat NULL 3208c2ecf20Sopenharmony_ci#endif 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic const struct file_operations snd_hwdep_f_ops = 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3298c2ecf20Sopenharmony_ci .llseek = snd_hwdep_llseek, 3308c2ecf20Sopenharmony_ci .read = snd_hwdep_read, 3318c2ecf20Sopenharmony_ci .write = snd_hwdep_write, 3328c2ecf20Sopenharmony_ci .open = snd_hwdep_open, 3338c2ecf20Sopenharmony_ci .release = snd_hwdep_release, 3348c2ecf20Sopenharmony_ci .poll = snd_hwdep_poll, 3358c2ecf20Sopenharmony_ci .unlocked_ioctl = snd_hwdep_ioctl, 3368c2ecf20Sopenharmony_ci .compat_ioctl = snd_hwdep_ioctl_compat, 3378c2ecf20Sopenharmony_ci .mmap = snd_hwdep_mmap, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void release_hwdep_device(struct device *dev) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci kfree(container_of(dev, struct snd_hwdep, dev)); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/** 3468c2ecf20Sopenharmony_ci * snd_hwdep_new - create a new hwdep instance 3478c2ecf20Sopenharmony_ci * @card: the card instance 3488c2ecf20Sopenharmony_ci * @id: the id string 3498c2ecf20Sopenharmony_ci * @device: the device index (zero-based) 3508c2ecf20Sopenharmony_ci * @rhwdep: the pointer to store the new hwdep instance 3518c2ecf20Sopenharmony_ci * 3528c2ecf20Sopenharmony_ci * Creates a new hwdep instance with the given index on the card. 3538c2ecf20Sopenharmony_ci * The callbacks (hwdep->ops) must be set on the returned instance 3548c2ecf20Sopenharmony_ci * after this call manually by the caller. 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_ciint snd_hwdep_new(struct snd_card *card, char *id, int device, 3598c2ecf20Sopenharmony_ci struct snd_hwdep **rhwdep) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep; 3628c2ecf20Sopenharmony_ci int err; 3638c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 3648c2ecf20Sopenharmony_ci .dev_free = snd_hwdep_dev_free, 3658c2ecf20Sopenharmony_ci .dev_register = snd_hwdep_dev_register, 3668c2ecf20Sopenharmony_ci .dev_disconnect = snd_hwdep_dev_disconnect, 3678c2ecf20Sopenharmony_ci }; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 3708c2ecf20Sopenharmony_ci return -ENXIO; 3718c2ecf20Sopenharmony_ci if (rhwdep) 3728c2ecf20Sopenharmony_ci *rhwdep = NULL; 3738c2ecf20Sopenharmony_ci hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL); 3748c2ecf20Sopenharmony_ci if (!hwdep) 3758c2ecf20Sopenharmony_ci return -ENOMEM; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci init_waitqueue_head(&hwdep->open_wait); 3788c2ecf20Sopenharmony_ci mutex_init(&hwdep->open_mutex); 3798c2ecf20Sopenharmony_ci hwdep->card = card; 3808c2ecf20Sopenharmony_ci hwdep->device = device; 3818c2ecf20Sopenharmony_ci if (id) 3828c2ecf20Sopenharmony_ci strlcpy(hwdep->id, id, sizeof(hwdep->id)); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci snd_device_initialize(&hwdep->dev, card); 3858c2ecf20Sopenharmony_ci hwdep->dev.release = release_hwdep_device; 3868c2ecf20Sopenharmony_ci dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device); 3878c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 3888c2ecf20Sopenharmony_ci hwdep->oss_type = -1; 3898c2ecf20Sopenharmony_ci#endif 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops); 3928c2ecf20Sopenharmony_ci if (err < 0) { 3938c2ecf20Sopenharmony_ci put_device(&hwdep->dev); 3948c2ecf20Sopenharmony_ci return err; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (rhwdep) 3988c2ecf20Sopenharmony_ci *rhwdep = hwdep; 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_hwdep_new); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int snd_hwdep_dev_free(struct snd_device *device) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep = device->device_data; 4068c2ecf20Sopenharmony_ci if (!hwdep) 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci if (hwdep->private_free) 4098c2ecf20Sopenharmony_ci hwdep->private_free(hwdep); 4108c2ecf20Sopenharmony_ci put_device(&hwdep->dev); 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int snd_hwdep_dev_register(struct snd_device *device) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep = device->device_data; 4178c2ecf20Sopenharmony_ci struct snd_card *card = hwdep->card; 4188c2ecf20Sopenharmony_ci int err; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 4218c2ecf20Sopenharmony_ci if (snd_hwdep_search(card, hwdep->device)) { 4228c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 4238c2ecf20Sopenharmony_ci return -EBUSY; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci list_add_tail(&hwdep->list, &snd_hwdep_devices); 4268c2ecf20Sopenharmony_ci err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, 4278c2ecf20Sopenharmony_ci hwdep->card, hwdep->device, 4288c2ecf20Sopenharmony_ci &snd_hwdep_f_ops, hwdep, &hwdep->dev); 4298c2ecf20Sopenharmony_ci if (err < 0) { 4308c2ecf20Sopenharmony_ci dev_err(&hwdep->dev, "unable to register\n"); 4318c2ecf20Sopenharmony_ci list_del(&hwdep->list); 4328c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 4338c2ecf20Sopenharmony_ci return err; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 4378c2ecf20Sopenharmony_ci hwdep->ossreg = 0; 4388c2ecf20Sopenharmony_ci if (hwdep->oss_type >= 0) { 4398c2ecf20Sopenharmony_ci if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM && 4408c2ecf20Sopenharmony_ci hwdep->device) 4418c2ecf20Sopenharmony_ci dev_warn(&hwdep->dev, 4428c2ecf20Sopenharmony_ci "only hwdep device 0 can be registered as OSS direct FM device!\n"); 4438c2ecf20Sopenharmony_ci else if (snd_register_oss_device(hwdep->oss_type, 4448c2ecf20Sopenharmony_ci card, hwdep->device, 4458c2ecf20Sopenharmony_ci &snd_hwdep_f_ops, hwdep) < 0) 4468c2ecf20Sopenharmony_ci dev_warn(&hwdep->dev, 4478c2ecf20Sopenharmony_ci "unable to register OSS compatibility device\n"); 4488c2ecf20Sopenharmony_ci else 4498c2ecf20Sopenharmony_ci hwdep->ossreg = 1; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci#endif 4528c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int snd_hwdep_dev_disconnect(struct snd_device *device) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep = device->device_data; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (snd_BUG_ON(!hwdep)) 4618c2ecf20Sopenharmony_ci return -ENXIO; 4628c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 4638c2ecf20Sopenharmony_ci if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) { 4648c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 4658c2ecf20Sopenharmony_ci return -EINVAL; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci mutex_lock(&hwdep->open_mutex); 4688c2ecf20Sopenharmony_ci wake_up(&hwdep->open_wait); 4698c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_OSSEMUL 4708c2ecf20Sopenharmony_ci if (hwdep->ossreg) 4718c2ecf20Sopenharmony_ci snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); 4728c2ecf20Sopenharmony_ci#endif 4738c2ecf20Sopenharmony_ci snd_unregister_device(&hwdep->dev); 4748c2ecf20Sopenharmony_ci list_del_init(&hwdep->list); 4758c2ecf20Sopenharmony_ci mutex_unlock(&hwdep->open_mutex); 4768c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 4818c2ecf20Sopenharmony_ci/* 4828c2ecf20Sopenharmony_ci * Info interface 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic void snd_hwdep_proc_read(struct snd_info_entry *entry, 4868c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct snd_hwdep *hwdep; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 4918c2ecf20Sopenharmony_ci list_for_each_entry(hwdep, &snd_hwdep_devices, list) 4928c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%02i-%02i: %s\n", 4938c2ecf20Sopenharmony_ci hwdep->card->number, hwdep->device, hwdep->name); 4948c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic struct snd_info_entry *snd_hwdep_proc_entry; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic void __init snd_hwdep_proc_init(void) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) { 5048c2ecf20Sopenharmony_ci entry->c.text.read = snd_hwdep_proc_read; 5058c2ecf20Sopenharmony_ci if (snd_info_register(entry) < 0) { 5068c2ecf20Sopenharmony_ci snd_info_free_entry(entry); 5078c2ecf20Sopenharmony_ci entry = NULL; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci snd_hwdep_proc_entry = entry; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void __exit snd_hwdep_proc_done(void) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci snd_info_free_entry(snd_hwdep_proc_entry); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci#else /* !CONFIG_SND_PROC_FS */ 5188c2ecf20Sopenharmony_ci#define snd_hwdep_proc_init() 5198c2ecf20Sopenharmony_ci#define snd_hwdep_proc_done() 5208c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */ 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/* 5248c2ecf20Sopenharmony_ci * ENTRY functions 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int __init alsa_hwdep_init(void) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci snd_hwdep_proc_init(); 5308c2ecf20Sopenharmony_ci snd_ctl_register_ioctl(snd_hwdep_control_ioctl); 5318c2ecf20Sopenharmony_ci snd_ctl_register_ioctl_compat(snd_hwdep_control_ioctl); 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void __exit alsa_hwdep_exit(void) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci snd_ctl_unregister_ioctl(snd_hwdep_control_ioctl); 5388c2ecf20Sopenharmony_ci snd_ctl_unregister_ioctl_compat(snd_hwdep_control_ioctl); 5398c2ecf20Sopenharmony_ci snd_hwdep_proc_done(); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cimodule_init(alsa_hwdep_init) 5438c2ecf20Sopenharmony_cimodule_exit(alsa_hwdep_exit) 544