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;
152 if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
153 hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
156 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_ADD, info) < 0)
161 static int snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
163 snd_ctl_hw_t *hw = handle->private_data;
165 if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
166 hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
169 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REPLACE, info) < 0)
174 static int snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
176 snd_ctl_hw_t *hw = handle->private_data;
177 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REMOVE, id) < 0)
182 static int snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
184 snd_ctl_hw_t *hw = handle->private_data;
185 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_READ, control) < 0)
190 static int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
192 snd_ctl_hw_t *hw = handle->private_data;
193 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) < 0)
198 static int snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
200 snd_ctl_hw_t *hw = handle->private_data;
201 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LOCK, id) < 0)
206 static int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
208 snd_ctl_hw_t *hw = handle->private_data;
209 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_UNLOCK, id) < 0)
214 static int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag,
216 unsigned int *tlv, unsigned int tlv_size)
219 snd_ctl_hw_t *hw = handle->private_data;
220 struct sndrv_ctl_tlv *xtlv;
222 /* we don't support TLV on protocol ver 2.0.3 or earlier */
223 if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 4))
227 case -1: inum = SNDRV_CTL_IOCTL_TLV_COMMAND; break;
228 case 0: inum = SNDRV_CTL_IOCTL_TLV_READ; break;
229 case 1: inum = SNDRV_CTL_IOCTL_TLV_WRITE; break;
230 default: return -EINVAL;
232 xtlv = malloc(sizeof(struct sndrv_ctl_tlv) + tlv_size);
236 xtlv->length = tlv_size;
237 memcpy(xtlv->tlv, tlv, tlv_size);
238 if (ioctl(hw->fd, inum, xtlv) < 0) {
243 if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size)
245 memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int));
251 static int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device)
253 snd_ctl_hw_t *hw = handle->private_data;
254 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, device) < 0)
259 static int snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info)
261 snd_ctl_hw_t *hw = handle->private_data;
262 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_INFO, info) < 0)
267 static int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device)
269 snd_ctl_hw_t *hw = handle->private_data;
270 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, device) < 0)
275 static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info)
277 snd_ctl_hw_t *hw = handle->private_data;
278 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0)
283 static int snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev)
285 snd_ctl_hw_t *hw = handle->private_data;
286 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdev) < 0)
291 static int snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device)
293 snd_ctl_hw_t *hw = handle->private_data;
294 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, device) < 0)
299 static int snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info)
301 snd_ctl_hw_t *hw = handle->private_data;
302 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, info) < 0)
307 static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev)
309 snd_ctl_hw_t *hw = handle->private_data;
310 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, &subdev) < 0)
315 static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state)
317 snd_ctl_hw_t *hw = handle->private_data;
318 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER, &state) < 0)
323 static int snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state)
325 snd_ctl_hw_t *hw = handle->private_data;
326 if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER_STATE, state) < 0)
331 static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)
333 snd_ctl_hw_t *hw = handle->private_data;
334 ssize_t res = read(hw->fd, event, sizeof(*event));
337 if (CHECK_SANITY(res != sizeof(*event))) {
338 SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)\n",
339 sizeof(*event), res);
345 static const snd_ctl_ops_t snd_ctl_hw_ops = {
346 .close = snd_ctl_hw_close,
347 .nonblock = snd_ctl_hw_nonblock,
348 .async = snd_ctl_hw_async,
349 .subscribe_events = snd_ctl_hw_subscribe_events,
350 .card_info = snd_ctl_hw_card_info,
351 .element_list = snd_ctl_hw_elem_list,
352 .element_info = snd_ctl_hw_elem_info,
353 .element_add = snd_ctl_hw_elem_add,
354 .element_replace = snd_ctl_hw_elem_replace,
355 .element_remove = snd_ctl_hw_elem_remove,
356 .element_read = snd_ctl_hw_elem_read,
357 .element_write = snd_ctl_hw_elem_write,
358 .element_lock = snd_ctl_hw_elem_lock,
359 .element_unlock = snd_ctl_hw_elem_unlock,
360 .element_tlv = snd_ctl_hw_elem_tlv,
361 .hwdep_next_device = snd_ctl_hw_hwdep_next_device,
362 .hwdep_info = snd_ctl_hw_hwdep_info,
363 .pcm_next_device = snd_ctl_hw_pcm_next_device,
364 .pcm_info = snd_ctl_hw_pcm_info,
365 .pcm_prefer_subdevice = snd_ctl_hw_pcm_prefer_subdevice,
366 .rawmidi_next_device = snd_ctl_hw_rawmidi_next_device,
367 .rawmidi_info = snd_ctl_hw_rawmidi_info,
368 .rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice,
369 .set_power_state = snd_ctl_hw_set_power_state,
370 .get_power_state = snd_ctl_hw_get_power_state,
371 .read = snd_ctl_hw_read,
374 int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)
377 char filename[sizeof(SNDRV_FILE_CONTROL) + 10];
385 if (CHECK_SANITY(card < 0 || card >= 32)) {
386 SNDMSG("Invalid card index %d", card);
389 sprintf(filename, SNDRV_FILE_CONTROL, card);
390 if (mode & SND_CTL_READONLY)
394 if (mode & SND_CTL_NONBLOCK)
396 if (mode & SND_CTL_ASYNC)
398 fd = snd_open_device(filename, fmode);
401 fd = snd_open_device(filename, fmode);
405 if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) {
410 if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) {
412 return -SND_ERROR_INCOMPATIBLE_VERSION;
414 hw = calloc(1, sizeof(snd_ctl_hw_t));
423 err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name);
429 ctl->ops = &snd_ctl_hw_ops;
430 ctl->private_data = hw;
436 int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)
438 snd_config_iterator_t i, next;
442 snd_config_for_each(i, next, conf) {
443 snd_config_t *n = snd_config_iterator_entry(i);
445 if (snd_config_get_id(n, &id) < 0)
447 if (strcmp(id, "comment") == 0)
449 if (strcmp(id, "type") == 0)
451 if (strcmp(id, "card") == 0) {
452 err = snd_config_get_integer(n, &card);
454 err = snd_config_get_string(n, &str);
457 card = snd_card_get_index(str);
467 return snd_ctl_hw_open(handlep, name, card, mode);
469 SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION);