]> sigrok.org Git - libsigrok.git/blame - src/resource.c
demo: Properly handle low samplerates
[libsigrok.git] / src / resource.c
CommitLineData
bee24666
DE
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2015 Daniel Elstner <daniel.kitta@gmail.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (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 General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
21#include <errno.h>
22#include <stdio.h>
23#include <glib.h>
24#include <glib/gstdio.h>
25#include <libsigrok/libsigrok.h>
26#include "libsigrok-internal.h"
27
28/** @cond PRIVATE */
29#define LOG_PREFIX "resource"
30/** @endcond */
31
32/**
33 * @file
34 *
35 * Access to resource files.
36 */
37
7d89fd60
DE
38/** Retrieve the size of the open stream @a file.
39 *
40 * This function only works on seekable streams. However, the set of seekable
41 * streams is generally congruent with the set of streams that have a size.
42 * Code that needs to work with any type of stream (including pipes) should
43 * require neither seekability nor advance knowledge of the size.
44 * On failure, the return value is negative and errno is set.
45 *
46 * @param file An I/O stream opened in binary mode.
47 * @return The size of @a file in bytes, or a negative value on failure.
48 *
49 * @private
50 */
51SR_PRIV int64_t sr_file_get_size(FILE *file)
52{
53 off_t filepos, filesize;
54
55 /* ftello() and fseeko() are not standard C, but part of POSIX.1-2001.
56 * Thus, if these functions are available at all, they can reasonably
57 * be expected to also conform to POSIX semantics. In particular, this
58 * means that ftello() after fseeko(..., SEEK_END) has a defined result
59 * and can be used to get the size of a seekable stream.
60 * On Windows, the result is fully defined only for binary streams.
61 */
62 filepos = ftello(file);
63 if (filepos < 0)
64 return -1;
65
66 if (fseeko(file, 0, SEEK_END) < 0)
67 return -1;
68
69 filesize = ftello(file);
70 if (filesize < 0)
71 return -1;
72
73 if (fseeko(file, filepos, SEEK_SET) < 0)
74 return -1;
75
76 return filesize;
77}
78
bee24666
DE
79static FILE *try_open_file(const char *datadir, const char *subdir,
80 const char *name)
81{
82 char *filename;
83 FILE *file;
84
85 filename = g_build_filename(datadir, subdir, name, NULL);
86 file = g_fopen(filename, "rb");
87
88 if (file)
89 sr_info("Opened '%s'.", filename);
90 else
91 sr_spew("Attempt to open '%s' failed: %s",
92 filename, g_strerror(errno));
93 g_free(filename);
94
95 return file;
96}
97
98static int resource_open_default(struct sr_resource *res,
99 const char *name, void *cb_data)
100{
101 int64_t filesize;
127c9cec
DE
102#ifdef FIRMWARE_DIR
103 const char *builtindir;
104#endif
1503d457 105 const char *subdir, *env;
bee24666 106 const char *const *datadirs;
1503d457 107 FILE *file = NULL;
bee24666
DE
108
109 (void)cb_data;
110
111 switch (res->type) {
112 case SR_RESOURCE_FIRMWARE:
127c9cec 113#ifdef FIRMWARE_DIR
bee24666 114 builtindir = FIRMWARE_DIR;
127c9cec 115#endif
bee24666
DE
116 subdir = "sigrok-firmware";
117 break;
118 default:
119 sr_err("%s: unknown type %d.", __func__, res->type);
120 return SR_ERR_ARG;
121 }
122
1503d457
UH
123 env = g_getenv("SIGROK_FIRMWARE_DIR");
124 if (!env)
125 sr_dbg("SIGROK_FIRMWARE_DIR environment variable not set, ignoring.");
126 else
127 file = try_open_file(env, "", name);
128
129 if (!file)
130 file = try_open_file(g_get_user_data_dir(), subdir, name);
131
bee24666
DE
132 /*
133 * Scan the hard-coded directory before the system directories to
134 * avoid picking up possibly outdated files from a system install.
135 */
127c9cec 136#ifdef FIRMWARE_DIR
bee24666
DE
137 if (!file)
138 file = try_open_file(builtindir, "", name);
127c9cec 139#endif
bee24666
DE
140 if (!file) {
141 datadirs = g_get_system_data_dirs();
142 while (*datadirs && !file)
143 file = try_open_file(*datadirs++, subdir, name);
144 }
145 if (!file) {
7ade12b4 146 sr_dbg("Failed to locate '%s'.", name);
bee24666
DE
147 return SR_ERR;
148 }
149
150 filesize = sr_file_get_size(file);
151 if (filesize < 0) {
152 sr_err("Failed to obtain size of '%s': %s",
153 name, g_strerror(errno));
154 fclose(file);
155 return SR_ERR;
156 }
157 res->size = filesize;
158 res->handle = file;
159
160 return SR_OK;
161}
162
163static int resource_close_default(struct sr_resource *res, void *cb_data)
164{
165 FILE *file;
166
167 (void)cb_data;
168
169 file = res->handle;
170 if (!file) {
171 sr_err("%s: invalid handle.", __func__);
172 return SR_ERR_ARG;
173 }
174
175 if (fclose(file) < 0) {
176 sr_err("Failed to close file: %s", g_strerror(errno));
177 return SR_ERR;
178 }
179 res->handle = NULL;
180
181 return SR_OK;
182}
183
32ba0d80 184static gssize resource_read_default(const struct sr_resource *res,
bee24666
DE
185 void *buf, size_t count, void *cb_data)
186{
187 FILE *file;
188 size_t n_read;
189
190 (void)cb_data;
191
192 file = res->handle;
193 if (!file) {
194 sr_err("%s: invalid handle.", __func__);
195 return SR_ERR_ARG;
196 }
32ba0d80 197 if (count > G_MAXSSIZE) {
bee24666
DE
198 sr_err("%s: count %zu too large.", __func__, count);
199 return SR_ERR_ARG;
200 }
201
202 n_read = fread(buf, 1, count, file);
203
204 if (n_read != count && ferror(file)) {
205 sr_err("Failed to read resource file: %s", g_strerror(errno));
206 return SR_ERR;
207 }
208 return n_read;
209}
210
211/**
212 * Install resource access hooks.
213 *
214 * @param ctx libsigrok context. Must not be NULL.
215 * @param open_cb Resource open callback, or NULL to unset.
216 * @param close_cb Resource close callback, or NULL to unset.
217 * @param read_cb Resource read callback, or NULL to unset.
218 * @param cb_data User data pointer passed to callbacks.
219 *
220 * @retval SR_OK Success.
221 * @retval SR_ERR_ARG Invalid argument.
222 *
223 * @since 0.4.0
224 */
225SR_API int sr_resource_set_hooks(struct sr_context *ctx,
226 sr_resource_open_callback open_cb,
227 sr_resource_close_callback close_cb,
228 sr_resource_read_callback read_cb, void *cb_data)
229{
230 if (!ctx) {
231 sr_err("%s: ctx was NULL.", __func__);
232 return SR_ERR_ARG;
233 }
234 if (open_cb && close_cb && read_cb) {
d9251a2c 235 ctx->resource_open_cb = open_cb;
bee24666 236 ctx->resource_close_cb = close_cb;
d9251a2c
UH
237 ctx->resource_read_cb = read_cb;
238 ctx->resource_cb_data = cb_data;
bee24666 239 } else if (!open_cb && !close_cb && !read_cb) {
d9251a2c 240 ctx->resource_open_cb = &resource_open_default;
bee24666 241 ctx->resource_close_cb = &resource_close_default;
d9251a2c
UH
242 ctx->resource_read_cb = &resource_read_default;
243 ctx->resource_cb_data = ctx;
bee24666
DE
244 } else {
245 sr_err("%s: inconsistent callback pointers.", __func__);
246 return SR_ERR_ARG;
247 }
248 return SR_OK;
249}
250
251/**
252 * Open resource.
253 *
254 * @param ctx libsigrok context. Must not be NULL.
255 * @param[out] res Resource descriptor to fill in. Must not be NULL.
256 * @param type Resource type ID.
257 * @param name Name of the resource. Must not be NULL.
258 *
259 * @retval SR_OK Success.
260 * @retval SR_ERR_ARG Invalid argument.
261 * @retval SR_ERR Other error.
262 *
263 * @private
264 */
265SR_PRIV int sr_resource_open(struct sr_context *ctx,
266 struct sr_resource *res, int type, const char *name)
267{
268 int ret;
269
270 res->size = 0;
271 res->handle = NULL;
272 res->type = type;
273
274 ret = (*ctx->resource_open_cb)(res, name, ctx->resource_cb_data);
275
276 if (ret != SR_OK)
7ade12b4
UH
277 sr_err("Failed to open resource '%s' (use loglevel 5/spew for"
278 " details).", name);
bee24666
DE
279
280 return ret;
281}
282
283/**
284 * Close resource.
285 *
286 * @param ctx libsigrok context. Must not be NULL.
287 * @param[inout] res Resource descriptor. Must not be NULL.
288 *
289 * @retval SR_OK Success.
290 * @retval SR_ERR_ARG Invalid argument.
291 * @retval SR_ERR Other error.
292 *
293 * @private
294 */
295SR_PRIV int sr_resource_close(struct sr_context *ctx, struct sr_resource *res)
296{
297 int ret;
298
299 ret = (*ctx->resource_close_cb)(res, ctx->resource_cb_data);
300
301 if (ret != SR_OK)
302 sr_err("Failed to close resource.");
303
304 return ret;
305}
306
307/**
308 * Read resource data.
309 *
310 * @param ctx libsigrok context. Must not be NULL.
311 * @param[in] res Resource descriptor. Must not be NULL.
312 * @param[out] buf Buffer to store @a count bytes into. Must not be NULL.
313 * @param count Number of bytes to read.
314 *
315 * @return The number of bytes actually read, or a negative value on error.
316 * @retval SR_ERR_ARG Invalid argument.
317 * @retval SR_ERR Other error.
318 *
319 * @private
320 */
32ba0d80 321SR_PRIV gssize sr_resource_read(struct sr_context *ctx,
bee24666
DE
322 const struct sr_resource *res, void *buf, size_t count)
323{
32ba0d80 324 gssize n_read;
bee24666
DE
325
326 n_read = (*ctx->resource_read_cb)(res, buf, count,
327 ctx->resource_cb_data);
328 if (n_read < 0)
329 sr_err("Failed to read resource.");
330
331 return n_read;
332}
333
334/**
335 * Load a resource into memory.
336 *
337 * @param ctx libsigrok context. Must not be NULL.
338 * @param type Resource type ID.
339 * @param name Name of the resource. Must not be NULL.
340 * @param[out] size Size in bytes of the returned buffer. Must not be NULL.
341 * @param max_size Size limit. Error out if the resource is larger than this.
342 *
343 * @return A buffer containing the resource data, or NULL on failure. Must
d9251a2c 344 * be freed by the caller using g_free().
bee24666
DE
345 *
346 * @private
347 */
348SR_PRIV void *sr_resource_load(struct sr_context *ctx,
349 int type, const char *name, size_t *size, size_t max_size)
350{
351 struct sr_resource res;
352 void *buf;
45d835ed 353 size_t res_size;
32ba0d80 354 gssize n_read;
bee24666
DE
355
356 if (sr_resource_open(ctx, &res, type, name) != SR_OK)
357 return NULL;
358
359 if (res.size > max_size) {
360 sr_err("Size %" PRIu64 " of '%s' exceeds limit %zu.",
361 res.size, name, max_size);
362 sr_resource_close(ctx, &res);
363 return NULL;
364 }
45d835ed
DE
365 res_size = res.size;
366
367 buf = g_try_malloc(res_size);
bee24666
DE
368 if (!buf) {
369 sr_err("Failed to allocate buffer for '%s'.", name);
370 sr_resource_close(ctx, &res);
371 return NULL;
372 }
373
45d835ed 374 n_read = sr_resource_read(ctx, &res, buf, res_size);
bee24666
DE
375 sr_resource_close(ctx, &res);
376
45d835ed 377 if (n_read < 0 || (size_t)n_read != res_size) {
bee24666
DE
378 if (n_read >= 0)
379 sr_err("Failed to read '%s': premature end of file.",
380 name);
381 g_free(buf);
382 return NULL;
383 }
384
45d835ed 385 *size = res_size;
bee24666
DE
386 return buf;
387}
388
389/** @} */