OSDN Git Service

Changed Jaroslav Kysela's e-mail from perex@suse.cz to perex@perex.cz
[android-x86/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 subscribe;
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         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_ADD, info) < 0)
152                 return -errno;
153         return 0;
154 }
155
156 static int snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
157 {
158         snd_ctl_hw_t *hw = handle->private_data;
159         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REPLACE, info) < 0)
160                 return -errno;
161         return 0;
162 }
163
164 static int snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
165 {
166         snd_ctl_hw_t *hw = handle->private_data;
167         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REMOVE, id) < 0)
168                 return -errno;
169         return 0;
170 }
171
172 static int snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
173 {
174         snd_ctl_hw_t *hw = handle->private_data;
175         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_READ, control) < 0)
176                 return -errno;
177         return 0;
178 }
179
180 static int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
181 {
182         snd_ctl_hw_t *hw = handle->private_data;
183         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) < 0)
184                 return -errno;
185         return 0;
186 }
187
188 static int snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
189 {
190         snd_ctl_hw_t *hw = handle->private_data;
191         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LOCK, id) < 0)
192                 return -errno;
193         return 0;
194 }
195
196 static int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
197 {
198         snd_ctl_hw_t *hw = handle->private_data;
199         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_UNLOCK, id) < 0)
200                 return -errno;
201         return 0;
202 }
203
204 static int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag,
205                                unsigned int numid,
206                                unsigned int *tlv, unsigned int tlv_size)
207 {
208         int inum;
209         snd_ctl_hw_t *hw = handle->private_data;
210         struct sndrv_ctl_tlv *xtlv;
211         
212         /* we don't support TLV on protocol ver 2.0.3 or earlier */
213         if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 4))
214                 return -ENXIO;
215
216         switch (op_flag) {
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;
221         }
222         xtlv = malloc(sizeof(struct sndrv_ctl_tlv) + tlv_size);
223         if (xtlv == NULL)
224                 return -ENOMEM; 
225         xtlv->numid = numid;
226         xtlv->length = tlv_size;
227         memcpy(xtlv->tlv, tlv, tlv_size);
228         if (ioctl(hw->fd, inum, xtlv) < 0) {
229                 free(xtlv);
230                 return -errno;
231         }
232         if (op_flag == 0) {
233                 if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size)
234                         return -EFAULT;
235                 memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int));
236         }
237         free(xtlv);
238         return 0;
239 }
240
241 static int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device)
242 {
243         snd_ctl_hw_t *hw = handle->private_data;
244         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, device) < 0)
245                 return -errno;
246         return 0;
247 }
248
249 static int snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info)
250 {
251         snd_ctl_hw_t *hw = handle->private_data;
252         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_INFO, info) < 0)
253                 return -errno;
254         return 0;
255 }
256
257 static int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device)
258 {
259         snd_ctl_hw_t *hw = handle->private_data;
260         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, device) < 0)
261                 return -errno;
262         return 0;
263 }
264
265 static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info)
266 {
267         snd_ctl_hw_t *hw = handle->private_data;
268         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0)
269                 return -errno;
270         return 0;
271 }
272
273 static int snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev)
274 {
275         snd_ctl_hw_t *hw = handle->private_data;
276         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdev) < 0)
277                 return -errno;
278         return 0;
279 }
280
281 static int snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device)
282 {
283         snd_ctl_hw_t *hw = handle->private_data;
284         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, device) < 0)
285                 return -errno;
286         return 0;
287 }
288
289 static int snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info)
290 {
291         snd_ctl_hw_t *hw = handle->private_data;
292         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, info) < 0)
293                 return -errno;
294         return 0;
295 }
296
297 static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev)
298 {
299         snd_ctl_hw_t *hw = handle->private_data;
300         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, &subdev) < 0)
301                 return -errno;
302         return 0;
303 }
304
305 static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state)
306 {
307         snd_ctl_hw_t *hw = handle->private_data;
308         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER, &state) < 0)
309                 return -errno;
310         return 0;
311 }
312
313 static int snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state)
314 {
315         snd_ctl_hw_t *hw = handle->private_data;
316         if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER_STATE, state) < 0)
317                 return -errno;
318         return 0;
319 }
320
321 static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)
322 {
323         snd_ctl_hw_t *hw = handle->private_data;
324         ssize_t res = read(hw->fd, event, sizeof(*event));
325         if (res <= 0)
326                 return -errno;
327         assert(res == sizeof(*event));
328         return 1;
329 }
330
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,
358 };
359
360 int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)
361 {
362         int fd, ver;
363         char filename[sizeof(SNDRV_FILE_CONTROL) + 10];
364         int fmode;
365         snd_ctl_t *ctl;
366         snd_ctl_hw_t *hw;
367         int err;
368
369         *handle = NULL; 
370
371         assert(card >= 0 && card < 32);
372         sprintf(filename, SNDRV_FILE_CONTROL, card);
373         if (mode & SND_CTL_READONLY)
374                 fmode = O_RDONLY;
375         else
376                 fmode = O_RDWR;
377         if (mode & SND_CTL_NONBLOCK)
378                 fmode |= O_NONBLOCK;
379         if (mode & SND_CTL_ASYNC)
380                 fmode |= O_ASYNC;
381         fd = snd_open_device(filename, fmode);
382         if (fd < 0) {
383                 snd_card_load(card);
384                 fd = snd_open_device(filename, fmode);
385                 if (fd < 0)
386                         return -errno;
387         }
388 #if 0
389         /*
390          * this is bogus, an application have to care about open filedescriptors
391          */
392         if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
393                 SYSERR("fcntl FD_CLOEXEC failed");
394                 err = -errno;
395                 close(fd);
396                 return err;
397         }
398 #endif
399         if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) {
400                 err = -errno;
401                 close(fd);
402                 return err;
403         }
404         if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) {
405                 close(fd);
406                 return -SND_ERROR_INCOMPATIBLE_VERSION;
407         }
408         hw = calloc(1, sizeof(snd_ctl_hw_t));
409         if (hw == NULL) {
410                 close(fd);
411                 return -ENOMEM;
412         }
413         hw->card = card;
414         hw->fd = fd;
415         hw->protocol = ver;
416
417         err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name);
418         if (err < 0) {
419                 close(fd);
420                 free(hw);
421         }
422         ctl->ops = &snd_ctl_hw_ops;
423         ctl->private_data = hw;
424         ctl->poll_fd = fd;
425         *handle = ctl;
426         return 0;
427 }
428
429 int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)
430 {
431         snd_config_iterator_t i, next;
432         long card = -1;
433         const char *str;
434         int err;
435         snd_config_for_each(i, next, conf) {
436                 snd_config_t *n = snd_config_iterator_entry(i);
437                 const char *id;
438                 if (snd_config_get_id(n, &id) < 0)
439                         continue;
440                 if (strcmp(id, "comment") == 0)
441                         continue;
442                 if (strcmp(id, "type") == 0)
443                         continue;
444                 if (strcmp(id, "card") == 0) {
445                         err = snd_config_get_integer(n, &card);
446                         if (err < 0) {
447                                 err = snd_config_get_string(n, &str);
448                                 if (err < 0)
449                                         return -EINVAL;
450                                 card = snd_card_get_index(str);
451                                 if (card < 0)
452                                         return card;
453                         }
454                         continue;
455                 }
456                 return -EINVAL;
457         }
458         if (card < 0)
459                 return -EINVAL;
460         return snd_ctl_hw_open(handlep, name, card, mode);
461 }
462 SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION);