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