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(&register_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(&register_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(&register_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(&register_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(&register_mutex);
42662306a36Sopenharmony_ci	if (snd_hwdep_search(card, hwdep->device)) {
42762306a36Sopenharmony_ci		mutex_unlock(&register_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(&register_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(&register_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(&register_mutex);
46862306a36Sopenharmony_ci	if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
46962306a36Sopenharmony_ci		mutex_unlock(&register_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(&register_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(&register_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(&register_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