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