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(&register_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(&register_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(&register_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(&register_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(&register_mutex);
4218c2ecf20Sopenharmony_ci	if (snd_hwdep_search(card, hwdep->device)) {
4228c2ecf20Sopenharmony_ci		mutex_unlock(&register_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(&register_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(&register_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(&register_mutex);
4638c2ecf20Sopenharmony_ci	if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
4648c2ecf20Sopenharmony_ci		mutex_unlock(&register_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(&register_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(&register_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(&register_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