]> sigrok.org Git - libsigrok.git/blame - src/hardware/chromium-twinkie/protocol.c
chromium-twinkie: add analog VBUS channels
[libsigrok.git] / src / hardware / chromium-twinkie / protocol.c
CommitLineData
744e43bc
VP
1/*
2 * This file is part of the libsigrok project.
3 *
fcc65337 4 * Copyright 2017 Google, Inc
744e43bc
VP
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 2 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#include <stdint.h>
22#include <string.h>
23#include <libusb.h>
24#include <stdio.h>
25#include <errno.h>
26#include <math.h>
27#include <libsigrok/libsigrok.h>
28#include "libsigrok-internal.h"
29#include "protocol.h"
30
fcc65337
VP
31/* 'twinkie vbus' command output format */
32#define VBUS_FORMAT "VBUS = %d mV ; %d mA"
33
744e43bc
VP
34SR_PRIV int twinkie_start_acquisition(const struct sr_dev_inst *sdi)
35{
fcc65337
VP
36 struct dev_context *devc = sdi->priv;
37 struct timespec tsample;
38
39 clock_gettime(CLOCK_REALTIME, &tsample);
40 devc->vbus_t0 = tsample.tv_nsec + (uint64_t)tsample.tv_sec *1000000000ULL;
744e43bc
VP
41
42 return SR_OK;
43}
44
45SR_PRIV int twinkie_init_device(const struct sr_dev_inst *sdi)
46{
47 (void)sdi;
48
49 return SR_OK;
50}
51
52static void finish_acquisition(struct sr_dev_inst *sdi)
53{
54 struct sr_datafeed_packet packet;
55 struct dev_context *devc = sdi->priv;
56
57 /* Terminate session. */
58 packet.type = SR_DF_END;
59 sr_session_send(sdi, &packet);
60
61 /* Remove fds from polling. */
62 usb_source_remove(sdi->session, devc->ctx);
63
64 devc->num_transfers = 0;
65 g_free(devc->transfers);
66 g_free(devc->convbuffer);
67}
68
69static void free_transfer(struct libusb_transfer *transfer)
70{
71 struct sr_dev_inst *sdi;
72 struct dev_context *devc;
73 unsigned int i;
74
75 sdi = transfer->user_data;
76 devc = sdi->priv;
77
78 g_free(transfer->buffer);
79 transfer->buffer = NULL;
80 libusb_free_transfer(transfer);
81
82 for (i = 0; i < devc->num_transfers; i++) {
83 if (devc->transfers[i] == transfer) {
84 devc->transfers[i] = NULL;
85 break;
86 }
87 }
88
89 devc->submitted_transfers--;
90 if (devc->submitted_transfers == 0)
91 finish_acquisition(sdi);
92}
93
94static void export_samples(const struct sr_dev_inst *sdi, size_t cnt)
95{
96 struct sr_datafeed_packet packet;
97 struct sr_datafeed_logic logic;
98 struct dev_context *devc = sdi->priv;
99
100 /* export the received data */
101 packet.type = SR_DF_LOGIC;
102 packet.payload = &logic;
103 if (devc->limit_samples &&
104 cnt > devc->limit_samples - devc->sent_samples)
105 cnt = devc->limit_samples - devc->sent_samples;
106 logic.length = cnt;
107 logic.unitsize = 1;
108 logic.data = devc->convbuffer;
109 sr_session_send(sdi, &packet);
110 devc->sent_samples += cnt;
111}
112
113static void expand_sample_data(const struct sr_dev_inst *sdi,
114 const uint8_t *src, size_t srccnt)
115{
116 struct dev_context *devc = sdi->priv;
117 int i, f;
118 size_t b;
119 size_t rdy_samples, left_samples;
120 int frames = srccnt / 64;
121
122 for (f = 0; f < frames; f++) {
123 int ch = (src[1] >> 4) & 3; /* samples channel number */
124 int bit = 1 << ch; /* channel bit mask */
125 struct cc_context *cc = devc->cc + ch;
126 uint8_t *dest = devc->convbuffer + cc->idx;
127
128 if (ch >= 2) /* only acquires CCx channels */
129 continue;
130
131 /* TODO: check timestamp, overflow, sequence number */
132
133 /* skip header, go to edges data */
134 src+=4;
135 for (i = 0; i < 60; i++,src++)
136 if (*src == cc->prev_src) {
137 cc->rollbacks++;
138 } else {
139 uint8_t diff = *src - cc->prev_src;
140 int fixup = cc->rollbacks && (((int)*src < (int)cc->prev_src) || (*src == 0xff));
141 size_t total = (fixup ? cc->rollbacks - 1 : cc->rollbacks) * 256 + diff;
142
143 if (total + cc->idx > devc->convbuffer_size) {
144 sr_warn("overflow %d+%zd/%zd\n",
145 cc->idx, total,
146 devc->convbuffer_size);
147 /* reset current decoding */
148 cc->rollbacks = 0;
149 break;
150 }
151
152 /* insert bits in the buffer */
153 if (cc->level)
154 for (b = 0 ; b < total ; b++, dest++)
155 *dest |= bit;
156 else
157 dest += total;
158 cc->idx += total;
159
160 /* flip level on the next edge */
161 cc->level = ~cc->level;
162
163 cc->rollbacks = 0;
164 cc->prev_src = *src;
165 }
166 /* expand repeated rollbacks */
167 if (cc->rollbacks > 1) {
168 size_t total = 256 * (cc->rollbacks - 1);
169 if (total + cc->idx > devc->convbuffer_size) {
170 sr_warn("overflow %d+%zd/%zd\n",
171 cc->idx, total, devc->convbuffer_size);
172 /* reset current decoding */
173 total = 0;
174 }
175 /* insert bits in the buffer */
176 if (cc->level)
177 for (b = 0 ; b < total ; b++, dest++)
178 *dest |= bit ;
179 cc->idx += total;
180 cc->rollbacks = 1;
181 }
182 }
183
184 /* samples ready to be pushed (with both channels) */
185 rdy_samples = MIN(devc->cc[0].idx, devc->cc[1].idx);
186 left_samples = MAX(devc->cc[0].idx, devc->cc[1].idx) - rdy_samples;
187 /* skip empty transfer */
188 if (rdy_samples == 0)
189 return;
190
191 export_samples(sdi, rdy_samples);
192
193 /* clean up what we have sent */
194 memmove(devc->convbuffer, devc->convbuffer + rdy_samples, left_samples);
195 memset(devc->convbuffer + left_samples, 0, rdy_samples);
196 devc->cc[0].idx -= rdy_samples;
197 devc->cc[1].idx -= rdy_samples;
198}
199
200SR_PRIV void LIBUSB_CALL twinkie_receive_transfer(struct libusb_transfer *transfer)
201{
202 gboolean packet_has_error = FALSE;
203 struct sr_dev_inst *sdi;
204 struct dev_context *devc;
205
206 sdi = transfer->user_data;
207 devc = sdi->priv;
208
209 /*
210 * If acquisition has already ended, just free any queued up
211 * transfer that come in.
212 */
213 if (devc->sent_samples < 0) {
214 free_transfer(transfer);
215 return;
216 }
217
218 if (transfer->status || transfer->actual_length)
219 sr_info("receive_transfer(): status %d received %d bytes.",
220 transfer->status, transfer->actual_length);
221
222 switch (transfer->status) {
223 case LIBUSB_TRANSFER_NO_DEVICE:
224 devc->sent_samples = -2;
225 free_transfer(transfer);
226 return;
227 case LIBUSB_TRANSFER_COMPLETED:
228 case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */
229 break;
230 default:
231 packet_has_error = TRUE;
232 break;
233 }
234
235 if (transfer->actual_length % 64) {
236 sr_err("Bad USB packet size.");
237 packet_has_error = TRUE;
238 }
239
240 if (transfer->actual_length == 0 || packet_has_error)
241 goto resubmit;
242
243 /* decode received edges */
244 expand_sample_data(sdi, transfer->buffer, transfer->actual_length);
245
246 if (devc->limit_samples &&
247 (uint64_t)devc->sent_samples >= devc->limit_samples) {
248 devc->sent_samples = -2;
249 free_transfer(transfer);
250 return;
251 }
252resubmit:
253 if (libusb_submit_transfer(transfer) != LIBUSB_SUCCESS)
254 free_transfer(transfer);
255}
fcc65337
VP
256
257static void export_vbus(const struct sr_dev_inst *sdi, int mv, int ma)
258{
259 static float tmp_data[VBUS_GRP_COUNT][32768];
260 struct dev_context *devc = sdi->priv;
261 struct sr_datafeed_packet packet[VBUS_GRP_COUNT];
262 uint64_t tlen = devc->vbus_delta * 24 / 10000;
263 uint64_t len = MIN(32768, tlen);
264 unsigned i;
265 int g;
266
267 for (g = 0; g < devc->vbus_channels; g++) {
268 float val = g == VBUS_V ? mv/1000.0 : ma/1000.0;
269 packet[g].type = SR_DF_ANALOG;
270 packet[g].payload = &devc->vbus_packet[g];
271 devc->vbus_packet[g].data = tmp_data[g];
272 for (i = 0; i < len; i++)
273 tmp_data[g][i] = val;
274 }
275
276 do {
277 for (g = 0; g < devc->vbus_channels; g++) {
278 devc->vbus_packet[g].num_samples = len;
279 sr_session_send(sdi, &packet[g]);
280 }
281 tlen -= len;
282 len = MIN(32768, tlen);
283 } while (tlen);
284}
285
286SR_PRIV void LIBUSB_CALL twinkie_vbus_sent(struct libusb_transfer *transfer)
287{
288 struct sr_dev_inst *sdi = transfer->user_data;
289 struct dev_context *devc = sdi->priv;
290 struct libusb_transfer *in_xfer = devc->transfers[11];
291 struct timespec tsample;
292 uint64_t now;
293
294 /* acquisition has already ended */
295 if (devc->sent_samples < 0 || transfer->status == LIBUSB_TRANSFER_NO_DEVICE)
296 goto abort_vbus;
297
298 if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
299 goto abort_vbus;
300
301 clock_gettime(CLOCK_REALTIME, &tsample);
302 now = tsample.tv_nsec + (uint64_t)tsample.tv_sec *1000000000ULL;
303 devc->vbus_delta = now - devc->vbus_t0;
304 devc->vbus_t0 = now;
305 if (libusb_submit_transfer(in_xfer) != LIBUSB_SUCCESS)
306 goto abort_vbus;
307
308 return;
309abort_vbus:
310 libusb_free_transfer(transfer);
311 libusb_free_transfer(in_xfer);
312 devc->transfers[10] = NULL;
313 devc->transfers[11] = NULL;
314 devc->submitted_transfers--;
315 if (devc->submitted_transfers == 0)
316 finish_acquisition(sdi);
317}
318
319SR_PRIV void LIBUSB_CALL twinkie_vbus_recv(struct libusb_transfer *transfer)
320{
321 struct sr_dev_inst *sdi = transfer->user_data;
322 struct dev_context *devc = sdi->priv;
323 struct libusb_transfer *out_xfer = devc->transfers[10];
324
325 /* acquisition has already ended */
326 if (devc->sent_samples < 0 || transfer->status == LIBUSB_TRANSFER_NO_DEVICE)
327 goto abort_vbus;
328
329 if (transfer->status == LIBUSB_TRANSFER_COMPLETED &&
330 transfer->actual_length) {
331 int vbus_ma, vbus_mv;
332 int len = transfer->actual_length;
333 if (len > 63)
334 len = 63;
335 devc->vbus_data[len] = 0;
336 if (sscanf(devc->vbus_data, VBUS_FORMAT, &vbus_mv, &vbus_ma) == 2) {
337 export_vbus(sdi, vbus_mv, vbus_ma);
338 }
339 }
340
341 if (libusb_submit_transfer(out_xfer) != LIBUSB_SUCCESS)
342 goto abort_vbus;
343
344 return;
345abort_vbus:
346 libusb_free_transfer(transfer);
347 libusb_free_transfer(out_xfer);
348 devc->transfers[10] = NULL;
349 devc->transfers[11] = NULL;
350 devc->submitted_transfers--;
351 if (devc->submitted_transfers == 0)
352 finish_acquisition(sdi);
353}