fluke-dmm: flesh out the driver API
[sigrok:sigrok.git] / libsigrok / hardware / fluke-dmm / api.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2012 Bert Vermeulen <bert@biot.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 <glib.h>
21 #include "libsigrok.h"
22 #include "libsigrok-internal.h"
23 #include "config.h"
24 #include "fluke-dmm.h"
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <errno.h>
30
31 static const int hwopts[] = {
32         SR_HWOPT_CONN,
33         SR_HWOPT_SERIALCOMM,
34         0,
35 };
36
37 static const int hwcaps[] = {
38         SR_HWCAP_MULTIMETER,
39         SR_HWCAP_LIMIT_SAMPLES,
40         SR_HWCAP_LIMIT_MSEC,
41         SR_HWCAP_CONTINUOUS,
42         0,
43 };
44
45 static const char *probe_names[] = {
46         "Probe",
47         NULL,
48 };
49
50 SR_PRIV struct sr_dev_driver flukedmm_driver_info;
51 static struct sr_dev_driver *di = &flukedmm_driver_info;
52
53 static const struct flukedmm_profile supported_flukedmm[] = {
54         { FLUKE_187, "187" },
55 };
56
57
58 /* Properly close and free all devices. */
59 static int clear_instances(void)
60 {
61         struct sr_dev_inst *sdi;
62         struct drv_context *drvc;
63         struct dev_context *devc;
64         GSList *l;
65
66         if (!(drvc = di->priv))
67                 return SR_OK;
68
69         drvc = di->priv;
70         for (l = drvc->instances; l; l = l->next) {
71                 if (!(sdi = l->data))
72                         continue;
73                 if (!(devc = sdi->priv))
74                         continue;
75                 sr_serial_dev_inst_free(devc->serial);
76                 sr_dev_inst_free(sdi);
77         }
78         g_slist_free(drvc->instances);
79         drvc->instances = NULL;
80
81         return SR_OK;
82 }
83
84 static int hw_init(void)
85 {
86         struct drv_context *drvc;
87
88         if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) {
89                 sr_err("fluke-dmm: driver context malloc failed.");
90                 return SR_ERR;
91         }
92
93         di->priv = drvc;
94
95         return SR_OK;
96 }
97
98 static int serial_readline(int fd, char **buf, int *buflen, uint64_t timeout_ms)
99 {
100         uint64_t start;
101         int maxlen, len;
102
103         timeout_ms *= 1000;
104         start = g_get_monotonic_time();
105
106         maxlen = *buflen;
107         *buflen = len = 0;
108         while(1) {
109                 len = maxlen - *buflen - 1;
110                 if (len < 1)
111                         break;
112                 len = serial_read(fd, *buf + *buflen, 1);
113                 if (len > 0) {
114                         *buflen += len;
115                         *(*buf + *buflen) = '\0';
116                         if (*buflen > 0 && *(*buf + *buflen - 1) == '\n') {
117                                 /* Strip LF and terminate. */
118                                 *(*buf + --*buflen) = '\0';
119                                 break;
120                         }
121                 }
122                 if (g_get_monotonic_time() - start > timeout_ms)
123                         /* Timeout */
124                         break;
125                 g_usleep(2000);
126         }
127         sr_dbg("fluke-dmm: received %d: '%s'", *buflen, *buf);
128
129         return SR_OK;
130 }
131
132 static GSList *fluke_scan(const char *conn, const char *serialcomm)
133 {
134         struct sr_dev_inst *sdi;
135         struct drv_context *drvc;
136         struct dev_context *devc;
137         struct sr_probe *probe;
138         GSList *devices;
139         int fd, retry, len, i, s;
140         char buf[128], *b, **tokens;
141
142         if ((fd = serial_open(conn, O_RDWR|O_NONBLOCK)) == -1) {
143                 sr_err("fluke-dmm: unable to open %s: %s", conn, strerror(errno));
144                 return NULL;
145         }
146         if (serial_set_paramstr(fd, serialcomm) != SR_OK) {
147                 sr_err("fluke-dmm: unable to set serial parameters");
148                 return NULL;
149         }
150
151         drvc = di->priv;
152         b = buf;
153         retry = 0;
154         devices = NULL;
155         /* We'll try the discovery sequence three times in case the device
156          * is not in an idle state when we send ID. */
157         while (!devices && retry < 3) {
158                 retry++;
159                 serial_flush(fd);
160                 if (serial_write(fd, "ID\r", 3) == -1) {
161                         sr_err("fluke-dmm: unable to send ID string: %s",
162                                         strerror(errno));
163                         continue;
164                 }
165
166                 /* Response is first a CMD_ACK byte (ASCII '0' for OK,
167                  * or '1' to signify an error. */
168                 len = 128;
169                 serial_readline(fd, &b, &len, 150);
170                 if (len != 1)
171                         continue;
172                 if (buf[0] != '0')
173                         continue;
174
175                 /* If CMD_ACK was OK, ID string follows. */
176                 len = 128;
177                 serial_readline(fd, &b, &len, 150);
178                 if (len < 10)
179                         continue;
180                 tokens = g_strsplit(buf, ",", 3);
181                 if (!strncmp("FLUKE", tokens[0], 5)
182                                 && tokens[1] && tokens[2]) {
183                         for (i = 0; supported_flukedmm[i].model; i++) {
184                                 if (strcmp(supported_flukedmm[i].modelname, tokens[0] + 6))
185                                         continue;
186                                 /* Skip leading spaces in version number. */
187                                 for (s = 0; tokens[1][s] == ' '; s++);
188                                 if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "Fluke",
189                                                 tokens[0] + 6, tokens[1] + s)))
190                                         return NULL;
191                                 if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
192                                         sr_dbg("fluke-dmm: failed to malloc devc");
193                                         return NULL;
194                                 }
195                                 devc->profile = &supported_flukedmm[i];
196                                 devc->serial = sr_serial_dev_inst_new(conn, -1);
197                                 devc->serialcomm = g_strdup(serialcomm);
198                                 sdi->priv = devc;
199                                 sdi->driver = di;
200                                 if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
201                                         return NULL;
202                                 sdi->probes = g_slist_append(sdi->probes, probe);
203                                 drvc->instances = g_slist_append(drvc->instances, sdi);
204                                 devices = g_slist_append(devices, sdi);
205                                 break;
206                         }
207                 }
208                 g_strfreev(tokens);
209         }
210         serial_close(fd);
211
212         return devices;
213 }
214
215 static GSList *hw_scan(GSList *options)
216 {
217         struct sr_hwopt *opt;
218         GSList *l, *devices;
219         const char *conn, *serialcomm;
220
221         conn = serialcomm = NULL;
222         for (l = options; l; l = l->next) {
223                 opt = l->data;
224                 switch (opt->hwopt) {
225                 case SR_HWOPT_CONN:
226                         conn = opt->value;
227                         break;
228                 case SR_HWOPT_SERIALCOMM:
229                         serialcomm = opt->value;
230                         break;
231                 }
232         }
233         if (!conn)
234                 return NULL;
235
236         if (serialcomm) {
237                 /* Use the provided comm specs. */
238                 devices = fluke_scan(conn, serialcomm);
239         } else {
240                 /* Try 115200, as used on 287/289. */
241                 devices = fluke_scan(conn, "115200/8n1");
242                 if (!devices)
243                         /* Fall back to 9600 for 187/189. */
244                         devices = fluke_scan(conn, "9600/8n1");
245         }
246
247         return devices;
248 }
249
250 static GSList *hw_dev_list(void)
251 {
252         struct drv_context *drvc;
253
254         drvc = di->priv;
255
256         return drvc->instances;
257 }
258
259 static int hw_dev_open(struct sr_dev_inst *sdi)
260 {
261         struct dev_context *devc;
262
263         if (!(devc = sdi->priv)) {
264                 sr_err("fluke-dmm: sdi->priv was NULL.");
265                 return SR_ERR_BUG;
266         }
267
268         devc->serial->fd = serial_open(devc->serial->port, O_RDWR | O_NONBLOCK);
269         if (devc->serial->fd == -1) {
270                 sr_err("fluke-dmm: Couldn't open serial port '%s'.",
271                        devc->serial->port);
272                 return SR_ERR;
273         }
274         if (serial_set_paramstr(devc->serial->fd, devc->serialcomm) != SR_OK) {
275                 sr_err("fluke-dmm: unable to set serial parameters");
276                 return SR_ERR;
277         }
278         sdi->status = SR_ST_ACTIVE;
279
280         return SR_OK;
281 }
282
283 static int hw_dev_close(struct sr_dev_inst *sdi)
284 {
285         struct dev_context *devc;
286
287         if (!(devc = sdi->priv)) {
288                 sr_err("fluke-dmm: sdi->priv was NULL.");
289                 return SR_ERR_BUG;
290         }
291
292         if (devc->serial && devc->serial->fd != -1) {
293                 serial_close(devc->serial->fd);
294                 devc->serial->fd = -1;
295                 sdi->status = SR_ST_INACTIVE;
296         }
297
298         return SR_OK;
299 }
300
301 static int hw_cleanup(void)
302 {
303
304         clear_instances();
305
306         return SR_OK;
307 }
308
309 static int hw_info_get(int info_id, const void **data,
310        const struct sr_dev_inst *sdi)
311 {
312
313         (void)sdi;
314
315         switch (info_id) {
316         case SR_DI_HWOPTS:
317                 *data = hwopts;
318                 break;
319         case SR_DI_HWCAPS:
320                 *data = hwcaps;
321                 break;
322         case SR_DI_NUM_PROBES:
323                 *data = GINT_TO_POINTER(1);
324                 break;
325         case SR_DI_PROBE_NAMES:
326                 *data = probe_names;
327                 break;
328         default:
329                 return SR_ERR_ARG;
330         }
331
332         return SR_OK;
333 }
334
335 static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap,
336                 const void *value)
337 {
338         struct dev_context *devc;
339
340         if (sdi->status != SR_ST_ACTIVE)
341                 return SR_ERR;
342
343         if (!(devc = sdi->priv)) {
344                 sr_err("fluke-dmm: sdi->priv was NULL.");
345                 return SR_ERR_BUG;
346         }
347
348         switch (hwcap) {
349         case SR_HWCAP_LIMIT_MSEC:
350                 /* TODO: not yet implemented */
351                 if (*(const uint64_t *)value == 0) {
352                         sr_err("fluke-dmm: LIMIT_MSEC can't be 0.");
353                         return SR_ERR;
354                 }
355                 devc->limit_msec = *(const uint64_t *)value;
356                 sr_dbg("fluke-dmm: Setting time limit to %" PRIu64 "ms.",
357                        devc->limit_msec);
358                 break;
359         case SR_HWCAP_LIMIT_SAMPLES:
360                 devc->limit_samples = *(const uint64_t *)value;
361                 sr_dbg("fluke-dmm: Setting sample limit to %" PRIu64 ".",
362                        devc->limit_samples);
363                 break;
364         default:
365                 sr_err("fluke-dmm: Unknown capability: %d.", hwcap);
366                 return SR_ERR;
367                 break;
368         }
369
370         return SR_OK;
371 }
372
373 static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
374                 void *cb_data)
375 {
376         struct sr_datafeed_packet packet;
377         struct sr_datafeed_header header;
378         struct sr_datafeed_meta_analog meta;
379         struct dev_context *devc;
380
381         if (!(devc = sdi->priv)) {
382                 sr_err("fluke-dmm: sdi->priv was NULL.");
383                 return SR_ERR_BUG;
384         }
385
386         sr_dbg("fluke-dmm: Starting acquisition.");
387
388         devc->cb_data = cb_data;
389
390         /* Send header packet to the session bus. */
391         sr_dbg("fluke-dmm: Sending SR_DF_HEADER.");
392         packet.type = SR_DF_HEADER;
393         packet.payload = (uint8_t *)&header;
394         header.feed_version = 1;
395         gettimeofday(&header.starttime, NULL);
396         sr_session_send(devc->cb_data, &packet);
397
398         /* Send metadata about the SR_DF_ANALOG packets to come. */
399         sr_dbg("fluke-dmm: Sending SR_DF_META_ANALOG.");
400         packet.type = SR_DF_META_ANALOG;
401         packet.payload = &meta;
402         meta.num_probes = 1;
403         sr_session_send(devc->cb_data, &packet);
404
405         /* Poll every 100ms, or whenever some data comes in. */
406         sr_source_add(devc->serial->fd, G_IO_IN, 100, fluke_receive_data, (void *)sdi);
407
408         return SR_OK;
409 }
410
411 static int hw_dev_acquisition_stop(const struct sr_dev_inst *sdi,
412                 void *cb_data)
413 {
414         struct sr_datafeed_packet packet;
415         struct dev_context *devc;
416
417         if (sdi->status != SR_ST_ACTIVE)
418                 return SR_ERR;
419
420         if (!(devc = sdi->priv)) {
421                 sr_err("fluke-dmm: sdi->priv was NULL.");
422                 return SR_ERR_BUG;
423         }
424
425         sr_dbg("fluke-dmm: Stopping acquisition.");
426
427         sr_source_remove(devc->serial->fd);
428         hw_dev_close((struct sr_dev_inst *)sdi);
429
430         /* Send end packet to the session bus. */
431         sr_dbg("fluke-dmm: Sending SR_DF_END.");
432         packet.type = SR_DF_END;
433         sr_session_send(cb_data, &packet);
434
435         return SR_OK;
436 }
437
438 SR_PRIV struct sr_dev_driver flukedmm_driver_info = {
439         .name = "fluke-dmm",
440         .longname = "Fluke 18x/28x series DMMs",
441         .api_version = 1,
442         .init = hw_init,
443         .cleanup = hw_cleanup,
444         .scan = hw_scan,
445         .dev_list = hw_dev_list,
446         .dev_clear = clear_instances,
447         .dev_open = hw_dev_open,
448         .dev_close = hw_dev_close,
449         .info_get = hw_info_get,
450         .dev_config_set = hw_dev_config_set,
451         .dev_acquisition_start = hw_dev_acquisition_start,
452         .dev_acquisition_stop = hw_dev_acquisition_stop,
453         .priv = NULL,
454 };