]> sigrok.org Git - libsigrok.git/blob - src/hardware/devantech-eth008/api.c
devantech-eth008: first driver implementation, relay output only
[libsigrok.git] / src / hardware / devantech-eth008 / api.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2023 Gerhard Sittig <gerhard.sittig@gmx.net>
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
22 #include <string.h>
23
24 #include "protocol.h"
25
26 #define VENDOR_TEXT     "Devantech"
27
28 static const uint32_t scanopts[] = {
29         SR_CONF_CONN,
30 };
31
32 static const uint32_t drvopts[] = {
33         SR_CONF_MULTIPLEXER,
34 };
35
36 static const uint32_t devopts[] = {
37         SR_CONF_CONN | SR_CONF_GET,
38         SR_CONF_ENABLED | SR_CONF_SET, /* Enable/disable all relays at once. */
39 };
40
41 static const uint32_t devopts_cg_do[] = {
42         SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
43 };
44
45 static const uint32_t devopts_cg_ai[] = {
46         SR_CONF_VOLTAGE | SR_CONF_GET,
47 };
48
49 static const struct devantech_eth008_model models[] = {
50         { 19, "ETH008", 8, 0, 1, },
51 };
52
53 static const struct devantech_eth008_model *find_model(uint8_t code)
54 {
55         size_t idx;
56         const struct devantech_eth008_model *check;
57
58         for (idx = 0; idx < ARRAY_SIZE(models); idx++) {
59                 check = &models[idx];
60                 if (check->code != code)
61                         continue;
62                 return check;
63         }
64
65         return NULL;
66 }
67
68 static struct sr_dev_driver devantech_eth008_driver_info;
69
70 static struct sr_dev_inst *probe_device_conn(const char *conn)
71 {
72         struct sr_dev_inst *sdi;
73         struct dev_context *devc;
74         struct sr_serial_dev_inst *ser;
75         uint8_t code, hwver, fwver;
76         const struct devantech_eth008_model *model;
77         gboolean has_serno_cmd;
78         char snr_txt[16];
79         struct channel_group_context *cgc;
80         size_t ch_idx, nr, do_idx;
81         struct sr_channel_group *cg;
82         char cg_name[24];
83         int ret;
84
85         sdi = g_malloc0(sizeof(*sdi));
86         devc = g_malloc0(sizeof(*devc));
87         sdi->priv = devc;
88         ser = sr_serial_dev_inst_new(conn, NULL);
89         sdi->conn = ser;
90         if (!ser)
91                 goto probe_fail;
92         ret = serial_open(ser, 0);
93         if (ret != SR_OK)
94                 goto probe_fail;
95
96         ret = devantech_eth008_get_model(ser, &code, &hwver, &fwver);
97         if (ret != SR_OK)
98                 goto probe_fail;
99         model = find_model(code);
100         if (!model) {
101                 sr_err("Unknown model ID 0x%02x (HW %u, FW %u).",
102                         code, hwver, fwver);
103                 goto probe_fail;
104         }
105         devc->model_code = code;
106         devc->hardware_version = hwver;
107         devc->firmware_version = fwver;
108         devc->model = model;
109         sdi->vendor = g_strdup(VENDOR_TEXT);
110         sdi->model = g_strdup(model->name);
111         sdi->version = g_strdup_printf("HW%u FW%u", hwver, fwver);
112         sdi->connection_id = g_strdup(conn);
113         sdi->driver = &devantech_eth008_driver_info;
114         sdi->inst_type = SR_INST_SERIAL;
115
116         has_serno_cmd = TRUE;
117         if (model->min_serno_fw && fwver < model->min_serno_fw)
118                 has_serno_cmd = FALSE;
119         if (has_serno_cmd) {
120                 snr_txt[0] = '\0';
121                 ret = devantech_eth008_get_serno(ser,
122                         snr_txt, sizeof(snr_txt));
123                 if (ret != SR_OK)
124                         goto probe_fail;
125                 sdi->serial_num = g_strdup(snr_txt);
126         }
127
128         ch_idx = 0;
129         devc->mask_do = (1UL << devc->model->ch_count_do) - 1;
130         for (do_idx = 0; do_idx < devc->model->ch_count_do; do_idx++) {
131                 nr = do_idx + 1;
132                 snprintf(cg_name, sizeof(cg_name), "DO%zu", nr);
133                 cgc = g_malloc0(sizeof(*cgc));
134                 cg = sr_channel_group_new(sdi, cg_name, cgc);
135                 cgc->index = do_idx;
136                 cgc->number = nr;
137                 cgc->ch_type = DV_CH_DIGITAL_OUTPUT;
138                 (void)cg;
139                 ch_idx++;
140         }
141         if (1) {
142                 /* Create an analog channel for the supply voltage. */
143                 snprintf(cg_name, sizeof(cg_name), "Vsupply");
144                 cgc = g_malloc0(sizeof(*cgc));
145                 cg = sr_channel_group_new(sdi, cg_name, cgc);
146                 cgc->index = 0;
147                 cgc->number = 0;
148                 cgc->ch_type = DV_CH_SUPPLY_VOLTAGE;
149                 (void)cg;
150                 ch_idx++;
151         }
152
153         return sdi;
154
155 probe_fail:
156         if (ser) {
157                 serial_close(ser);
158                 sr_serial_dev_inst_free(ser);
159         }
160         if (devc) {
161                 g_free(devc);
162         }
163         if (sdi) {
164                 sdi->priv = NULL;
165                 sr_dev_inst_free(sdi);
166                 sdi = NULL;
167         }
168         return sdi;
169 }
170
171 static GSList *scan(struct sr_dev_driver *di, GSList *options)
172 {
173         struct drv_context *drvc;
174         const char *conn;
175         GSList *devices;
176         struct sr_dev_inst *sdi;
177
178         drvc = di->context;
179         drvc->instances = NULL;
180
181         /* A conn= spec is required for the TCP attached device. */
182         conn = NULL;
183         (void)sr_serial_extract_options(options, &conn, NULL);
184         if (!conn || !*conn)
185                 return NULL;
186
187         devices = NULL;
188         sdi = probe_device_conn(conn);
189         if (sdi)
190                 devices = g_slist_append(devices, sdi);
191
192         return std_scan_complete(di, devices);
193 }
194
195 static int config_get(uint32_t key, GVariant **data,
196         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
197 {
198         struct channel_group_context *cgc;
199         gboolean on;
200         uint16_t vin;
201         double vsupply;
202         int ret;
203
204         if (!cg) {
205                 switch (key) {
206                 case SR_CONF_CONN:
207                         if (!sdi->connection_id)
208                                 return SR_ERR_NA;
209                         *data = g_variant_new_string(sdi->connection_id);
210                         return SR_OK;
211                 default:
212                         return SR_ERR_NA;
213                 }
214         }
215
216         cgc = cg->priv;
217         if (!cgc)
218                 return SR_ERR_NA;
219         switch (key) {
220         case SR_CONF_ENABLED:
221                 if (cgc->ch_type == DV_CH_DIGITAL_OUTPUT) {
222                         ret = devantech_eth008_query_do(sdi, cg, &on);
223                         if (ret != SR_OK)
224                                 return ret;
225                         *data = g_variant_new_boolean(on);
226                         return SR_OK;
227                 }
228                 return SR_ERR_NA;
229         case SR_CONF_VOLTAGE:
230                 if (cgc->ch_type == DV_CH_SUPPLY_VOLTAGE) {
231                         ret = devantech_eth008_query_supply(sdi, cg, &vin);
232                         if (ret != SR_OK)
233                                 return ret;
234                         vsupply = vin;
235                         vsupply /= 1000.;
236                         *data = g_variant_new_double(vsupply);
237                         return SR_OK;
238                 }
239                 return SR_ERR_NA;
240         default:
241                 return SR_ERR_NA;
242         }
243 }
244
245 static int config_set(uint32_t key, GVariant *data,
246         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
247 {
248         struct channel_group_context *cgc;
249         gboolean on;
250
251         if (!cg) {
252                 switch (key) {
253                 case SR_CONF_ENABLED:
254                         /* Enable/disable all channels at the same time. */
255                         on = g_variant_get_boolean(data);
256                         return devantech_eth008_setup_do(sdi, cg, on);
257                 default:
258                         return SR_ERR_NA;
259                 }
260         }
261
262         cgc = cg->priv;
263         if (!cgc)
264                 return SR_ERR_NA;
265         switch (key) {
266         case SR_CONF_ENABLED:
267                 if (cgc->ch_type != DV_CH_DIGITAL_OUTPUT)
268                         return SR_ERR_NA;
269                 on = g_variant_get_boolean(data);
270                 return devantech_eth008_setup_do(sdi, cg, on);
271         default:
272                 return SR_ERR_NA;
273         }
274
275         /* XXX Is this actually UNREACH? */
276         return SR_OK;
277 }
278
279 static int config_list(uint32_t key, GVariant **data,
280         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
281 {
282         struct channel_group_context *cgc;
283
284         if (!cg) {
285                 switch (key) {
286                 case SR_CONF_SCAN_OPTIONS:
287                 case SR_CONF_DEVICE_OPTIONS:
288                         return STD_CONFIG_LIST(key, data, sdi, cg,
289                                 scanopts, drvopts, devopts);
290                 default:
291                         return SR_ERR_NA;
292                 }
293         }
294
295         cgc = cg->priv;
296         if (!cgc)
297                 return SR_ERR_NA;
298         switch (key) {
299         case SR_CONF_DEVICE_OPTIONS:
300                 if (cgc->ch_type == DV_CH_DIGITAL_OUTPUT) {
301                         *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_do));
302                         return SR_OK;
303                 }
304                 if (cgc->ch_type == DV_CH_SUPPLY_VOLTAGE) {
305                         *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg_ai));
306                         return SR_OK;
307                 }
308                 return SR_ERR_NA;
309         default:
310                 return SR_ERR_NA;
311         }
312 }
313
314 static struct sr_dev_driver devantech_eth008_driver_info = {
315         .name = "devantech-eth008",
316         .longname = "Devantech ETH008",
317         .api_version = 1,
318         .init = std_init,
319         .cleanup = std_cleanup,
320         .scan = scan,
321         .dev_list = std_dev_list,
322         .dev_clear = std_dev_clear,
323         .config_get = config_get,
324         .config_set = config_set,
325         .config_list = config_list,
326         .dev_open = std_serial_dev_open,
327         .dev_close = std_serial_dev_close,
328         .dev_acquisition_start = std_dummy_dev_acquisition_start,
329         .dev_acquisition_stop = std_dummy_dev_acquisition_stop,
330         .context = NULL,
331 };
332 SR_REGISTER_DEV_DRIVER(devantech_eth008_driver_info);