]> sigrok.org Git - libsigrok.git/blob - hardware/radioshack-dmm/api.c
7a6f033901113346372923f5098b784e0ea8b723
[libsigrok.git] / hardware / radioshack-dmm / api.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
5  * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <glib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <errno.h>
27 #include "libsigrok.h"
28 #include "libsigrok-internal.h"
29 #include "protocol.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_CONTINUOUS,
41         0,
42 };
43
44 static const char *probe_names[] = {
45         "Probe",
46         NULL,
47 };
48
49 SR_PRIV struct sr_dev_driver radioshackdmm_driver_info;
50 static struct sr_dev_driver *di = &radioshackdmm_driver_info;
51
52 /* Properly close and free all devices. */
53 static int clear_instances(void)
54 {
55         struct sr_dev_inst *sdi;
56         struct drv_context *drvc;
57         struct dev_context *devc;
58         GSList *l;
59
60         if (!(drvc = di->priv))
61                 return SR_OK;
62
63         drvc = di->priv;
64         for (l = drvc->instances; l; l = l->next) {
65                 if (!(sdi = l->data))
66                         continue;
67                 if (!(devc = sdi->priv))
68                         continue;
69                 sr_serial_dev_inst_free(devc->serial);
70                 sr_dev_inst_free(sdi);
71         }
72         g_slist_free(drvc->instances);
73         drvc->instances = NULL;
74
75         return SR_OK;
76 }
77
78 static int hw_init(void)
79 {
80         struct drv_context *drvc;
81
82         if (!(drvc = g_try_malloc0(sizeof(struct drv_context)))) {
83                 sr_err("Driver context malloc failed.");
84                 return SR_ERR_MALLOC;
85         }
86
87         di->priv = drvc;
88
89         return SR_OK;
90 }
91
92 static GSList *rs_22_812_scan(const char *conn, const char *serialcomm)
93 {
94         struct sr_dev_inst *sdi;
95         struct drv_context *drvc;
96         struct dev_context *devc;
97         struct sr_probe *probe;
98         GSList *devices;
99         int fd, retry;
100         size_t len, i, good_packets, dropped;
101         char buf[128], *b;
102         const struct rs_22_812_packet *rs_packet;
103
104         if ((fd = serial_open(conn, O_RDONLY | O_NONBLOCK)) < 0) {
105                 sr_err("Unable to open '%s': %d.", conn, fd);
106                 return NULL;
107         }
108
109         if (serial_set_paramstr(fd, serialcomm) != SR_OK) {
110                 sr_err("Unable to set serial parameters.");
111                 return NULL;
112         }
113
114         sr_info("Probing port '%s' readonly.", conn);
115
116         drvc = di->priv;
117         b = buf;
118         retry = 0;
119         devices = NULL;
120
121         /*
122          * There's no way to get an ID from the multimeter. It just sends data
123          * periodically, so the best we can do is check if the packets match
124          * the expected format.
125          */
126         while (!devices && retry < 3) {
127                 good_packets = 0;
128                 retry++;
129                 serial_flush(fd);
130
131                 /* Let's get a bit of data and see if we can find a packet. */
132                 len = sizeof(buf);
133                 serial_readline(fd, &b, &len, 250);
134                 if ((len == 0) || (len < RS_22_812_PACKET_SIZE)) {
135                         /* Not enough data received, is the DMM connected? */
136                         continue;
137                 }
138
139                 /* Treat our buffer as stream, and find any valid packets. */
140                 for (i = 0; i < len - RS_22_812_PACKET_SIZE + 1;) {
141                         rs_packet = (void *)(&buf[i]);
142                         if (!rs_22_812_packet_valid(rs_packet)) {
143                                 i++;
144                                 continue;
145                         }
146                         good_packets++;
147                         i += RS_22_812_PACKET_SIZE;
148                 }
149
150                 /* If we dropped more than two packets, something is wrong. */
151                 dropped = len - (good_packets * RS_22_812_PACKET_SIZE);
152                 if (dropped > 2 * RS_22_812_PACKET_SIZE)
153                         continue;
154
155                 /* Let's see if we have anything good. */
156                 if (good_packets == 0)
157                         continue;
158
159                 sr_info("Found RadioShack 22-812 on port '%s'.", conn);
160
161                 if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "RadioShack",
162                                             "22-812", "")))
163                         return NULL;
164
165                 if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
166                         sr_err("Device context malloc failed.");
167                         return NULL;
168                 }
169
170                 devc->serial = sr_serial_dev_inst_new(conn, -1);
171                 devc->serialcomm = g_strdup(serialcomm);
172
173                 sdi->priv = devc;
174                 sdi->driver = di;
175                 if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
176                         return NULL;
177                 sdi->probes = g_slist_append(sdi->probes, probe);
178                 drvc->instances = g_slist_append(drvc->instances, sdi);
179                 devices = g_slist_append(devices, sdi);
180                 break;
181         }
182         serial_close(fd);
183
184         return devices;
185 }
186
187 static GSList *hw_scan(GSList * options)
188 {
189         struct sr_hwopt *opt;
190         GSList *l, *devices;
191         const char *conn, *serialcomm;
192
193         conn = serialcomm = NULL;
194         for (l = options; l; l = l->next) {
195                 opt = l->data;
196                 switch (opt->hwopt) {
197                 case SR_HWOPT_CONN:
198                         conn = opt->value;
199                         break;
200                 case SR_HWOPT_SERIALCOMM:
201                         serialcomm = opt->value;
202                         break;
203                 }
204         }
205         if (!conn)
206                 return NULL;
207
208         if (serialcomm) {
209                 /* Use the provided comm specs. */
210                 devices = rs_22_812_scan(conn, serialcomm);
211         } else {
212                 /* Try the default 4800/8n1. */
213                 devices = rs_22_812_scan(conn, "4800/8n1");
214         }
215
216         return devices;
217 }
218
219 static GSList *hw_dev_list(void)
220 {
221         struct drv_context *drvc;
222
223         drvc = di->priv;
224
225         return drvc->instances;
226 }
227
228 static int hw_dev_open(struct sr_dev_inst *sdi)
229 {
230         struct dev_context *devc;
231
232         if (!(devc = sdi->priv)) {
233                 sr_err("sdi->priv was NULL.");
234                 return SR_ERR_BUG;
235         }
236
237         devc->serial->fd = serial_open(devc->serial->port, O_RDONLY);
238         if (devc->serial->fd < 0) {
239                 sr_err("Couldn't open serial port '%s'.", devc->serial->port);
240                 return SR_ERR;
241         }
242         if (serial_set_paramstr(devc->serial->fd, devc->serialcomm) != SR_OK) {
243                 sr_err("Unable to set serial parameters.");
244                 return SR_ERR;
245         }
246         sdi->status = SR_ST_ACTIVE;
247
248         return SR_OK;
249 }
250
251 static int hw_dev_close(struct sr_dev_inst *sdi)
252 {
253         struct dev_context *devc;
254
255         if (!(devc = sdi->priv)) {
256                 sr_err("sdi->priv was NULL.");
257                 return SR_ERR_BUG;
258         }
259
260         if (devc->serial && devc->serial->fd != -1) {
261                 serial_close(devc->serial->fd);
262                 devc->serial->fd = -1;
263                 sdi->status = SR_ST_INACTIVE;
264         }
265
266         return SR_OK;
267 }
268
269 static int hw_cleanup(void)
270 {
271         clear_instances();
272
273         return SR_OK;
274 }
275
276 static int hw_info_get(int info_id, const void **data,
277                        const struct sr_dev_inst *sdi)
278 {
279         (void)sdi;
280
281         switch (info_id) {
282         case SR_DI_HWOPTS:
283                 *data = hwopts;
284                 break;
285         case SR_DI_HWCAPS:
286                 *data = hwcaps;
287                 break;
288         case SR_DI_NUM_PROBES:
289                 *data = GINT_TO_POINTER(1);
290                 break;
291         case SR_DI_PROBE_NAMES:
292                 *data = probe_names;
293                 break;
294         default:
295                 sr_err("Unknown info_id: %d.", info_id);
296                 return SR_ERR_ARG;
297         }
298
299         return SR_OK;
300 }
301
302 static int hw_dev_config_set(const struct sr_dev_inst *sdi, int hwcap,
303                              const void *value)
304 {
305         struct dev_context *devc;
306
307         if (sdi->status != SR_ST_ACTIVE)
308                 return SR_ERR;
309
310         if (!(devc = sdi->priv)) {
311                 sr_err("sdi->priv was NULL.");
312                 return SR_ERR_BUG;
313         }
314
315         switch (hwcap) {
316         case SR_HWCAP_LIMIT_SAMPLES:
317                 devc->limit_samples = *(const uint64_t *)value;
318                 sr_dbg("Setting sample limit to %" PRIu64 ".",
319                        devc->limit_samples);
320                 break;
321         default:
322                 sr_err("Unknown capability: %d.", hwcap);
323                 return SR_ERR_ARG;
324                 break;
325         }
326
327         return SR_OK;
328 }
329
330 static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi,
331                                     void *cb_data)
332 {
333         struct sr_datafeed_packet packet;
334         struct sr_datafeed_header header;
335         struct sr_datafeed_meta_analog meta;
336         struct dev_context *devc;
337
338         if (!(devc = sdi->priv)) {
339                 sr_err("sdi->priv was NULL.");
340                 return SR_ERR_BUG;
341         }
342
343         sr_dbg("Starting acquisition.");
344
345         devc->cb_data = cb_data;
346
347         /*
348          * Reset the number of samples to take. If we've already collected our
349          * quota, but we start a new session, and don't reset this, we'll just
350          * quit without aquiring any new samples.
351          */
352         devc->num_samples = 0;
353
354         /* Send header packet to the session bus. */
355         sr_dbg("Sending SR_DF_HEADER.");
356         packet.type = SR_DF_HEADER;
357         packet.payload = (uint8_t *) & header;
358         header.feed_version = 1;
359         gettimeofday(&header.starttime, NULL);
360         sr_session_send(devc->cb_data, &packet);
361
362         /* Send metadata about the SR_DF_ANALOG packets to come. */
363         sr_dbg("Sending SR_DF_META_ANALOG.");
364         packet.type = SR_DF_META_ANALOG;
365         packet.payload = &meta;
366         meta.num_probes = 1;
367         sr_session_send(devc->cb_data, &packet);
368
369         /* Poll every 50ms, or whenever some data comes in. */
370         sr_source_add(devc->serial->fd, G_IO_IN, 50,
371                       radioshack_dmm_receive_data, (void *)sdi);
372
373         return SR_OK;
374 }
375
376 static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
377 {
378         struct sr_datafeed_packet packet;
379         struct dev_context *devc;
380
381         if (sdi->status != SR_ST_ACTIVE)
382                 return SR_ERR;
383
384         if (!(devc = sdi->priv)) {
385                 sr_err("sdi->priv was NULL.");
386                 return SR_ERR_BUG;
387         }
388
389         sr_dbg("Stopping acquisition.");
390
391         sr_source_remove(devc->serial->fd);
392         hw_dev_close((struct sr_dev_inst *)sdi);
393
394         /* Send end packet to the session bus. */
395         sr_dbg("Sending SR_DF_END.");
396         packet.type = SR_DF_END;
397         sr_session_send(cb_data, &packet);
398
399         return SR_OK;
400 }
401
402 SR_PRIV struct sr_dev_driver radioshackdmm_driver_info = {
403         .name = "radioshack-dmm",
404         .longname = "RadioShack 22-812/22-039 DMMs",
405         .api_version = 1,
406         .init = hw_init,
407         .cleanup = hw_cleanup,
408         .scan = hw_scan,
409         .dev_list = hw_dev_list,
410         .dev_clear = clear_instances,
411         .dev_open = hw_dev_open,
412         .dev_close = hw_dev_close,
413         .info_get = hw_info_get,
414         .dev_config_set = hw_dev_config_set,
415         .dev_acquisition_start = hw_dev_acquisition_start,
416         .dev_acquisition_stop = hw_dev_acquisition_stop,
417         .priv = NULL,
418 };