]> sigrok.org Git - libsigrok.git/blame - src/hardware/icstation-usbrelay/protocol.c
icstation-usbrelay: Initial ICStation USBRelay driver.
[libsigrok.git] / src / hardware / icstation-usbrelay / protocol.c
CommitLineData
dfa92ad4
FS
1/*
2 * This file is part of the libsigrok project.
3 *
cefdd911 4 * Copyright (C) 2021-2023 Frank Stettner <frank-stettner@gmx.net>
dfa92ad4
FS
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>
cefdd911 21
dfa92ad4
FS
22#include "protocol.h"
23
cefdd911
FS
24#define SERIAL_TIMEOUT_MS 1000
25
26#define ICSTATION_USBRELAY_CMD_ID 0x50
27#define ICSTATION_USBRELAY_CMD_START 0x51
28
29static 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
43static 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
57SR_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
92SR_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
106SR_PRIV int icstation_usbrelay_switch_cg(const struct sr_dev_inst *sdi,
107 const struct sr_channel_group *cg, gboolean on)
dfa92ad4 108{
dfa92ad4 109 struct dev_context *devc;
cefdd911
FS
110 struct channel_group_context *cgc;
111 uint8_t state, mask;
112 uint8_t tx_state;
dfa92ad4 113
cefdd911 114 devc = sdi->priv;
dfa92ad4 115
cefdd911
FS
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 */
dfa92ad4 126
cefdd911
FS
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 }
dfa92ad4 142
cefdd911
FS
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;
dfa92ad4
FS
148 }
149
cefdd911
FS
150 devc->relay_state = state;
151
152 return SR_OK;
dfa92ad4 153}