]>
Commit | Line | Data |
---|---|---|
6ed4f044 | 1 | /* |
6944b2d0 | 2 | * This file is part of the libsigrok project. |
6ed4f044 DR |
3 | * |
4 | * Copyright (C) 2011 Daniel Ribeiro <drwyrm@gmail.com> | |
0254651d | 5 | * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de> |
6944b2d0 | 6 | * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com> |
6ed4f044 DR |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | */ | |
22 | ||
9cd9f6b7 | 23 | #include "protocol.h" |
45c59c8b BV |
24 | #include "libsigrok.h" |
25 | #include "libsigrok-internal.h" | |
6ed4f044 | 26 | |
9cd9f6b7 AG |
27 | #include <stdlib.h> |
28 | #include <unistd.h> | |
29 | #include <string.h> | |
472bbb46 | 30 | |
9cd9f6b7 | 31 | #define DEFAULT_PROBES 2 |
0254651d UH |
32 | #define SAMPLE_WIDTH 16 |
33 | #define DEFAULT_SAMPLERATE 44100 | |
a4cfb10f | 34 | |
915f7cc8 | 35 | static const int hwcaps[] = { |
5a2326a7 UH |
36 | SR_HWCAP_SAMPLERATE, |
37 | SR_HWCAP_LIMIT_SAMPLES, | |
38 | SR_HWCAP_CONTINUOUS, | |
0254651d | 39 | 0, |
6ed4f044 DR |
40 | }; |
41 | ||
0254651d | 42 | static const char *probe_names[] = { |
78693401 | 43 | "Channel 0", "Channel 1", |
464d12c7 KS |
44 | NULL, |
45 | }; | |
46 | ||
0254651d UH |
47 | SR_PRIV struct sr_dev_driver alsa_driver_info; |
48 | static struct sr_dev_driver *di = &alsa_driver_info; | |
6ed4f044 | 49 | |
0254651d UH |
50 | static int clear_instances(void) |
51 | { | |
6944b2d0 AG |
52 | struct drv_context *drvc; |
53 | ||
54 | if (!(drvc = di->priv)) | |
55 | return SR_OK; | |
56 | ||
57 | g_slist_free_full(drvc->instances, (GDestroyNotify)alsa_dev_inst_clear); | |
58 | drvc->instances = NULL; | |
0254651d UH |
59 | |
60 | return SR_OK; | |
61 | } | |
62 | ||
34f06b90 | 63 | static int hw_init(struct sr_context *sr_ctx) |
6ed4f044 | 64 | { |
0254651d | 65 | struct drv_context *drvc; |
6ed4f044 | 66 | |
0254651d UH |
67 | if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) { |
68 | sr_err("Driver context malloc failed."); | |
886a52b6 | 69 | return SR_ERR_MALLOC; |
92b3101c | 70 | } |
6ed4f044 | 71 | |
0254651d UH |
72 | drvc->sr_ctx = sr_ctx; |
73 | di->priv = drvc; | |
74 | ||
75 | return SR_OK; | |
76 | } | |
77 | ||
78 | static GSList *hw_scan(GSList *options) | |
79 | { | |
6944b2d0 | 80 | return alsa_scan(options, di); |
6ed4f044 DR |
81 | } |
82 | ||
0254651d | 83 | static GSList *hw_dev_list(void) |
6ed4f044 | 84 | { |
0254651d UH |
85 | struct drv_context *drvc; |
86 | ||
87 | drvc = di->priv; | |
88 | ||
89 | return drvc->instances; | |
90 | } | |
91 | ||
92 | static int hw_dev_open(struct sr_dev_inst *sdi) | |
93 | { | |
94 | struct dev_context *devc; | |
ebc34738 | 95 | int ret; |
6ed4f044 | 96 | |
0254651d | 97 | devc = sdi->priv; |
6ed4f044 | 98 | |
6944b2d0 AG |
99 | if (!(devc->hwdev)) { |
100 | sr_err("devc->hwdev was NULL."); | |
101 | return SR_ERR_BUG; | |
102 | } | |
103 | ||
104 | sr_dbg("Opening audio device '%s' for stream capture.", devc->hwdev); | |
105 | ret = snd_pcm_open(&devc->capture_handle, devc->hwdev, | |
ea9cfed7 | 106 | SND_PCM_STREAM_CAPTURE, 0); |
ebc34738 | 107 | if (ret < 0) { |
0254651d | 108 | sr_err("Can't open audio device: %s.", snd_strerror(ret)); |
e46b8fb1 | 109 | return SR_ERR; |
6ed4f044 DR |
110 | } |
111 | ||
0254651d UH |
112 | sr_dbg("Initializing hardware parameter structure."); |
113 | ret = snd_pcm_hw_params_any(devc->capture_handle, devc->hw_params); | |
ebc34738 | 114 | if (ret < 0) { |
0254651d | 115 | sr_err("Can't initialize hardware parameter structure: %s.", |
472bbb46 | 116 | snd_strerror(ret)); |
e46b8fb1 | 117 | return SR_ERR; |
6ed4f044 DR |
118 | } |
119 | ||
e46b8fb1 | 120 | return SR_OK; |
6ed4f044 DR |
121 | } |
122 | ||
0254651d | 123 | static int hw_dev_close(struct sr_dev_inst *sdi) |
6ed4f044 | 124 | { |
0254651d UH |
125 | int ret; |
126 | struct dev_context *devc; | |
6ed4f044 | 127 | |
0254651d UH |
128 | devc = sdi->priv; |
129 | ||
130 | sr_dbg("Closing device."); | |
6ed4f044 | 131 | |
0254651d UH |
132 | if (devc->capture_handle) { |
133 | sr_dbg("Closing PCM device."); | |
134 | if ((ret = snd_pcm_close(devc->capture_handle)) < 0) { | |
135 | sr_err("Failed to close device: %s.", | |
136 | snd_strerror(ret)); | |
6944b2d0 | 137 | devc->capture_handle = NULL; |
0254651d UH |
138 | } |
139 | } else { | |
140 | sr_dbg("No capture handle, no need to close audio device."); | |
141 | } | |
697785d1 UH |
142 | |
143 | return SR_OK; | |
6ed4f044 DR |
144 | } |
145 | ||
57ab7d9f | 146 | static int hw_cleanup(void) |
6ed4f044 | 147 | { |
0254651d | 148 | clear_instances(); |
57ab7d9f UH |
149 | |
150 | return SR_OK; | |
6ed4f044 DR |
151 | } |
152 | ||
0254651d UH |
153 | static int hw_info_get(int info_id, const void **data, |
154 | const struct sr_dev_inst *sdi) | |
6ed4f044 | 155 | { |
0254651d | 156 | struct dev_context *devc; |
6ed4f044 | 157 | |
0254651d UH |
158 | if (info_id != SR_DI_HWCAPS) /* For SR_DI_HWCAPS sdi will be NULL. */ |
159 | devc = sdi->priv; | |
6ed4f044 | 160 | |
0254651d UH |
161 | switch (info_id) { |
162 | case SR_DI_HWCAPS: | |
163 | *data = hwcaps; | |
6ed4f044 | 164 | break; |
5a2326a7 | 165 | case SR_DI_NUM_PROBES: |
6944b2d0 | 166 | *data = &devc->num_probes; |
6ed4f044 | 167 | break; |
464d12c7 | 168 | case SR_DI_PROBE_NAMES: |
0254651d | 169 | *data = probe_names; |
464d12c7 | 170 | break; |
5a2326a7 | 171 | case SR_DI_CUR_SAMPLERATE: |
0254651d | 172 | *data = &devc->cur_samplerate; |
6ed4f044 | 173 | break; |
0254651d UH |
174 | default: |
175 | sr_err("Invalid info_id: %d.", info_id); | |
176 | return SR_ERR_ARG; | |
6ed4f044 DR |
177 | } |
178 | ||
0254651d | 179 | return SR_OK; |
6ed4f044 DR |
180 | } |
181 | ||
0254651d UH |
182 | static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap, |
183 | const void *value) | |
6ed4f044 | 184 | { |
0254651d | 185 | struct dev_context *devc; |
6ed4f044 | 186 | |
0254651d | 187 | devc = sdi->priv; |
6ed4f044 | 188 | |
ffedd0bf | 189 | switch (hwcap) { |
5a2326a7 | 190 | case SR_HWCAP_SAMPLERATE: |
0254651d UH |
191 | devc->cur_samplerate = *(const uint64_t *)value; |
192 | break; | |
5a2326a7 | 193 | case SR_HWCAP_LIMIT_SAMPLES: |
0254651d UH |
194 | devc->limit_samples = *(const uint64_t *)value; |
195 | break; | |
6ed4f044 | 196 | default: |
0254651d | 197 | sr_err("Unknown capability: %d.", hwcap); |
e46b8fb1 | 198 | return SR_ERR; |
6ed4f044 | 199 | } |
0254651d UH |
200 | |
201 | return SR_OK; | |
6ed4f044 DR |
202 | } |
203 | ||
0254651d UH |
204 | static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, |
205 | void *cb_data) | |
6ed4f044 | 206 | { |
b9c735a2 UH |
207 | struct sr_datafeed_packet packet; |
208 | struct sr_datafeed_header header; | |
0254651d UH |
209 | struct sr_datafeed_meta_analog meta; |
210 | struct dev_context *devc; | |
211 | int count, ret; | |
6ed4f044 | 212 | |
0254651d UH |
213 | devc = sdi->priv; |
214 | devc->cb_data = cb_data; | |
6ed4f044 | 215 | |
0254651d UH |
216 | sr_dbg("Setting audio access type to RW/interleaved."); |
217 | ret = snd_pcm_hw_params_set_access(devc->capture_handle, | |
218 | devc->hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); | |
ebc34738 | 219 | if (ret < 0) { |
0254651d | 220 | sr_err("Can't set audio access type: %s.", snd_strerror(ret)); |
e46b8fb1 | 221 | return SR_ERR; |
6ed4f044 DR |
222 | } |
223 | ||
0254651d UH |
224 | /* FIXME: Hardcoded for 16bits. */ |
225 | sr_dbg("Setting audio sample format to signed 16bit (little endian)."); | |
226 | ret = snd_pcm_hw_params_set_format(devc->capture_handle, | |
227 | devc->hw_params, SND_PCM_FORMAT_S16_LE); | |
ebc34738 | 228 | if (ret < 0) { |
0254651d | 229 | sr_err("Can't set audio sample format: %s.", snd_strerror(ret)); |
e46b8fb1 | 230 | return SR_ERR; |
6ed4f044 DR |
231 | } |
232 | ||
0254651d UH |
233 | sr_dbg("Setting audio samplerate to %" PRIu64 "Hz.", |
234 | devc->cur_samplerate); | |
0d6ff103 AG |
235 | ret = snd_pcm_hw_params_set_rate(devc->capture_handle, devc->hw_params, |
236 | (unsigned int)devc->cur_samplerate, 0); | |
ebc34738 | 237 | if (ret < 0) { |
0254651d | 238 | sr_err("Can't set audio sample rate: %s.", snd_strerror(ret)); |
e46b8fb1 | 239 | return SR_ERR; |
6ed4f044 DR |
240 | } |
241 | ||
9cd9f6b7 | 242 | sr_dbg("Setting audio channel count to %d.", devc->num_probes); |
0254651d | 243 | ret = snd_pcm_hw_params_set_channels(devc->capture_handle, |
9cd9f6b7 | 244 | devc->hw_params, devc->num_probes); |
ebc34738 | 245 | if (ret < 0) { |
0254651d | 246 | sr_err("Can't set channel count: %s.", snd_strerror(ret)); |
e46b8fb1 | 247 | return SR_ERR; |
6ed4f044 DR |
248 | } |
249 | ||
0254651d UH |
250 | sr_dbg("Setting audio parameters."); |
251 | ret = snd_pcm_hw_params(devc->capture_handle, devc->hw_params); | |
ebc34738 | 252 | if (ret < 0) { |
0254651d | 253 | sr_err("Can't set parameters: %s.", snd_strerror(ret)); |
e46b8fb1 | 254 | return SR_ERR; |
6ed4f044 DR |
255 | } |
256 | ||
0254651d UH |
257 | sr_dbg("Preparing audio interface for use."); |
258 | ret = snd_pcm_prepare(devc->capture_handle); | |
ebc34738 | 259 | if (ret < 0) { |
0254651d | 260 | sr_err("Can't prepare audio interface for use: %s.", |
ebc34738 | 261 | snd_strerror(ret)); |
e46b8fb1 | 262 | return SR_ERR; |
6ed4f044 DR |
263 | } |
264 | ||
0254651d | 265 | count = snd_pcm_poll_descriptors_count(devc->capture_handle); |
6ed4f044 | 266 | if (count < 1) { |
472bbb46 | 267 | sr_err("Unable to obtain poll descriptors count."); |
e46b8fb1 | 268 | return SR_ERR; |
6ed4f044 | 269 | } |
0254651d | 270 | sr_spew("Obtained poll descriptor count: %d.", count); |
6ed4f044 | 271 | |
0254651d UH |
272 | if (!(devc->ufds = g_try_malloc(count * sizeof(struct pollfd)))) { |
273 | sr_err("Failed to malloc ufds."); | |
e46b8fb1 | 274 | return SR_ERR_MALLOC; |
92b3101c | 275 | } |
6ed4f044 | 276 | |
0254651d UH |
277 | sr_err("Getting %d poll descriptors.", count); |
278 | ret = snd_pcm_poll_descriptors(devc->capture_handle, devc->ufds, count); | |
ebc34738 | 279 | if (ret < 0) { |
0254651d | 280 | sr_err("Unable to obtain poll descriptors: %s.", |
ebc34738 | 281 | snd_strerror(ret)); |
0254651d | 282 | g_free(devc->ufds); |
e46b8fb1 | 283 | return SR_ERR; |
6ed4f044 DR |
284 | } |
285 | ||
0254651d UH |
286 | /* Send header packet to the session bus. */ |
287 | sr_dbg("Sending SR_DF_HEADER packet."); | |
5a2326a7 | 288 | packet.type = SR_DF_HEADER; |
0254651d | 289 | packet.payload = (uint8_t *)&header; |
6ed4f044 DR |
290 | header.feed_version = 1; |
291 | gettimeofday(&header.starttime, NULL); | |
0254651d UH |
292 | sr_session_send(devc->cb_data, &packet); |
293 | ||
294 | /* Send metadata about the SR_DF_ANALOG packets to come. */ | |
295 | sr_dbg("Sending SR_DF_META_ANALOG packet."); | |
296 | packet.type = SR_DF_META_ANALOG; | |
297 | packet.payload = &meta; | |
9cd9f6b7 | 298 | meta.num_probes = devc->num_probes; |
0254651d UH |
299 | sr_session_send(devc->cb_data, &packet); |
300 | ||
301 | /* Poll every 10ms, or whenever some data comes in. */ | |
302 | sr_source_add(devc->ufds[0].fd, devc->ufds[0].events, 10, | |
9cd9f6b7 | 303 | alsa_receive_data, (void *)sdi); |
0254651d UH |
304 | |
305 | // g_free(devc->ufds); /* FIXME */ | |
6ed4f044 | 306 | |
e46b8fb1 | 307 | return SR_OK; |
6ed4f044 DR |
308 | } |
309 | ||
0254651d | 310 | static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) |
6ed4f044 | 311 | { |
0254651d UH |
312 | struct sr_datafeed_packet packet; |
313 | struct dev_context *devc; | |
314 | ||
315 | devc = sdi->priv; | |
316 | devc->cb_data = cb_data; | |
317 | ||
318 | sr_source_remove(devc->ufds[0].fd); | |
319 | ||
320 | /* Send end packet to the session bus. */ | |
321 | sr_dbg("Sending SR_DF_END packet."); | |
322 | packet.type = SR_DF_END; | |
323 | sr_session_send(cb_data, &packet); | |
3010f21c UH |
324 | |
325 | return SR_OK; | |
6ed4f044 DR |
326 | } |
327 | ||
c09f0b57 | 328 | SR_PRIV struct sr_dev_driver alsa_driver_info = { |
e519ba86 UH |
329 | .name = "alsa", |
330 | .longname = "ALSA driver", | |
331 | .api_version = 1, | |
332 | .init = hw_init, | |
333 | .cleanup = hw_cleanup, | |
0254651d UH |
334 | .scan = hw_scan, |
335 | .dev_list = hw_dev_list, | |
336 | .dev_clear = clear_instances, | |
e7eb703f UH |
337 | .dev_open = hw_dev_open, |
338 | .dev_close = hw_dev_close, | |
0254651d | 339 | .info_get = hw_info_get, |
a9a245b4 | 340 | .dev_config_set = hw_dev_config_set, |
69040b7c UH |
341 | .dev_acquisition_start = hw_dev_acquisition_start, |
342 | .dev_acquisition_stop = hw_dev_acquisition_stop, | |
0254651d | 343 | .priv = NULL, |
6ed4f044 | 344 | }; |