dcttech-usbrelay: implement multiplexer driver for USB relay card
[libsigrok.git] / src / hardware / dcttech-usbrelay / protocol.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2021 Gerhard Sittig <gerhard.sittig@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 <string.h>
23
24 #include "protocol.h"
25
26 SR_PRIV int dcttech_usbrelay_update_state(const struct sr_dev_inst *sdi)
27 {
28         struct dev_context *devc;
29         uint8_t report[1 + REPORT_BYTECOUNT];
30         int ret;
31         GString *txt;
32
33         devc = sdi->priv;
34
35         /* Get another HID report. */
36         memset(report, 0, sizeof(report));
37         report[0] = REPORT_NUMBER;
38         ret = hid_get_feature_report(devc->hid_dev, report, sizeof(report));
39         if (ret != sizeof(report))
40                 return SR_ERR_IO;
41         if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
42                 txt = sr_hexdump_new(report, sizeof(report));
43                 sr_spew("got report bytes: %s", txt->str);
44                 sr_hexdump_free(txt);
45         }
46
47         /* Update relay state cache from HID report content. */
48         devc->relay_state = report[1 + STATE_INDEX];
49         devc->relay_state &= devc->relay_mask;
50
51         return SR_OK;
52 }
53
54 SR_PRIV int dcttech_usbrelay_switch_cg(const struct sr_dev_inst *sdi,
55         const struct sr_channel_group *cg, gboolean on)
56 {
57         struct dev_context *devc;
58         struct channel_group_context *cgc;
59         gboolean is_all;
60         size_t relay_idx;
61         uint8_t report[1 + REPORT_BYTECOUNT];
62         int ret;
63         GString *txt;
64
65         devc = sdi->priv;
66
67         /* Determine if all or a single relay should be turned off or on. */
68         is_all = !cg ? TRUE : FALSE;
69         if (is_all) {
70                 relay_idx = 0;
71         } else {
72                 cgc = cg->priv;
73                 relay_idx = cgc->number;
74         }
75
76         /*
77          * Construct and send the HID report. Notice the weird(?) bit
78          * pattern. Bit 1 is low when all relays are affected at once,
79          * and high to control an individual relay? Bit 0 communicates
80          * whether the relay(s) should be on or off? And all other bits
81          * are always set? It's assumed that the explicit assignment of
82          * full byte values simplifies future maintenance.
83          */
84         memset(report, 0, sizeof(report));
85         report[0] = REPORT_NUMBER;
86         if (is_all) {
87                 if (on) {
88                         report[1] = 0xfe;
89                 } else {
90                         report[1] = 0xfc;
91                 }
92         } else {
93                 if (on) {
94                         report[1] = 0xff;
95                         report[2] = relay_idx;
96                 } else {
97                         report[1] = 0xfd;
98                         report[2] = relay_idx;
99                 }
100         }
101         if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
102                 txt = sr_hexdump_new(report, sizeof(report));
103                 sr_spew("sending report bytes: %s", txt->str);
104                 sr_hexdump_free(txt);
105         }
106         ret = hid_send_feature_report(devc->hid_dev, report, sizeof(report));
107         if (ret != sizeof(report))
108                 return SR_ERR_IO;
109
110         /* Update relay state cache (non-fatal). */
111         (void)dcttech_usbrelay_update_state(sdi);
112
113         return SR_OK;
114 }
115
116 /* Answers the query from cached relay state. Beware of 1-based indexing. */
117 SR_PRIV int dcttech_usbrelay_query_cg(const struct sr_dev_inst *sdi,
118         const struct sr_channel_group *cg, gboolean *on)
119 {
120         struct dev_context *devc;
121         struct channel_group_context *cgc;
122         size_t relay_idx;
123         uint32_t relay_mask;
124
125         devc = sdi->priv;
126         if (!cg)
127                 return SR_ERR_ARG;
128         cgc = cg->priv;
129         relay_idx = cgc->number;
130         if (relay_idx < 1 || relay_idx > devc->relay_count)
131                 return SR_ERR_ARG;
132         relay_mask = 1U << (relay_idx - 1);
133
134         *on = devc->relay_state & relay_mask;
135
136         return SR_OK;
137 }