]> sigrok.org Git - libsigrok.git/blob - src/hardware/colead-slm/protocol.c
Consistency and whitespace fixes for switch statements.
[libsigrok.git] / src / hardware / colead-slm / protocol.c
1 /*
2  * This file is part of the libsigrok 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 <stdlib.h>
21 #include <glib.h>
22 #include "libsigrok.h"
23 #include "libsigrok-internal.h"
24 #include "protocol.h"
25 #include <errno.h>
26 #include <string.h>
27
28 static void process_packet(const struct sr_dev_inst *sdi)
29 {
30         struct dev_context *devc;
31         struct sr_datafeed_packet packet;
32         struct sr_datafeed_analog analog;
33         GString *dbg;
34         float fvalue;
35         int checksum, mode, i;
36
37         devc = sdi->priv;
38         if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
39                 dbg = g_string_sized_new(128);
40                 g_string_printf(dbg, "received packet:");
41                 for (i = 0; i < 10; i++)
42                         g_string_append_printf(dbg, " %.2x", (devc->buf)[i]);
43                 sr_spew("%s", dbg->str);
44                 g_string_free(dbg, TRUE);
45         }
46
47         if (devc->buf[0] != 0x08 || devc->buf[1] != 0x04) {
48                 sr_dbg("invalid packet header.");
49                 return;
50         }
51
52         if (devc->buf[8] != 0x01) {
53                 sr_dbg("invalid measurement.");
54                 return;
55         }
56
57         checksum = 0;
58         for (i = 0; i < 9; i++)
59                 checksum += devc->buf[i];
60         if ((checksum & 0xff) != devc->buf[9]) {
61                 sr_dbg("invalid packet checksum.");
62                 return;
63         }
64
65         fvalue = 0.0;
66         for (i = 3; i < 8; i++) {
67                 if (devc->buf[i] > 0x09)
68                         continue;
69                 fvalue *= 10;
70                 fvalue += devc->buf[i];
71         }
72         fvalue /= 10;
73
74         memset(&analog, 0, sizeof(struct sr_datafeed_analog));
75         analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
76         analog.unit = SR_UNIT_DECIBEL_SPL;
77         analog.channels = sdi->channels;
78         analog.num_samples = 1;
79         analog.data = &fvalue;
80
81         /* High nibble should only have 0x01 or 0x02. */
82         mode = (devc->buf[2] >> 4) & 0x0f;
83         if (mode == 0x02)
84                 analog.mqflags |= SR_MQFLAG_HOLD;
85         else if (mode != 0x01) {
86                 sr_dbg("unknown measurement mode 0x%.2x", mode);
87                 return;
88         }
89
90         /* Low nibble has 14 combinations of direct/long-term average,
91          * time scale of that average, frequency weighting, and time
92          * weighting. */
93         mode = devc->buf[2] & 0x0f;
94         switch (mode) {
95         case 0x0:
96                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
97                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
98                 break;
99         case 0x1:
100                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
101                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
102                 break;
103         case 0x2:
104                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
105                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
106                 break;
107         case 0x3:
108                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
109                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
110                 break;
111         case 0x4:
112                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
113                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
114                 break;
115         case 0x5:
116                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
117                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
118                 break;
119         case 0x6:
120                 analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
121                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
122                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
123                 break;
124         case 0x7:
125                 analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
126                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
127                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
128                 break;
129         case 0x8:
130                 /* 10-second mean, but we don't have MQ flags to express it. */
131                 analog.mqflags |= SR_MQFLAG_SPL_LAT \
132                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
133                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
134                 break;
135         case 0x9:
136                 /* Mean over a time period between 11 seconds and 24 hours.
137                  * Which is so silly that there's no point in expressing
138                  * either this or the previous case.  */
139                 analog.mqflags |= SR_MQFLAG_SPL_LAT \
140                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
141                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
142                 break;
143         case 0xa:
144                 /* 10-second mean. */
145                 analog.mqflags |= SR_MQFLAG_SPL_LAT \
146                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
147                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
148                 break;
149         case 0xb:
150                 /* Mean over a time period between 11 seconds and 24 hours. */
151                 analog.mqflags |= SR_MQFLAG_SPL_LAT \
152                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
153                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
154                 break;
155         case 0xc:
156                 /* Internal calibration on 1kHz sine at 94dB, not useful
157                  * to anything but the device. */
158                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
159                 break;
160         case 0xd:
161                 /* Internal calibration on 1kHz sine at 94dB, not useful
162                  * to anything but the device. */
163                 analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
164                 break;
165         default:
166                 sr_dbg("unknown configuration 0x%.2x", mode);
167                 return;
168         }
169
170         packet.type = SR_DF_ANALOG;
171         packet.payload = &analog;
172         sr_session_send(devc->cb_data, &packet);
173
174         devc->num_samples++;
175         if (devc->num_samples >= devc->limit_samples)
176                 sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi,
177                                 devc->cb_data);
178
179 }
180
181 SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
182 {
183         const struct sr_dev_inst *sdi;
184         struct dev_context *devc;
185         struct sr_serial_dev_inst *serial;
186         int delay_ms, len;
187         char buf[128];
188
189         (void)fd;
190
191         if (!(sdi = cb_data))
192                 return TRUE;
193
194         if (!(devc = sdi->priv))
195                 return TRUE;
196
197         if (revents != G_IO_IN)
198                 /* Timeout event. */
199                 return TRUE;
200
201         serial = sdi->conn;
202         if (devc->state == IDLE) {
203                 if (serial_read_nonblocking(serial, buf, 128) != 1 || buf[0] != 0x10)
204                         /* Nothing there, or caught the tail end of a previous packet,
205                          * or some garbage. Unless it's a single "data ready" byte,
206                          * we don't want it. */
207                         return TRUE;
208                 /* Got 0x10, "measurement ready". */
209                 delay_ms = serial_timeout(serial, 1);
210                 if (serial_write_blocking(serial, "\x20", 1, delay_ms) < 1)
211                         sr_err("unable to send command");
212                 else {
213                         devc->state = COMMAND_SENT;
214                         devc->buflen = 0;
215                 }
216         } else {
217                 len = serial_read_nonblocking(serial, devc->buf + devc->buflen,
218                                 10 - devc->buflen);
219                 if (len < 1)
220                         return TRUE;
221                 devc->buflen += len;
222                 if (devc->buflen > 10) {
223                         sr_dbg("buffer overrun");
224                         devc->state = IDLE;
225                         return TRUE;
226                 }
227                 if (devc->buflen == 10) {
228                         /* If we got here, we're sure the device sent a "data ready"
229                          * notification, we asked for data, and got it. */
230                         process_packet(sdi);
231                         devc->state = IDLE;
232                 }
233         }
234
235         return TRUE;
236 }