xref: /openbsd-src/sys/dev/usb/utwitch.c (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1 /*	$OpenBSD: utwitch.c,v 1.22 2021/11/15 15:36:24 anton Exp $ */
2 
3 /*
4  * Copyright (c) 2010 Yojiro UO <yuo@nui.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* Driver for Maywa-Denki & KAYAC YUREX BBU sensor */
20 /* this driver was previously known as uyurex(4). */
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/kernel.h>
25 #include <sys/malloc.h>
26 #include <sys/device.h>
27 #include <sys/conf.h>
28 #include <sys/sensors.h>
29 
30 #include <dev/usb/usb.h>
31 #include <dev/usb/usbhid.h>
32 #include <dev/usb/usbdi.h>
33 #include <dev/usb/usbdi_util.h>
34 #include <dev/usb/usbdevs.h>
35 #include <dev/usb/uhidev.h>
36 
37 #define	CMD_NONE	0xf0
38 #define CMD_EOF		0x0d
39 #define CMD_ACK		0x21
40 #define	CMD_MODE	0x41 /* XXX */
41 #define	CMD_VALUE	0x43
42 #define CMD_READ	0x52
43 #define CMD_WRITE	0x53
44 #define CMD_PADDING	0xff
45 
46 #define UPDATE_TICK	5 /* sec */
47 
48 #ifdef UYUREX_DEBUG
49 #define DPRINTF(x)	do { printf x; } while (0)
50 #else
51 #define DPRINTF(x)
52 #endif
53 
54 struct utwitch_softc {
55 	struct uhidev		 sc_hdev;
56 	struct usbd_device	*sc_udev;
57 
58 	/* uhidev parameters */
59 	size_t			 sc_ilen;	/* input report length */
60 	size_t			 sc_olen;	/* output report length */
61 
62 	uint8_t			*sc_ibuf;
63 
64 	/* sensor framework */
65 	struct ksensor		 sc_sensor_val;
66 	struct ksensor		 sc_sensor_delta;
67 	struct ksensordev	 sc_sensordev;
68 	struct sensor_task	*sc_sensortask;
69 
70 	/* device private */
71 	int			 sc_initialized;
72 	uint8_t			 issueing_cmd;
73 	uint8_t			 accepted_cmd;
74 
75 	uint32_t		 sc_curval;
76 	uint32_t		 sc_oldval;
77 };
78 
79 const struct usb_devno utwitch_devs[] = {
80 	{ USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_YUREX},
81 };
82 
83 int utwitch_match(struct device *, void *, void *);
84 void utwitch_attach(struct device *, struct device *, void *);
85 int utwitch_detach(struct device *, int);
86 
87 void utwitch_set_mode(struct utwitch_softc *, uint8_t);
88 void utwitch_read_value_request(struct utwitch_softc *);
89 void utwitch_write_value_request(struct utwitch_softc *, uint32_t);
90 
91 void utwitch_intr(struct uhidev *, void *, u_int);
92 void utwitch_refresh(void *);
93 
94 struct cfdriver utwitch_cd = {
95 	NULL, "utwitch", DV_DULL
96 };
97 
98 const struct cfattach utwitch_ca = {
99 	sizeof(struct utwitch_softc),
100 	utwitch_match,
101 	utwitch_attach,
102 	utwitch_detach
103 };
104 
105 int
106 utwitch_match(struct device *parent, void *match, void *aux)
107 {
108 	struct uhidev_attach_arg *uha = aux;
109 
110 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
111 		return (UMATCH_NONE);
112 
113 	return (usb_lookup(utwitch_devs, uha->uaa->vendor, uha->uaa->product) != NULL ?
114 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
115 }
116 
117 void
118 utwitch_attach(struct device *parent, struct device *self, void *aux)
119 {
120 	struct utwitch_softc *sc = (struct utwitch_softc *)self;
121 	struct usb_attach_arg *uaa = aux;
122 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
123 	struct usbd_device *dev = uha->parent->sc_udev;
124 	int size, repid, err;
125 	void *desc;
126 
127 	sc->sc_udev = dev;
128 	sc->sc_hdev.sc_intr = utwitch_intr;
129 	sc->sc_hdev.sc_parent = uha->parent;
130 	sc->sc_hdev.sc_report_id = uha->reportid;
131 
132 	uhidev_get_report_desc(uha->parent, &desc, &size);
133 	repid = uha->reportid;
134 	sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
135 	sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
136 
137 	err = uhidev_open(&sc->sc_hdev);
138 	if (err) {
139 		printf("%s: uhidev_open %d\n", __func__, err);
140 		return;
141 	}
142 	sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK);
143 
144 	printf("\n");
145 
146 
147 	/* attach sensor */
148 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
149 	    sizeof(sc->sc_sensordev.xname));
150 
151 	/* add BBU sensor */
152 	sc->sc_sensor_val.type = SENSOR_INTEGER;
153 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_val);
154 	strlcpy(sc->sc_sensor_val.desc, "BBU",
155 		sizeof(sc->sc_sensor_val.desc));
156 
157 	/* add BBU delta sensor */
158 	sc->sc_sensor_delta.type = SENSOR_INTEGER;
159 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_delta);
160 	strlcpy(sc->sc_sensor_delta.desc, "mBBU/sec",
161 		sizeof(sc->sc_sensor_delta.desc));
162 
163 	sc->sc_sensortask = sensor_task_register(sc, utwitch_refresh, UPDATE_TICK);
164 	if (sc->sc_sensortask == NULL) {
165 		printf(", unable to register update task\n");
166 		return;
167 	}
168 	sensordev_install(&sc->sc_sensordev);
169 
170 	DPRINTF(("utwitch_attach: complete\n"));
171 
172 	/* init device */ /* XXX */
173 	utwitch_set_mode(sc, 0);
174 }
175 
176 int
177 utwitch_detach(struct device *self, int flags)
178 {
179 	struct utwitch_softc *sc = (struct utwitch_softc *)self;
180 	int rv = 0;
181 
182 	wakeup(&sc->sc_sensortask);
183 	sensordev_deinstall(&sc->sc_sensordev);
184 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_val);
185 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_delta);
186 	if (sc->sc_sensortask != NULL)
187 		sensor_task_unregister(sc->sc_sensortask);
188 
189 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
190 		uhidev_close(&sc->sc_hdev);
191 
192 	if (sc->sc_ibuf != NULL) {
193 		free(sc->sc_ibuf, M_USBDEV, sc->sc_ilen);
194 		sc->sc_ibuf = NULL;
195 	}
196 
197 	return (rv);
198 }
199 
200 void
201 utwitch_intr(struct uhidev *addr, void *ibuf, u_int len)
202 {
203 	struct utwitch_softc *sc = (struct utwitch_softc *)addr;
204 	uint8_t buf[8];
205 	uint32_t val;
206 
207 	if (sc->sc_ibuf == NULL)
208 		return;
209 
210 	/* process requests */
211 	memcpy(buf, ibuf, 8);
212 	DPRINTF(("intr: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
213 		buf[0], buf[1], buf[2], buf[3],
214 		buf[4], buf[5], buf[6], buf[7]));
215 
216 
217 	switch (buf[0]) {
218 	case CMD_ACK:
219 		if (buf[1] == sc->issueing_cmd) {
220 			DPRINTF(("ack received for cmd 0x%.2x\n", buf[1]));
221 			sc->accepted_cmd = buf[1];
222 		} else {
223 			DPRINTF(("cmd-ack mismatch: recved 0x%.2x, expect 0x%.2x\n",
224 				buf[1], sc->issueing_cmd));
225 			/* discard previous command */
226 			sc->accepted_cmd = CMD_NONE;
227 			sc->issueing_cmd = CMD_NONE;
228 		}
229 		break;
230 	case CMD_READ:
231 	case CMD_VALUE:
232 		val = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8)  + buf[5];
233 		if (!sc->sc_initialized) {
234 			sc->sc_oldval = val;
235 			sc->sc_initialized = 1;
236 		}
237 		sc->sc_sensor_val.value = val;
238 		sc->sc_curval = val;
239 		DPRINTF(("recv value update message: %d\n", val));
240 		break;
241 	default:
242 		DPRINTF(("unknown message: 0x%.2x\n", buf[0]));
243 	}
244 
245 	return;
246 }
247 
248 void
249 utwitch_refresh(void *arg)
250 {
251 	struct utwitch_softc *sc = arg;
252 
253 	if (!sc->sc_initialized) {
254 		utwitch_read_value_request(sc);
255 	} else {
256 		/* calculate delta value */
257 		sc->sc_sensor_delta.value =
258 			(1000 * (sc->sc_curval - sc->sc_oldval)) / UPDATE_TICK;
259 		sc->sc_oldval = sc->sc_curval;
260 	}
261 }
262 
263 void
264 utwitch_set_mode(struct utwitch_softc *sc, uint8_t val)
265 {
266 	uint8_t req[8];
267 	int olen;
268 
269 	olen = MIN(sc->sc_olen, sizeof(req));
270 	memset(req, CMD_PADDING, sizeof(req));
271 	req[0] = CMD_MODE;
272 	req[1] = val;
273 	req[2] = CMD_EOF;
274 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
275 	    sc->sc_hdev.sc_report_id, req, olen) != olen) {
276 		printf("uhidev_set_report error:EIO\n");
277 		return;
278 	}
279 
280 	/* wait ack */
281 	tsleep_nsec(&sc->sc_sensortask, 0, "utwitch", MSEC_TO_NSEC(1000));
282 }
283 
284 void
285 utwitch_read_value_request(struct utwitch_softc *sc)
286 {
287 	uint8_t req[8];
288 	int olen;
289 
290 	olen = MIN(sc->sc_olen, sizeof(req));
291 	memset(req, CMD_PADDING, sizeof(req));
292 	req[0] = CMD_READ;
293 	req[1] = CMD_EOF;
294 	sc->issueing_cmd = CMD_READ;
295 	sc->accepted_cmd = CMD_NONE;
296 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
297 	    sc->sc_hdev.sc_report_id, req, olen) != olen)
298 		return;
299 
300 	/* wait till sensor data are updated, 500ms will be enough */
301 	tsleep_nsec(&sc->sc_sensortask, 0, "utwitch", MSEC_TO_NSEC(500));
302 }
303 
304 void
305 utwitch_write_value_request(struct utwitch_softc *sc, uint32_t val)
306 {
307 	uint32_t v;
308 	uint8_t req[8];
309 	int olen;
310 
311 	olen = MIN(sc->sc_olen, sizeof(req));
312 	req[0] = CMD_WRITE;
313 	req[1] = 0;
314 	req[6] = CMD_EOF;
315 	req[7] = CMD_PADDING;
316 	v = htobe32(val);
317 	memcpy(req + 2, &v, sizeof(uint32_t));
318 
319 	sc->issueing_cmd = CMD_WRITE;
320 	sc->accepted_cmd = CMD_NONE;
321 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
322 	    sc->sc_hdev.sc_report_id, req, olen) != olen)
323 		return;
324 
325 	/* wait till sensor data are updated, 250ms will be enough */
326 	tsleep_nsec(&sc->sc_sensortask, 0, "utwitch", MSEC_TO_NSEC(250));
327 }
328