2 * Control Interface - Hardware
3 * Copyright (c) 1998,1999,2000 by Jaroslav Kysela <perex@perex.cz>
4 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <sys/ioctl.h>
30 #include "control_local.h"
33 /* entry for static linking */
34 const char *_snd_module_control_hw = "";
42 #define SNDRV_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i"
43 #define SNDRV_CTL_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 4)
48 unsigned int protocol;
50 #endif /* DOC_HIDDEN */
52 static int snd_ctl_hw_close(snd_ctl_t *handle)
54 snd_ctl_hw_t *hw = handle->private_data;
56 res = close(hw->fd) < 0 ? -errno : 0;
61 static int snd_ctl_hw_nonblock(snd_ctl_t *handle, int nonblock)
63 snd_ctl_hw_t *hw = handle->private_data;
66 if ((flags = fcntl(fd, F_GETFL)) < 0) {
67 SYSERR("F_GETFL failed");
74 if (fcntl(fd, F_SETFL, flags) < 0) {
75 SYSERR("F_SETFL for O_NONBLOCK failed");
81 static int snd_ctl_hw_async(snd_ctl_t *ctl, int sig, pid_t pid)
84 snd_ctl_hw_t *hw = ctl->private_data;
87 if ((flags = fcntl(fd, F_GETFL)) < 0) {
88 SYSERR("F_GETFL failed");
95 if (fcntl(fd, F_SETFL, flags) < 0) {
96 SYSERR("F_SETFL for O_ASYNC failed");
101 if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
102 SYSERR("F_SETSIG failed");
105 if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
106 SYSERR("F_SETOWN failed");
112 static int snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe)
114 snd_ctl_hw_t *hw = handle->private_data;
115 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
116 SYSERR("SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS failed");
122 static int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)
124 snd_ctl_hw_t *hw = handle->private_data;
125 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, info) < 0) {
126 SYSERR("SNDRV_CTL_IOCTL_CARD_INFO failed");
132 static int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
134 snd_ctl_hw_t *hw = handle->private_data;
135 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LIST, list) < 0)
140 static int snd_ctl_hw_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
142 snd_ctl_hw_t *hw = handle->private_data;
143 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_INFO, info) < 0)
148 static int snd_ctl_hw_elem_add(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
150 snd_ctl_hw_t *hw = handle->private_data;
151 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_ADD, info) < 0)
156 static int snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
158 snd_ctl_hw_t *hw = handle->private_data;
159 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REPLACE, info) < 0)
164 static int snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
166 snd_ctl_hw_t *hw = handle->private_data;
167 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REMOVE, id) < 0)
172 static int snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
174 snd_ctl_hw_t *hw = handle->private_data;
175 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_READ, control) < 0)
180 static int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
182 snd_ctl_hw_t *hw = handle->private_data;
183 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) < 0)
188 static int snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
190 snd_ctl_hw_t *hw = handle->private_data;
191 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LOCK, id) < 0)
196 static int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
198 snd_ctl_hw_t *hw = handle->private_data;
199 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_UNLOCK, id) < 0)
204 static int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag,
206 unsigned int *tlv, unsigned int tlv_size)
209 snd_ctl_hw_t *hw = handle->private_data;
210 struct sndrv_ctl_tlv *xtlv;
212 /* we don't support TLV on protocol ver 2.0.3 or earlier */
213 if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 4))
217 case -1: inum = SNDRV_CTL_IOCTL_TLV_COMMAND; break;
218 case 0: inum = SNDRV_CTL_IOCTL_TLV_READ; break;
219 case 1: inum = SNDRV_CTL_IOCTL_TLV_WRITE; break;
220 default: return -EINVAL;
222 xtlv = malloc(sizeof(struct sndrv_ctl_tlv) + tlv_size);
226 xtlv->length = tlv_size;
227 memcpy(xtlv->tlv, tlv, tlv_size);
228 if (ioctl(hw->fd, inum, xtlv) < 0) {
233 if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size)
235 memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int));
241 static int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device)
243 snd_ctl_hw_t *hw = handle->private_data;
244 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, device) < 0)
249 static int snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info)
251 snd_ctl_hw_t *hw = handle->private_data;
252 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_INFO, info) < 0)
257 static int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device)
259 snd_ctl_hw_t *hw = handle->private_data;
260 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, device) < 0)
265 static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info)
267 snd_ctl_hw_t *hw = handle->private_data;
268 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0)
273 static int snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev)
275 snd_ctl_hw_t *hw = handle->private_data;
276 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdev) < 0)
281 static int snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device)
283 snd_ctl_hw_t *hw = handle->private_data;
284 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, device) < 0)
289 static int snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info)
291 snd_ctl_hw_t *hw = handle->private_data;
292 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, info) < 0)
297 static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev)
299 snd_ctl_hw_t *hw = handle->private_data;
300 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, &subdev) < 0)
305 static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state)
307 snd_ctl_hw_t *hw = handle->private_data;
308 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER, &state) < 0)
313 static int snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state)
315 snd_ctl_hw_t *hw = handle->private_data;
316 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER_STATE, state) < 0)
321 static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)
323 snd_ctl_hw_t *hw = handle->private_data;
324 ssize_t res = read(hw->fd, event, sizeof(*event));
327 assert(res == sizeof(*event));
331 snd_ctl_ops_t snd_ctl_hw_ops = {
332 .close = snd_ctl_hw_close,
333 .nonblock = snd_ctl_hw_nonblock,
334 .async = snd_ctl_hw_async,
335 .subscribe_events = snd_ctl_hw_subscribe_events,
336 .card_info = snd_ctl_hw_card_info,
337 .element_list = snd_ctl_hw_elem_list,
338 .element_info = snd_ctl_hw_elem_info,
339 .element_add = snd_ctl_hw_elem_add,
340 .element_replace = snd_ctl_hw_elem_replace,
341 .element_remove = snd_ctl_hw_elem_remove,
342 .element_read = snd_ctl_hw_elem_read,
343 .element_write = snd_ctl_hw_elem_write,
344 .element_lock = snd_ctl_hw_elem_lock,
345 .element_unlock = snd_ctl_hw_elem_unlock,
346 .element_tlv = snd_ctl_hw_elem_tlv,
347 .hwdep_next_device = snd_ctl_hw_hwdep_next_device,
348 .hwdep_info = snd_ctl_hw_hwdep_info,
349 .pcm_next_device = snd_ctl_hw_pcm_next_device,
350 .pcm_info = snd_ctl_hw_pcm_info,
351 .pcm_prefer_subdevice = snd_ctl_hw_pcm_prefer_subdevice,
352 .rawmidi_next_device = snd_ctl_hw_rawmidi_next_device,
353 .rawmidi_info = snd_ctl_hw_rawmidi_info,
354 .rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice,
355 .set_power_state = snd_ctl_hw_set_power_state,
356 .get_power_state = snd_ctl_hw_get_power_state,
357 .read = snd_ctl_hw_read,
360 int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)
363 char filename[sizeof(SNDRV_FILE_CONTROL) + 10];
371 assert(card >= 0 && card < 32);
372 sprintf(filename, SNDRV_FILE_CONTROL, card);
373 if (mode & SND_CTL_READONLY)
377 if (mode & SND_CTL_NONBLOCK)
379 if (mode & SND_CTL_ASYNC)
381 fd = snd_open_device(filename, fmode);
384 fd = snd_open_device(filename, fmode);
390 * this is bogus, an application have to care about open filedescriptors
392 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
393 SYSERR("fcntl FD_CLOEXEC failed");
399 if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) {
404 if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) {
406 return -SND_ERROR_INCOMPATIBLE_VERSION;
408 hw = calloc(1, sizeof(snd_ctl_hw_t));
417 err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name);
422 ctl->ops = &snd_ctl_hw_ops;
423 ctl->private_data = hw;
429 int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)
431 snd_config_iterator_t i, next;
435 snd_config_for_each(i, next, conf) {
436 snd_config_t *n = snd_config_iterator_entry(i);
438 if (snd_config_get_id(n, &id) < 0)
440 if (strcmp(id, "comment") == 0)
442 if (strcmp(id, "type") == 0)
444 if (strcmp(id, "card") == 0) {
445 err = snd_config_get_integer(n, &card);
447 err = snd_config_get_string(n, &str);
450 card = snd_card_get_index(str);
460 return snd_ctl_hw_open(handlep, name, card, mode);
462 SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION);