]> sigrok.org Git - libsigrok.git/blob - src/hardware/icstation-usbrelay/protocol.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / icstation-usbrelay / protocol.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2021-2023 Frank Stettner <frank-stettner@gmx.net>
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 <config.h>
21
22 #include "protocol.h"
23
24 #define SERIAL_TIMEOUT_MS               1000
25
26 #define ICSTATION_USBRELAY_CMD_ID       0x50
27 #define ICSTATION_USBRELAY_CMD_START    0x51
28
29 static int icstation_usbrelay_send_byte(struct sr_serial_dev_inst *serial,
30         uint8_t b)
31 {
32         int ret;
33
34         ret = serial_write_blocking(serial, &b, sizeof(b), SERIAL_TIMEOUT_MS);
35         if (ret < SR_OK)
36                 return SR_ERR_IO;
37         if ((size_t)ret != sizeof(b))
38                 return SR_ERR_IO;
39
40         return SR_OK;
41 }
42
43 static int icstation_usbrelay_recv_byte(struct sr_serial_dev_inst *serial,
44         uint8_t *b)
45 {
46         int ret;
47
48         ret = serial_read_blocking(serial, b, sizeof(*b), SERIAL_TIMEOUT_MS);
49         if (ret < SR_OK)
50                 return SR_ERR_IO;
51         if ((size_t)ret != sizeof(*b))
52                 return SR_ERR_IO;
53
54         return SR_OK;
55 }
56
57 SR_PRIV int icstation_usbrelay_identify(struct sr_serial_dev_inst *serial,
58         uint8_t *id)
59 {
60         int ret;
61
62         if (!id)
63                 return SR_ERR_ARG;
64
65         /*
66          * Send the identification request. Receive the device firmware's
67          * identification response.
68          *
69          * BEWARE!
70          * A vendor firmware implementation detail prevents the host from
71          * identifying the device again once command mode was entered.
72          * The UART protocol provides no means to leave command mode.
73          * The subsequent identification request is mistaken instead as
74          * another relay control request! Identifying the device will fail.
75          * The device must be power cycled before it identifies again.
76          */
77         ret = icstation_usbrelay_send_byte(serial, ICSTATION_USBRELAY_CMD_ID);
78         if (ret != SR_OK) {
79                 sr_dbg("Could not send identification request.");
80                 return SR_ERR_IO;
81         }
82         ret = icstation_usbrelay_recv_byte(serial, id);
83         if (ret != SR_OK) {
84                 sr_dbg("Could not receive identification response.");
85                 return SR_ERR_IO;
86         }
87         sr_dbg("Identification response 0x%02hhx.", *id);
88
89         return SR_OK;
90 }
91
92 SR_PRIV int icstation_usbrelay_start(const struct sr_dev_inst *sdi)
93 {
94         struct sr_serial_dev_inst *serial;
95
96         if (!sdi)
97                 return SR_ERR_ARG;
98         serial = sdi->conn;
99         if (!serial)
100                 return SR_ERR_ARG;
101
102         return icstation_usbrelay_send_byte(serial,
103                 ICSTATION_USBRELAY_CMD_START);
104 }
105
106 SR_PRIV int icstation_usbrelay_switch_cg(const struct sr_dev_inst *sdi,
107         const struct sr_channel_group *cg, gboolean on)
108 {
109         struct dev_context *devc;
110         struct channel_group_context *cgc;
111         uint8_t state, mask;
112         uint8_t tx_state;
113
114         devc = sdi->priv;
115
116         /*
117          * The device requires the communication of all relay states
118          * at the same time. Calling applications control individual
119          * relays. The device wants active-low state in the physical
120          * transport. Application uses positive logic (active-high).
121          *
122          * Update the locally cached state from the most recent request.
123          * Invert the result and send it to the device. Only update
124          * the internal cache after successful transmission.
125          */
126
127         state = devc->relay_state;
128         if (!cg) {
129                 /* Set all relays. */
130                 if (on)
131                         state |= devc->relay_mask;
132                 else
133                         state &= ~devc->relay_mask;
134         } else {
135                 cgc = cg->priv;
136                 mask = 1UL << cgc->index;
137                 if (on)
138                         state |= mask;
139                 else
140                         state &= ~mask;
141         }
142
143         tx_state = ~state & devc->relay_mask;
144         sr_spew("Sending status byte: %x", tx_state);
145         if (icstation_usbrelay_send_byte(sdi->conn, tx_state) != SR_OK) {
146                 sr_err("Unable to send status byte.");
147                 return SR_ERR_IO;
148         }
149
150         devc->relay_state = state;
151
152         return SR_OK;
153 }