tizen 2.3.1 release
[external/alsa-lib.git] / src / control / control_hw.c
1 /*
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>
5  *
6  *
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.
11  *
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.
16  *
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
20  *
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <signal.h>
27 #include <string.h>
28 #include <fcntl.h>
29 #include <sys/ioctl.h>
30 #include "control_local.h"
31
32 #ifndef PIC
33 /* entry for static linking */
34 const char *_snd_module_control_hw = "";
35 #endif
36
37 #ifndef F_SETSIG
38 #define F_SETSIG 10
39 #endif
40
41 #ifndef DOC_HIDDEN
42 #define SNDRV_FILE_CONTROL      ALSA_DEVICE_DIRECTORY "controlC%i"
43 #define SNDRV_CTL_VERSION_MAX   SNDRV_PROTOCOL_VERSION(2, 0, 4)
44
45 typedef struct {
46         int card;
47         int fd;
48         unsigned int protocol;
49 } snd_ctl_hw_t;
50 #endif /* DOC_HIDDEN */
51
52 static int snd_ctl_hw_close(snd_ctl_t *handle)
53 {
54         snd_ctl_hw_t *hw = handle->private_data;
55         int res;
56         res = close(hw->fd) < 0 ? -errno : 0;
57         free(hw);
58         return res;
59 }
60
61 static int snd_ctl_hw_nonblock(snd_ctl_t *handle, int nonblock)
62 {
63         snd_ctl_hw_t *hw = handle->private_data;
64         long flags;
65         int fd = hw->fd;
66         if ((flags = fcntl(fd, F_GETFL)) < 0) {
67                 SYSERR("F_GETFL failed");
68                 return -errno;
69         }
70         if (nonblock)
71                 flags |= O_NONBLOCK;
72         else
73                 flags &= ~O_NONBLOCK;
74         if (fcntl(fd, F_SETFL, flags) < 0) {
75                 SYSERR("F_SETFL for O_NONBLOCK failed");
76                 return -errno;
77         }
78         return 0;
79 }
80
81 static int snd_ctl_hw_async(snd_ctl_t *ctl, int sig, pid_t pid)
82 {
83         long flags;
84         snd_ctl_hw_t *hw = ctl->private_data;
85         int fd = hw->fd;
86
87         if ((flags = fcntl(fd, F_GETFL)) < 0) {
88                 SYSERR("F_GETFL failed");
89                 return -errno;
90         }
91         if (sig >= 0)
92                 flags |= O_ASYNC;
93         else
94                 flags &= ~O_ASYNC;
95         if (fcntl(fd, F_SETFL, flags) < 0) {
96                 SYSERR("F_SETFL for O_ASYNC failed");
97                 return -errno;
98         }
99         if (sig < 0)
100                 return 0;
101         if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
102                 SYSERR("F_SETSIG failed");
103                 return -errno;
104         }
105         if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
106                 SYSERR("F_SETOWN failed");
107                 return -errno;
108         }
109         return 0;
110 }
111
112 static int snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe)
113 {
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");
117                 return -errno;
118         }
119         return 0;
120 }
121
122 static int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)
123 {
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");
127                 return -errno;
128         }
129         return 0;
130 }
131
132 static int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
133 {
134         snd_ctl_hw_t *hw = handle->private_data;
135         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LIST, list) < 0)
136                 return -errno;
137         return 0;
138 }
139
140 static int snd_ctl_hw_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
141 {
142         snd_ctl_hw_t *hw = handle->private_data;
143         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_INFO, info) < 0)
144                 return -errno;
145         return 0;
146 }
147
148 static int snd_ctl_hw_elem_add(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
149 {
150         snd_ctl_hw_t *hw = handle->private_data;
151
152         if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
153             hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
154                 return -ENXIO;
155
156         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_ADD, info) < 0)
157                 return -errno;
158         return 0;
159 }
160
161 static int snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
162 {
163         snd_ctl_hw_t *hw = handle->private_data;
164
165         if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
166             hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
167                 return -ENXIO;
168
169         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REPLACE, info) < 0)
170                 return -errno;
171         return 0;
172 }
173
174 static int snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
175 {
176         snd_ctl_hw_t *hw = handle->private_data;
177         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REMOVE, id) < 0)
178                 return -errno;
179         return 0;
180 }
181
182 static int snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
183 {
184         snd_ctl_hw_t *hw = handle->private_data;
185         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_READ, control) < 0)
186                 return -errno;
187         return 0;
188 }
189
190 static int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
191 {
192         snd_ctl_hw_t *hw = handle->private_data;
193         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) < 0)
194                 return -errno;
195         return 0;
196 }
197
198 static int snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
199 {
200         snd_ctl_hw_t *hw = handle->private_data;
201         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LOCK, id) < 0)
202                 return -errno;
203         return 0;
204 }
205
206 static int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
207 {
208         snd_ctl_hw_t *hw = handle->private_data;
209         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_UNLOCK, id) < 0)
210                 return -errno;
211         return 0;
212 }
213
214 static int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag,
215                                unsigned int numid,
216                                unsigned int *tlv, unsigned int tlv_size)
217 {
218         int inum;
219         snd_ctl_hw_t *hw = handle->private_data;
220         struct sndrv_ctl_tlv *xtlv;
221         
222         /* we don't support TLV on protocol ver 2.0.3 or earlier */
223         if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 4))
224                 return -ENXIO;
225
226         switch (op_flag) {
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;
231         }
232         xtlv = malloc(sizeof(struct sndrv_ctl_tlv) + tlv_size);
233         if (xtlv == NULL)
234                 return -ENOMEM; 
235         xtlv->numid = numid;
236         xtlv->length = tlv_size;
237         memcpy(xtlv->tlv, tlv, tlv_size);
238         if (ioctl(hw->fd, inum, xtlv) < 0) {
239                 free(xtlv);
240                 return -errno;
241         }
242         if (op_flag == 0) {
243                 if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size)
244                         return -EFAULT;
245                 memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int));
246         }
247         free(xtlv);
248         return 0;
249 }
250
251 static int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device)
252 {
253         snd_ctl_hw_t *hw = handle->private_data;
254         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, device) < 0)
255                 return -errno;
256         return 0;
257 }
258
259 static int snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info)
260 {
261         snd_ctl_hw_t *hw = handle->private_data;
262         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_INFO, info) < 0)
263                 return -errno;
264         return 0;
265 }
266
267 static int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device)
268 {
269         snd_ctl_hw_t *hw = handle->private_data;
270         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, device) < 0)
271                 return -errno;
272         return 0;
273 }
274
275 static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info)
276 {
277         snd_ctl_hw_t *hw = handle->private_data;
278         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0)
279                 return -errno;
280         return 0;
281 }
282
283 static int snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev)
284 {
285         snd_ctl_hw_t *hw = handle->private_data;
286         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdev) < 0)
287                 return -errno;
288         return 0;
289 }
290
291 static int snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device)
292 {
293         snd_ctl_hw_t *hw = handle->private_data;
294         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, device) < 0)
295                 return -errno;
296         return 0;
297 }
298
299 static int snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info)
300 {
301         snd_ctl_hw_t *hw = handle->private_data;
302         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, info) < 0)
303                 return -errno;
304         return 0;
305 }
306
307 static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev)
308 {
309         snd_ctl_hw_t *hw = handle->private_data;
310         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, &subdev) < 0)
311                 return -errno;
312         return 0;
313 }
314
315 static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state)
316 {
317         snd_ctl_hw_t *hw = handle->private_data;
318         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER, &state) < 0)
319                 return -errno;
320         return 0;
321 }
322
323 static int snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state)
324 {
325         snd_ctl_hw_t *hw = handle->private_data;
326         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER_STATE, state) < 0)
327                 return -errno;
328         return 0;
329 }
330
331 static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)
332 {
333         snd_ctl_hw_t *hw = handle->private_data;
334         ssize_t res = read(hw->fd, event, sizeof(*event));
335         if (res <= 0)
336                 return -errno;
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);
340                 return -EINVAL;
341         }
342         return 1;
343 }
344
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,
372 };
373
374 int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)
375 {
376         int fd, ver;
377         char filename[sizeof(SNDRV_FILE_CONTROL) + 10];
378         int fmode;
379         snd_ctl_t *ctl;
380         snd_ctl_hw_t *hw;
381         int err;
382
383         *handle = NULL; 
384
385         if (CHECK_SANITY(card < 0 || card >= 32)) {
386                 SNDMSG("Invalid card index %d", card);
387                 return -EINVAL;
388         }
389         sprintf(filename, SNDRV_FILE_CONTROL, card);
390         if (mode & SND_CTL_READONLY)
391                 fmode = O_RDONLY;
392         else
393                 fmode = O_RDWR;
394         if (mode & SND_CTL_NONBLOCK)
395                 fmode |= O_NONBLOCK;
396         if (mode & SND_CTL_ASYNC)
397                 fmode |= O_ASYNC;
398         fd = snd_open_device(filename, fmode);
399         if (fd < 0) {
400                 snd_card_load(card);
401                 fd = snd_open_device(filename, fmode);
402                 if (fd < 0)
403                         return -errno;
404         }
405         if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) {
406                 err = -errno;
407                 close(fd);
408                 return err;
409         }
410         if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) {
411                 close(fd);
412                 return -SND_ERROR_INCOMPATIBLE_VERSION;
413         }
414         hw = calloc(1, sizeof(snd_ctl_hw_t));
415         if (hw == NULL) {
416                 close(fd);
417                 return -ENOMEM;
418         }
419         hw->card = card;
420         hw->fd = fd;
421         hw->protocol = ver;
422
423         err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name);
424         if (err < 0) {
425                 close(fd);
426                 free(hw);
427                 return err;
428         }
429         ctl->ops = &snd_ctl_hw_ops;
430         ctl->private_data = hw;
431         ctl->poll_fd = fd;
432         *handle = ctl;
433         return 0;
434 }
435
436 int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)
437 {
438         snd_config_iterator_t i, next;
439         long card = -1;
440         const char *str;
441         int err;
442         snd_config_for_each(i, next, conf) {
443                 snd_config_t *n = snd_config_iterator_entry(i);
444                 const char *id;
445                 if (snd_config_get_id(n, &id) < 0)
446                         continue;
447                 if (strcmp(id, "comment") == 0)
448                         continue;
449                 if (strcmp(id, "type") == 0)
450                         continue;
451                 if (strcmp(id, "card") == 0) {
452                         err = snd_config_get_integer(n, &card);
453                         if (err < 0) {
454                                 err = snd_config_get_string(n, &str);
455                                 if (err < 0)
456                                         return -EINVAL;
457                                 card = snd_card_get_index(str);
458                                 if (card < 0)
459                                         return card;
460                         }
461                         continue;
462                 }
463                 return -EINVAL;
464         }
465         if (card < 0)
466                 return -EINVAL;
467         return snd_ctl_hw_open(handlep, name, card, mode);
468 }
469 SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION);