1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file control/control_hw.c
3d5ac70f0Sopenharmony_ci * \brief CTL HW Plugin Interface
4d5ac70f0Sopenharmony_ci * \author Jaroslav Kysela <perex@perex.cz>
5d5ac70f0Sopenharmony_ci * \date 2000
6d5ac70f0Sopenharmony_ci */
7d5ac70f0Sopenharmony_ci/*
8d5ac70f0Sopenharmony_ci *  Control Interface - Hardware
9d5ac70f0Sopenharmony_ci *  Copyright (c) 1998,1999,2000 by Jaroslav Kysela <perex@perex.cz>
10d5ac70f0Sopenharmony_ci *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11d5ac70f0Sopenharmony_ci *
12d5ac70f0Sopenharmony_ci *
13d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
14d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
15d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
16d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
17d5ac70f0Sopenharmony_ci *
18d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
19d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
22d5ac70f0Sopenharmony_ci *
23d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
24d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
25d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26d5ac70f0Sopenharmony_ci *
27d5ac70f0Sopenharmony_ci */
28d5ac70f0Sopenharmony_ci
29d5ac70f0Sopenharmony_ci#include "control_local.h"
30d5ac70f0Sopenharmony_ci#include <stdio.h>
31d5ac70f0Sopenharmony_ci#include <stdlib.h>
32d5ac70f0Sopenharmony_ci#include <unistd.h>
33d5ac70f0Sopenharmony_ci#include <signal.h>
34d5ac70f0Sopenharmony_ci#include <string.h>
35d5ac70f0Sopenharmony_ci#include <fcntl.h>
36d5ac70f0Sopenharmony_ci#include <sys/ioctl.h>
37d5ac70f0Sopenharmony_ci
38d5ac70f0Sopenharmony_ci#ifndef PIC
39d5ac70f0Sopenharmony_ci/* entry for static linking */
40d5ac70f0Sopenharmony_ciconst char *_snd_module_control_hw = "";
41d5ac70f0Sopenharmony_ci#endif
42d5ac70f0Sopenharmony_ci
43d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
44d5ac70f0Sopenharmony_ci
45d5ac70f0Sopenharmony_ci#ifndef F_SETSIG
46d5ac70f0Sopenharmony_ci#define F_SETSIG 10
47d5ac70f0Sopenharmony_ci#endif
48d5ac70f0Sopenharmony_ci
49d5ac70f0Sopenharmony_ci#define SNDRV_FILE_CONTROL	ALSA_DEVICE_DIRECTORY "controlC%i"
50d5ac70f0Sopenharmony_ci#define SNDRV_CTL_VERSION_MAX	SNDRV_PROTOCOL_VERSION(2, 0, 4)
51d5ac70f0Sopenharmony_ci
52d5ac70f0Sopenharmony_citypedef struct {
53d5ac70f0Sopenharmony_ci	int card;
54d5ac70f0Sopenharmony_ci	int fd;
55d5ac70f0Sopenharmony_ci	unsigned int protocol;
56d5ac70f0Sopenharmony_ci} snd_ctl_hw_t;
57d5ac70f0Sopenharmony_ci#endif /* DOC_HIDDEN */
58d5ac70f0Sopenharmony_ci
59d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_close(snd_ctl_t *handle)
60d5ac70f0Sopenharmony_ci{
61d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
62d5ac70f0Sopenharmony_ci	int res;
63d5ac70f0Sopenharmony_ci	res = close(hw->fd) < 0 ? -errno : 0;
64d5ac70f0Sopenharmony_ci	free(hw);
65d5ac70f0Sopenharmony_ci	return res;
66d5ac70f0Sopenharmony_ci}
67d5ac70f0Sopenharmony_ci
68d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_nonblock(snd_ctl_t *handle, int nonblock)
69d5ac70f0Sopenharmony_ci{
70d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
71d5ac70f0Sopenharmony_ci	long flags;
72d5ac70f0Sopenharmony_ci	int fd = hw->fd;
73d5ac70f0Sopenharmony_ci	if ((flags = fcntl(fd, F_GETFL)) < 0) {
74d5ac70f0Sopenharmony_ci		SYSERR("F_GETFL failed");
75d5ac70f0Sopenharmony_ci		return -errno;
76d5ac70f0Sopenharmony_ci	}
77d5ac70f0Sopenharmony_ci	if (nonblock)
78d5ac70f0Sopenharmony_ci		flags |= O_NONBLOCK;
79d5ac70f0Sopenharmony_ci	else
80d5ac70f0Sopenharmony_ci		flags &= ~O_NONBLOCK;
81d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETFL, flags) < 0) {
82d5ac70f0Sopenharmony_ci		SYSERR("F_SETFL for O_NONBLOCK failed");
83d5ac70f0Sopenharmony_ci		return -errno;
84d5ac70f0Sopenharmony_ci	}
85d5ac70f0Sopenharmony_ci	return 0;
86d5ac70f0Sopenharmony_ci}
87d5ac70f0Sopenharmony_ci
88d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_async(snd_ctl_t *ctl, int sig, pid_t pid)
89d5ac70f0Sopenharmony_ci{
90d5ac70f0Sopenharmony_ci	long flags;
91d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = ctl->private_data;
92d5ac70f0Sopenharmony_ci	int fd = hw->fd;
93d5ac70f0Sopenharmony_ci
94d5ac70f0Sopenharmony_ci	if ((flags = fcntl(fd, F_GETFL)) < 0) {
95d5ac70f0Sopenharmony_ci		SYSERR("F_GETFL failed");
96d5ac70f0Sopenharmony_ci		return -errno;
97d5ac70f0Sopenharmony_ci	}
98d5ac70f0Sopenharmony_ci	if (sig >= 0)
99d5ac70f0Sopenharmony_ci		flags |= O_ASYNC;
100d5ac70f0Sopenharmony_ci	else
101d5ac70f0Sopenharmony_ci		flags &= ~O_ASYNC;
102d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETFL, flags) < 0) {
103d5ac70f0Sopenharmony_ci		SYSERR("F_SETFL for O_ASYNC failed");
104d5ac70f0Sopenharmony_ci		return -errno;
105d5ac70f0Sopenharmony_ci	}
106d5ac70f0Sopenharmony_ci	if (sig < 0)
107d5ac70f0Sopenharmony_ci		return 0;
108d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
109d5ac70f0Sopenharmony_ci		SYSERR("F_SETSIG failed");
110d5ac70f0Sopenharmony_ci		return -errno;
111d5ac70f0Sopenharmony_ci	}
112d5ac70f0Sopenharmony_ci	if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
113d5ac70f0Sopenharmony_ci		SYSERR("F_SETOWN failed");
114d5ac70f0Sopenharmony_ci		return -errno;
115d5ac70f0Sopenharmony_ci	}
116d5ac70f0Sopenharmony_ci	return 0;
117d5ac70f0Sopenharmony_ci}
118d5ac70f0Sopenharmony_ci
119d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe)
120d5ac70f0Sopenharmony_ci{
121d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
122d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
123d5ac70f0Sopenharmony_ci		SYSERR("SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS failed");
124d5ac70f0Sopenharmony_ci		return -errno;
125d5ac70f0Sopenharmony_ci	}
126d5ac70f0Sopenharmony_ci	return 0;
127d5ac70f0Sopenharmony_ci}
128d5ac70f0Sopenharmony_ci
129d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)
130d5ac70f0Sopenharmony_ci{
131d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
132d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, info) < 0) {
133d5ac70f0Sopenharmony_ci		SYSERR("SNDRV_CTL_IOCTL_CARD_INFO failed");
134d5ac70f0Sopenharmony_ci		return -errno;
135d5ac70f0Sopenharmony_ci	}
136d5ac70f0Sopenharmony_ci	return 0;
137d5ac70f0Sopenharmony_ci}
138d5ac70f0Sopenharmony_ci
139d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
140d5ac70f0Sopenharmony_ci{
141d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
142d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LIST, list) < 0)
143d5ac70f0Sopenharmony_ci		return -errno;
144d5ac70f0Sopenharmony_ci	return 0;
145d5ac70f0Sopenharmony_ci}
146d5ac70f0Sopenharmony_ci
147d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
148d5ac70f0Sopenharmony_ci{
149d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
150d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_INFO, info) < 0)
151d5ac70f0Sopenharmony_ci		return -errno;
152d5ac70f0Sopenharmony_ci	return 0;
153d5ac70f0Sopenharmony_ci}
154d5ac70f0Sopenharmony_ci
155d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_add(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
156d5ac70f0Sopenharmony_ci{
157d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
158d5ac70f0Sopenharmony_ci
159d5ac70f0Sopenharmony_ci	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
160d5ac70f0Sopenharmony_ci	    hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
161d5ac70f0Sopenharmony_ci		return -ENXIO;
162d5ac70f0Sopenharmony_ci
163d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_ADD, info) < 0)
164d5ac70f0Sopenharmony_ci		return -errno;
165d5ac70f0Sopenharmony_ci	return 0;
166d5ac70f0Sopenharmony_ci}
167d5ac70f0Sopenharmony_ci
168d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
169d5ac70f0Sopenharmony_ci{
170d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
171d5ac70f0Sopenharmony_ci
172d5ac70f0Sopenharmony_ci	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
173d5ac70f0Sopenharmony_ci	    hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
174d5ac70f0Sopenharmony_ci		return -ENXIO;
175d5ac70f0Sopenharmony_ci
176d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REPLACE, info) < 0)
177d5ac70f0Sopenharmony_ci		return -errno;
178d5ac70f0Sopenharmony_ci	return 0;
179d5ac70f0Sopenharmony_ci}
180d5ac70f0Sopenharmony_ci
181d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
182d5ac70f0Sopenharmony_ci{
183d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
184d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REMOVE, id) < 0)
185d5ac70f0Sopenharmony_ci		return -errno;
186d5ac70f0Sopenharmony_ci	return 0;
187d5ac70f0Sopenharmony_ci}
188d5ac70f0Sopenharmony_ci
189d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
190d5ac70f0Sopenharmony_ci{
191d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
192d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_READ, control) < 0)
193d5ac70f0Sopenharmony_ci		return -errno;
194d5ac70f0Sopenharmony_ci	return 0;
195d5ac70f0Sopenharmony_ci}
196d5ac70f0Sopenharmony_ci
197d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
198d5ac70f0Sopenharmony_ci{
199d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
200d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) < 0)
201d5ac70f0Sopenharmony_ci		return -errno;
202d5ac70f0Sopenharmony_ci	return 0;
203d5ac70f0Sopenharmony_ci}
204d5ac70f0Sopenharmony_ci
205d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
206d5ac70f0Sopenharmony_ci{
207d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
208d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LOCK, id) < 0)
209d5ac70f0Sopenharmony_ci		return -errno;
210d5ac70f0Sopenharmony_ci	return 0;
211d5ac70f0Sopenharmony_ci}
212d5ac70f0Sopenharmony_ci
213d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
214d5ac70f0Sopenharmony_ci{
215d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
216d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_UNLOCK, id) < 0)
217d5ac70f0Sopenharmony_ci		return -errno;
218d5ac70f0Sopenharmony_ci	return 0;
219d5ac70f0Sopenharmony_ci}
220d5ac70f0Sopenharmony_ci
221d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag,
222d5ac70f0Sopenharmony_ci			       unsigned int numid,
223d5ac70f0Sopenharmony_ci			       unsigned int *tlv, unsigned int tlv_size)
224d5ac70f0Sopenharmony_ci{
225d5ac70f0Sopenharmony_ci	unsigned int inum;
226d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
227d5ac70f0Sopenharmony_ci	struct snd_ctl_tlv *xtlv;
228d5ac70f0Sopenharmony_ci
229d5ac70f0Sopenharmony_ci	/* we don't support TLV on protocol ver 2.0.3 or earlier */
230d5ac70f0Sopenharmony_ci	if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 4))
231d5ac70f0Sopenharmony_ci		return -ENXIO;
232d5ac70f0Sopenharmony_ci
233d5ac70f0Sopenharmony_ci	switch (op_flag) {
234d5ac70f0Sopenharmony_ci	case -1: inum = SNDRV_CTL_IOCTL_TLV_COMMAND; break;
235d5ac70f0Sopenharmony_ci 	case 0:	inum = SNDRV_CTL_IOCTL_TLV_READ; break;
236d5ac70f0Sopenharmony_ci	case 1:	inum = SNDRV_CTL_IOCTL_TLV_WRITE; break;
237d5ac70f0Sopenharmony_ci	default: return -EINVAL;
238d5ac70f0Sopenharmony_ci	}
239d5ac70f0Sopenharmony_ci	xtlv = malloc(sizeof(struct snd_ctl_tlv) + tlv_size);
240d5ac70f0Sopenharmony_ci	if (xtlv == NULL)
241d5ac70f0Sopenharmony_ci		return -ENOMEM;
242d5ac70f0Sopenharmony_ci	xtlv->numid = numid;
243d5ac70f0Sopenharmony_ci	xtlv->length = tlv_size;
244d5ac70f0Sopenharmony_ci	memcpy(xtlv->tlv, tlv, tlv_size);
245d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, inum, xtlv) < 0) {
246d5ac70f0Sopenharmony_ci		free(xtlv);
247d5ac70f0Sopenharmony_ci		return -errno;
248d5ac70f0Sopenharmony_ci	}
249d5ac70f0Sopenharmony_ci	if (op_flag == 0) {
250d5ac70f0Sopenharmony_ci		unsigned int size;
251d5ac70f0Sopenharmony_ci		size = xtlv->tlv[SNDRV_CTL_TLVO_LEN] + 2 * sizeof(unsigned int);
252d5ac70f0Sopenharmony_ci		if (size > tlv_size) {
253d5ac70f0Sopenharmony_ci			free(xtlv);
254d5ac70f0Sopenharmony_ci			return -EFAULT;
255d5ac70f0Sopenharmony_ci		}
256d5ac70f0Sopenharmony_ci		memcpy(tlv, xtlv->tlv, size);
257d5ac70f0Sopenharmony_ci	}
258d5ac70f0Sopenharmony_ci	free(xtlv);
259d5ac70f0Sopenharmony_ci	return 0;
260d5ac70f0Sopenharmony_ci}
261d5ac70f0Sopenharmony_ci
262d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device)
263d5ac70f0Sopenharmony_ci{
264d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
265d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, device) < 0)
266d5ac70f0Sopenharmony_ci		return -errno;
267d5ac70f0Sopenharmony_ci	return 0;
268d5ac70f0Sopenharmony_ci}
269d5ac70f0Sopenharmony_ci
270d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info)
271d5ac70f0Sopenharmony_ci{
272d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
273d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_INFO, info) < 0)
274d5ac70f0Sopenharmony_ci		return -errno;
275d5ac70f0Sopenharmony_ci	return 0;
276d5ac70f0Sopenharmony_ci}
277d5ac70f0Sopenharmony_ci
278d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device)
279d5ac70f0Sopenharmony_ci{
280d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
281d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, device) < 0)
282d5ac70f0Sopenharmony_ci		return -errno;
283d5ac70f0Sopenharmony_ci	return 0;
284d5ac70f0Sopenharmony_ci}
285d5ac70f0Sopenharmony_ci
286d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info)
287d5ac70f0Sopenharmony_ci{
288d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
289d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0)
290d5ac70f0Sopenharmony_ci		return -errno;
291d5ac70f0Sopenharmony_ci	/* may be configurable (optional) */
292d5ac70f0Sopenharmony_ci	if (__snd_pcm_info_eld_fixup_check(info))
293d5ac70f0Sopenharmony_ci		return __snd_pcm_info_eld_fixup(info);
294d5ac70f0Sopenharmony_ci	return 0;
295d5ac70f0Sopenharmony_ci}
296d5ac70f0Sopenharmony_ci
297d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev)
298d5ac70f0Sopenharmony_ci{
299d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
300d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdev) < 0)
301d5ac70f0Sopenharmony_ci		return -errno;
302d5ac70f0Sopenharmony_ci	return 0;
303d5ac70f0Sopenharmony_ci}
304d5ac70f0Sopenharmony_ci
305d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device)
306d5ac70f0Sopenharmony_ci{
307d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
308d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, device) < 0)
309d5ac70f0Sopenharmony_ci		return -errno;
310d5ac70f0Sopenharmony_ci	return 0;
311d5ac70f0Sopenharmony_ci}
312d5ac70f0Sopenharmony_ci
313d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info)
314d5ac70f0Sopenharmony_ci{
315d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
316d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, info) < 0)
317d5ac70f0Sopenharmony_ci		return -errno;
318d5ac70f0Sopenharmony_ci	return 0;
319d5ac70f0Sopenharmony_ci}
320d5ac70f0Sopenharmony_ci
321d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev)
322d5ac70f0Sopenharmony_ci{
323d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
324d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, &subdev) < 0)
325d5ac70f0Sopenharmony_ci		return -errno;
326d5ac70f0Sopenharmony_ci	return 0;
327d5ac70f0Sopenharmony_ci}
328d5ac70f0Sopenharmony_ci
329d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_ump_next_device(snd_ctl_t *handle, int *device)
330d5ac70f0Sopenharmony_ci{
331d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
332d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE, device) < 0)
333d5ac70f0Sopenharmony_ci		return -errno;
334d5ac70f0Sopenharmony_ci	return 0;
335d5ac70f0Sopenharmony_ci}
336d5ac70f0Sopenharmony_ci
337d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_ump_endpoint_info(snd_ctl_t *handle,
338d5ac70f0Sopenharmony_ci					snd_ump_endpoint_info_t *info)
339d5ac70f0Sopenharmony_ci{
340d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
341d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO, info) < 0)
342d5ac70f0Sopenharmony_ci		return -errno;
343d5ac70f0Sopenharmony_ci	return 0;
344d5ac70f0Sopenharmony_ci}
345d5ac70f0Sopenharmony_ci
346d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_ump_block_info(snd_ctl_t *handle,
347d5ac70f0Sopenharmony_ci				     snd_ump_block_info_t *info)
348d5ac70f0Sopenharmony_ci{
349d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
350d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, info) < 0)
351d5ac70f0Sopenharmony_ci		return -errno;
352d5ac70f0Sopenharmony_ci	return 0;
353d5ac70f0Sopenharmony_ci}
354d5ac70f0Sopenharmony_ci
355d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state)
356d5ac70f0Sopenharmony_ci{
357d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
358d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER, &state) < 0)
359d5ac70f0Sopenharmony_ci		return -errno;
360d5ac70f0Sopenharmony_ci	return 0;
361d5ac70f0Sopenharmony_ci}
362d5ac70f0Sopenharmony_ci
363d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state)
364d5ac70f0Sopenharmony_ci{
365d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
366d5ac70f0Sopenharmony_ci	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER_STATE, state) < 0)
367d5ac70f0Sopenharmony_ci		return -errno;
368d5ac70f0Sopenharmony_ci	return 0;
369d5ac70f0Sopenharmony_ci}
370d5ac70f0Sopenharmony_ci
371d5ac70f0Sopenharmony_cistatic int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)
372d5ac70f0Sopenharmony_ci{
373d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw = handle->private_data;
374d5ac70f0Sopenharmony_ci	ssize_t res = read(hw->fd, event, sizeof(*event));
375d5ac70f0Sopenharmony_ci	if (res <= 0)
376d5ac70f0Sopenharmony_ci		return -errno;
377d5ac70f0Sopenharmony_ci	if (CHECK_SANITY(res != sizeof(*event))) {
378d5ac70f0Sopenharmony_ci		SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)",
379d5ac70f0Sopenharmony_ci		       sizeof(*event), res);
380d5ac70f0Sopenharmony_ci		return -EINVAL;
381d5ac70f0Sopenharmony_ci	}
382d5ac70f0Sopenharmony_ci	return 1;
383d5ac70f0Sopenharmony_ci}
384d5ac70f0Sopenharmony_ci
385d5ac70f0Sopenharmony_cistatic const snd_ctl_ops_t snd_ctl_hw_ops = {
386d5ac70f0Sopenharmony_ci	.close = snd_ctl_hw_close,
387d5ac70f0Sopenharmony_ci	.nonblock = snd_ctl_hw_nonblock,
388d5ac70f0Sopenharmony_ci	.async = snd_ctl_hw_async,
389d5ac70f0Sopenharmony_ci	.subscribe_events = snd_ctl_hw_subscribe_events,
390d5ac70f0Sopenharmony_ci	.card_info = snd_ctl_hw_card_info,
391d5ac70f0Sopenharmony_ci	.element_list = snd_ctl_hw_elem_list,
392d5ac70f0Sopenharmony_ci	.element_info = snd_ctl_hw_elem_info,
393d5ac70f0Sopenharmony_ci	.element_add = snd_ctl_hw_elem_add,
394d5ac70f0Sopenharmony_ci	.element_replace = snd_ctl_hw_elem_replace,
395d5ac70f0Sopenharmony_ci	.element_remove = snd_ctl_hw_elem_remove,
396d5ac70f0Sopenharmony_ci	.element_read = snd_ctl_hw_elem_read,
397d5ac70f0Sopenharmony_ci	.element_write = snd_ctl_hw_elem_write,
398d5ac70f0Sopenharmony_ci	.element_lock = snd_ctl_hw_elem_lock,
399d5ac70f0Sopenharmony_ci	.element_unlock = snd_ctl_hw_elem_unlock,
400d5ac70f0Sopenharmony_ci	.element_tlv = snd_ctl_hw_elem_tlv,
401d5ac70f0Sopenharmony_ci	.hwdep_next_device = snd_ctl_hw_hwdep_next_device,
402d5ac70f0Sopenharmony_ci	.hwdep_info = snd_ctl_hw_hwdep_info,
403d5ac70f0Sopenharmony_ci	.pcm_next_device = snd_ctl_hw_pcm_next_device,
404d5ac70f0Sopenharmony_ci	.pcm_info = snd_ctl_hw_pcm_info,
405d5ac70f0Sopenharmony_ci	.pcm_prefer_subdevice = snd_ctl_hw_pcm_prefer_subdevice,
406d5ac70f0Sopenharmony_ci	.rawmidi_next_device = snd_ctl_hw_rawmidi_next_device,
407d5ac70f0Sopenharmony_ci	.rawmidi_info = snd_ctl_hw_rawmidi_info,
408d5ac70f0Sopenharmony_ci	.rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice,
409d5ac70f0Sopenharmony_ci	.ump_next_device = snd_ctl_hw_ump_next_device,
410d5ac70f0Sopenharmony_ci	.ump_endpoint_info = snd_ctl_hw_ump_endpoint_info,
411d5ac70f0Sopenharmony_ci	.ump_block_info = snd_ctl_hw_ump_block_info,
412d5ac70f0Sopenharmony_ci	.set_power_state = snd_ctl_hw_set_power_state,
413d5ac70f0Sopenharmony_ci	.get_power_state = snd_ctl_hw_get_power_state,
414d5ac70f0Sopenharmony_ci	.read = snd_ctl_hw_read,
415d5ac70f0Sopenharmony_ci};
416d5ac70f0Sopenharmony_ci
417d5ac70f0Sopenharmony_ci/**
418d5ac70f0Sopenharmony_ci * \brief Creates a new hw control
419d5ac70f0Sopenharmony_ci * \param handle Returns created control handle
420d5ac70f0Sopenharmony_ci * \param name Name of control device
421d5ac70f0Sopenharmony_ci * \param card Number of card
422d5ac70f0Sopenharmony_ci * \param mode Control mode
423d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
424d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
425d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
426d5ac70f0Sopenharmony_ci *          changed in future.
427d5ac70f0Sopenharmony_ci */
428d5ac70f0Sopenharmony_ciint snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)
429d5ac70f0Sopenharmony_ci{
430d5ac70f0Sopenharmony_ci	int fd, ver;
431d5ac70f0Sopenharmony_ci	char filename[sizeof(SNDRV_FILE_CONTROL) + 10];
432d5ac70f0Sopenharmony_ci	int fmode;
433d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
434d5ac70f0Sopenharmony_ci	snd_ctl_hw_t *hw;
435d5ac70f0Sopenharmony_ci	int err;
436d5ac70f0Sopenharmony_ci
437d5ac70f0Sopenharmony_ci	*handle = NULL;
438d5ac70f0Sopenharmony_ci
439d5ac70f0Sopenharmony_ci	if (CHECK_SANITY(card < 0 || card >= SND_MAX_CARDS)) {
440d5ac70f0Sopenharmony_ci		SNDMSG("Invalid card index %d", card);
441d5ac70f0Sopenharmony_ci		return -EINVAL;
442d5ac70f0Sopenharmony_ci	}
443d5ac70f0Sopenharmony_ci	sprintf(filename, SNDRV_FILE_CONTROL, card);
444d5ac70f0Sopenharmony_ci	if (mode & SND_CTL_READONLY)
445d5ac70f0Sopenharmony_ci		fmode = O_RDONLY;
446d5ac70f0Sopenharmony_ci	else
447d5ac70f0Sopenharmony_ci		fmode = O_RDWR;
448d5ac70f0Sopenharmony_ci	if (mode & SND_CTL_NONBLOCK)
449d5ac70f0Sopenharmony_ci		fmode |= O_NONBLOCK;
450d5ac70f0Sopenharmony_ci	if (mode & SND_CTL_ASYNC)
451d5ac70f0Sopenharmony_ci		fmode |= O_ASYNC;
452d5ac70f0Sopenharmony_ci	fd = snd_open_device(filename, fmode);
453d5ac70f0Sopenharmony_ci	if (fd < 0) {
454d5ac70f0Sopenharmony_ci		snd_card_load(card);
455d5ac70f0Sopenharmony_ci		fd = snd_open_device(filename, fmode);
456d5ac70f0Sopenharmony_ci		if (fd < 0)
457d5ac70f0Sopenharmony_ci			return -errno;
458d5ac70f0Sopenharmony_ci	}
459d5ac70f0Sopenharmony_ci	if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) {
460d5ac70f0Sopenharmony_ci		err = -errno;
461d5ac70f0Sopenharmony_ci		close(fd);
462d5ac70f0Sopenharmony_ci		return err;
463d5ac70f0Sopenharmony_ci	}
464d5ac70f0Sopenharmony_ci	if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) {
465d5ac70f0Sopenharmony_ci		close(fd);
466d5ac70f0Sopenharmony_ci		return -SND_ERROR_INCOMPATIBLE_VERSION;
467d5ac70f0Sopenharmony_ci	}
468d5ac70f0Sopenharmony_ci	hw = calloc(1, sizeof(snd_ctl_hw_t));
469d5ac70f0Sopenharmony_ci	if (hw == NULL) {
470d5ac70f0Sopenharmony_ci		close(fd);
471d5ac70f0Sopenharmony_ci		return -ENOMEM;
472d5ac70f0Sopenharmony_ci	}
473d5ac70f0Sopenharmony_ci	hw->card = card;
474d5ac70f0Sopenharmony_ci	hw->fd = fd;
475d5ac70f0Sopenharmony_ci	hw->protocol = ver;
476d5ac70f0Sopenharmony_ci
477d5ac70f0Sopenharmony_ci	err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name, mode);
478d5ac70f0Sopenharmony_ci	if (err < 0) {
479d5ac70f0Sopenharmony_ci		close(fd);
480d5ac70f0Sopenharmony_ci		free(hw);
481d5ac70f0Sopenharmony_ci		return err;
482d5ac70f0Sopenharmony_ci	}
483d5ac70f0Sopenharmony_ci	ctl->ops = &snd_ctl_hw_ops;
484d5ac70f0Sopenharmony_ci	ctl->private_data = hw;
485d5ac70f0Sopenharmony_ci	ctl->poll_fd = fd;
486d5ac70f0Sopenharmony_ci	*handle = ctl;
487d5ac70f0Sopenharmony_ci	return 0;
488d5ac70f0Sopenharmony_ci}
489d5ac70f0Sopenharmony_ci
490d5ac70f0Sopenharmony_ci/*! \page control_plugins
491d5ac70f0Sopenharmony_ci
492d5ac70f0Sopenharmony_ci\section control_plugins_hw Plugin: hw
493d5ac70f0Sopenharmony_ci
494d5ac70f0Sopenharmony_ciThis plugin communicates directly with the ALSA kernel driver. It is a raw
495d5ac70f0Sopenharmony_cicommunication without any conversions.
496d5ac70f0Sopenharmony_ci
497d5ac70f0Sopenharmony_ci\code
498d5ac70f0Sopenharmony_cicontrol.name {
499d5ac70f0Sopenharmony_ci	type hw			# Kernel PCM
500d5ac70f0Sopenharmony_ci	card INT/STR		# Card name (string) or number (integer)
501d5ac70f0Sopenharmony_ci}
502d5ac70f0Sopenharmony_ci\endcode
503d5ac70f0Sopenharmony_ci
504d5ac70f0Sopenharmony_ci\subsection control_plugins_hw_funcref Function reference
505d5ac70f0Sopenharmony_ci
506d5ac70f0Sopenharmony_ci<UL>
507d5ac70f0Sopenharmony_ci  <LI>snd_ctl_hw_open()
508d5ac70f0Sopenharmony_ci  <LI>_snd_ctl_hw_open()
509d5ac70f0Sopenharmony_ci</UL>
510d5ac70f0Sopenharmony_ci
511d5ac70f0Sopenharmony_ci*/
512d5ac70f0Sopenharmony_ci
513d5ac70f0Sopenharmony_ci/**
514d5ac70f0Sopenharmony_ci * \brief Creates a new hw control handle
515d5ac70f0Sopenharmony_ci * \param handlep Returns created control handle
516d5ac70f0Sopenharmony_ci * \param name Name of control device
517d5ac70f0Sopenharmony_ci * \param root Root configuration node
518d5ac70f0Sopenharmony_ci * \param conf Configuration node with hw PCM description
519d5ac70f0Sopenharmony_ci * \param mode Control Mode
520d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
521d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
522d5ac70f0Sopenharmony_ci *          changed in future.
523d5ac70f0Sopenharmony_ci */
524d5ac70f0Sopenharmony_ciint _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)
525d5ac70f0Sopenharmony_ci{
526d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
527d5ac70f0Sopenharmony_ci	long card = -1;
528d5ac70f0Sopenharmony_ci	int err;
529d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
530d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
531d5ac70f0Sopenharmony_ci		const char *id;
532d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
533d5ac70f0Sopenharmony_ci			continue;
534d5ac70f0Sopenharmony_ci		if (_snd_conf_generic_id(id))
535d5ac70f0Sopenharmony_ci			continue;
536d5ac70f0Sopenharmony_ci		if (strcmp(id, "card") == 0) {
537d5ac70f0Sopenharmony_ci			err = snd_config_get_card(n);
538d5ac70f0Sopenharmony_ci			if (err < 0)
539d5ac70f0Sopenharmony_ci				return err;
540d5ac70f0Sopenharmony_ci			card = err;
541d5ac70f0Sopenharmony_ci			continue;
542d5ac70f0Sopenharmony_ci		}
543d5ac70f0Sopenharmony_ci		return -EINVAL;
544d5ac70f0Sopenharmony_ci	}
545d5ac70f0Sopenharmony_ci	if (card < 0)
546d5ac70f0Sopenharmony_ci		return -EINVAL;
547d5ac70f0Sopenharmony_ci	return snd_ctl_hw_open(handlep, name, card, mode);
548d5ac70f0Sopenharmony_ci}
549d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
550d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION);
551d5ac70f0Sopenharmony_ci#endif
552