1 /*
2 * Hardware dependent Interface - main file for hardware access
3 * Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
4 *
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28 #include "hwdep_local.h"
29
30 #ifndef PIC
31 /* entry for static linking */
32 const char *_snd_module_hwdep_hw = "";
33 #endif
34
35 #define SNDRV_FILE_HWDEP ALSA_DEVICE_DIRECTORY "hwC%iD%i"
36 #define SNDRV_HWDEP_VERSION_MAX SNDRV_PROTOCOL_VERSION(1, 0, 1)
37
snd_hwdep_hw_close(snd_hwdep_t * hwdep)38 static int snd_hwdep_hw_close(snd_hwdep_t *hwdep)
39 {
40 int res;
41 assert(hwdep);
42 res = close(hwdep->poll_fd) < 0 ? -errno : 0;
43 return res;
44 }
45
snd_hwdep_hw_nonblock(snd_hwdep_t * hwdep,int nonblock)46 static int snd_hwdep_hw_nonblock(snd_hwdep_t *hwdep, int nonblock)
47 {
48 long flags;
49 assert(hwdep);
50 if ((flags = fcntl(hwdep->poll_fd, F_GETFL)) < 0)
51 return -errno;
52 if (nonblock)
53 flags |= O_NONBLOCK;
54 else
55 flags &= ~O_NONBLOCK;
56 if (fcntl(hwdep->poll_fd, F_SETFL, flags) < 0)
57 return -errno;
58 return 0;
59 }
60
snd_hwdep_hw_info(snd_hwdep_t * hwdep,snd_hwdep_info_t * info)61 static int snd_hwdep_hw_info(snd_hwdep_t *hwdep, snd_hwdep_info_t *info)
62 {
63 assert(hwdep && info);
64 if (ioctl(hwdep->poll_fd, SNDRV_HWDEP_IOCTL_INFO, info) < 0)
65 return -errno;
66 return 0;
67 }
68
snd_hwdep_hw_ioctl(snd_hwdep_t * hwdep,unsigned int request,void * arg)69 static int snd_hwdep_hw_ioctl(snd_hwdep_t *hwdep, unsigned int request, void * arg)
70 {
71 assert(hwdep);
72 if (ioctl(hwdep->poll_fd, request, arg) < 0)
73 return -errno;
74 return 0;
75 }
76
snd_hwdep_hw_write(snd_hwdep_t * hwdep,const void * buffer,size_t size)77 static ssize_t snd_hwdep_hw_write(snd_hwdep_t *hwdep, const void *buffer, size_t size)
78 {
79 ssize_t result;
80 assert(hwdep && (buffer || size == 0));
81 result = write(hwdep->poll_fd, buffer, size);
82 if (result < 0)
83 return -errno;
84 return result;
85 }
86
snd_hwdep_hw_read(snd_hwdep_t * hwdep,void * buffer,size_t size)87 static ssize_t snd_hwdep_hw_read(snd_hwdep_t *hwdep, void *buffer, size_t size)
88 {
89 ssize_t result;
90 assert(hwdep && (buffer || size == 0));
91 result = read(hwdep->poll_fd, buffer, size);
92 if (result < 0)
93 return -errno;
94 return result;
95 }
96
97 static const snd_hwdep_ops_t snd_hwdep_hw_ops = {
98 .close = snd_hwdep_hw_close,
99 .nonblock = snd_hwdep_hw_nonblock,
100 .info = snd_hwdep_hw_info,
101 .ioctl = snd_hwdep_hw_ioctl,
102 .write = snd_hwdep_hw_write,
103 .read = snd_hwdep_hw_read,
104 };
105
snd_hwdep_hw_open(snd_hwdep_t ** handle,const char * name,int card,int device,int mode)106 int snd_hwdep_hw_open(snd_hwdep_t **handle, const char *name, int card, int device, int mode)
107 {
108 int fd, ver, ret;
109 char filename[sizeof(SNDRV_FILE_HWDEP) + 20];
110 snd_hwdep_t *hwdep;
111 assert(handle);
112
113 *handle = NULL;
114
115 if (card < 0 || card >= SND_MAX_CARDS)
116 return -EINVAL;
117 sprintf(filename, SNDRV_FILE_HWDEP, card, device);
118 fd = snd_open_device(filename, mode);
119 if (fd < 0) {
120 snd_card_load(card);
121 fd = snd_open_device(filename, mode);
122 if (fd < 0)
123 return -errno;
124 }
125 if (ioctl(fd, SNDRV_HWDEP_IOCTL_PVERSION, &ver) < 0) {
126 ret = -errno;
127 close(fd);
128 return ret;
129 }
130 if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_HWDEP_VERSION_MAX)) {
131 close(fd);
132 return -SND_ERROR_INCOMPATIBLE_VERSION;
133 }
134 hwdep = (snd_hwdep_t *) calloc(1, sizeof(snd_hwdep_t));
135 if (hwdep == NULL) {
136 close(fd);
137 return -ENOMEM;
138 }
139 hwdep->name = strdup(name);
140 hwdep->poll_fd = fd;
141 hwdep->mode = mode;
142 hwdep->type = SND_HWDEP_TYPE_HW;
143 hwdep->ops = &snd_hwdep_hw_ops;
144 *handle = hwdep;
145 return 0;
146 }
147
_snd_hwdep_hw_open(snd_hwdep_t ** hwdep,char * name,snd_config_t * root ATTRIBUTE_UNUSED,snd_config_t * conf,int mode)148 int _snd_hwdep_hw_open(snd_hwdep_t **hwdep, char *name,
149 snd_config_t *root ATTRIBUTE_UNUSED,
150 snd_config_t *conf, int mode)
151 {
152 snd_config_iterator_t i, next;
153 long card = -1, device = 0;
154 int err;
155 snd_config_for_each(i, next, conf) {
156 snd_config_t *n = snd_config_iterator_entry(i);
157 const char *id;
158 if (snd_config_get_id(n, &id) < 0)
159 continue;
160 if (_snd_conf_generic_id(id))
161 continue;
162 if (strcmp(id, "card") == 0) {
163 err = snd_config_get_card(n);
164 if (err < 0)
165 return err;
166 card = err;
167 continue;
168 }
169 if (strcmp(id, "device") == 0) {
170 err = snd_config_get_integer(n, &device);
171 if (err < 0)
172 return err;
173 continue;
174 }
175 SNDERR("Unexpected field %s", id);
176 return -EINVAL;
177 }
178 if (card < 0)
179 return -EINVAL;
180 return snd_hwdep_hw_open(hwdep, name, card, device, mode);
181 }
182 SND_DLSYM_BUILD_VERSION(_snd_hwdep_hw_open, SND_HWDEP_DLSYM_VERSION);
183