• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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