diff options
Diffstat (limited to 'rbutil/jztool/src/context.c')
-rw-r--r-- | rbutil/jztool/src/context.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/rbutil/jztool/src/context.c b/rbutil/jztool/src/context.c new file mode 100644 index 0000000000..94b21b5196 --- /dev/null +++ b/rbutil/jztool/src/context.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "jztool_private.h" | ||
23 | #include <string.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <stddef.h> | ||
26 | #include <stdarg.h> | ||
27 | #include <stdio.h> | ||
28 | #include <time.h> | ||
29 | |||
30 | /** \brief Allocate a library context | ||
31 | * \returns New context or NULL if out of memory | ||
32 | */ | ||
33 | jz_context* jz_context_create(void) | ||
34 | { | ||
35 | jz_context* jz = malloc(sizeof(struct jz_context)); | ||
36 | if(!jz) | ||
37 | return NULL; | ||
38 | |||
39 | memset(jz, 0, sizeof(struct jz_context)); | ||
40 | jz->log_level = JZ_LOG_ERROR; | ||
41 | return jz; | ||
42 | } | ||
43 | |||
44 | /** \brief Destroy the context and free its memory */ | ||
45 | void jz_context_destroy(jz_context* jz) | ||
46 | { | ||
47 | if(jz->usb_ctx) { | ||
48 | jz_log(jz, JZ_LOG_ERROR, "BUG: USB was not cleaned up properly"); | ||
49 | libusb_exit(jz->usb_ctx); | ||
50 | } | ||
51 | |||
52 | free(jz); | ||
53 | } | ||
54 | |||
55 | /** \brief Set a user data pointer. Useful for callbacks. */ | ||
56 | void jz_context_set_user_data(jz_context* jz, void* ptr) | ||
57 | { | ||
58 | jz->user_data = ptr; | ||
59 | } | ||
60 | |||
61 | /** \brief Get the user data pointer */ | ||
62 | void* jz_context_get_user_data(jz_context* jz) | ||
63 | { | ||
64 | return jz->user_data; | ||
65 | } | ||
66 | |||
67 | /** \brief Set the log message callback. | ||
68 | * \note By default, no message callback is set! No messages will be logged | ||
69 | * in this case, so ensure you set a callback if messages are desired. | ||
70 | */ | ||
71 | void jz_context_set_log_cb(jz_context* jz, jz_log_cb cb) | ||
72 | { | ||
73 | jz->log_cb = cb; | ||
74 | } | ||
75 | |||
76 | /** \brief Set the log level. | ||
77 | * | ||
78 | * Messages of less importance than the set log level are not logged. | ||
79 | * The default log level is `JZ_LOG_WARNING`. The special log level | ||
80 | * `JZ_LOG_IGNORE` can be used to disable all logging temporarily. | ||
81 | * | ||
82 | * The `JZ_LOG_DEBUG` log level is extremely verbose and will log all calls, | ||
83 | * normally it's only useful for catching bugs. | ||
84 | */ | ||
85 | void jz_context_set_log_level(jz_context* jz, jz_log_level lev) | ||
86 | { | ||
87 | jz->log_level = lev; | ||
88 | } | ||
89 | |||
90 | /** \brief Log an informational message. | ||
91 | * \param lev Log level for this message | ||
92 | * \param fmt `printf` style message format string | ||
93 | */ | ||
94 | void jz_log(jz_context* jz, jz_log_level lev, const char* fmt, ...) | ||
95 | { | ||
96 | if(!jz->log_cb) | ||
97 | return; | ||
98 | if(lev == JZ_LOG_IGNORE) | ||
99 | return; | ||
100 | if(lev > jz->log_level) | ||
101 | return; | ||
102 | |||
103 | va_list ap; | ||
104 | |||
105 | va_start(ap, fmt); | ||
106 | int n = vsnprintf(NULL, 0, fmt, ap); | ||
107 | va_end(ap); | ||
108 | |||
109 | if(n < 0) | ||
110 | return; | ||
111 | |||
112 | char* buf = malloc(n + 1); | ||
113 | if(!buf) | ||
114 | return; | ||
115 | |||
116 | va_start(ap, fmt); | ||
117 | n = vsnprintf(buf, n + 1, fmt, ap); | ||
118 | va_end(ap); | ||
119 | |||
120 | if(n >= 0) | ||
121 | jz->log_cb(lev, buf); | ||
122 | |||
123 | free(buf); | ||
124 | } | ||
125 | |||
126 | /** \brief Log callback which writes messages to `stderr`. | ||
127 | */ | ||
128 | void jz_log_cb_stderr(jz_log_level lev, const char* msg) | ||
129 | { | ||
130 | static const char* const tags[] = | ||
131 | {"ERROR", "WARNING", "NOTICE", "DETAIL", "DEBUG"}; | ||
132 | fprintf(stderr, "[%7s] %s\n", tags[lev], msg); | ||
133 | fflush(stderr); | ||
134 | } | ||
135 | |||
136 | /** \brief Sleep for `ms` milliseconds. | ||
137 | */ | ||
138 | void jz_sleepms(int ms) | ||
139 | { | ||
140 | struct timespec ts; | ||
141 | long ns = ms % 1000; | ||
142 | ts.tv_nsec = ns * 1000 * 1000; | ||
143 | ts.tv_sec = ms / 1000; | ||
144 | nanosleep(&ts, NULL); | ||
145 | } | ||
146 | |||
147 | int jz_context_ref_libusb(jz_context* jz) | ||
148 | { | ||
149 | if(jz->usb_ctxref == 0) { | ||
150 | int rc = libusb_init(&jz->usb_ctx); | ||
151 | if(rc < 0) { | ||
152 | jz_log(jz, JZ_LOG_ERROR, "libusb_init: %s", libusb_strerror(rc)); | ||
153 | return JZ_ERR_USB; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | jz->usb_ctxref += 1; | ||
158 | return JZ_SUCCESS; | ||
159 | } | ||
160 | |||
161 | void jz_context_unref_libusb(jz_context* jz) | ||
162 | { | ||
163 | if(--jz->usb_ctxref == 0) { | ||
164 | libusb_exit(jz->usb_ctx); | ||
165 | jz->usb_ctx = NULL; | ||
166 | } | ||
167 | } | ||