xref: /openbsd/sys/dev/usb/utwitch.c (revision 264ca280)
1 /*	$OpenBSD: utwitch.c,v 1.16 2016/01/09 04:14:42 jcs 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_flen;	/* feature report length */
60 	size_t			 sc_ilen;	/* input report length */
61 	size_t			 sc_olen;	/* output report length */
62 
63 	uint8_t			*sc_ibuf;
64 
65 	/* sensor framework */
66 	struct ksensor		 sc_sensor_val;
67 	struct ksensor		 sc_sensor_delta;
68 	struct ksensordev	 sc_sensordev;
69 	struct sensor_task	*sc_sensortask;
70 
71 	/* device private */
72 	int			 sc_initialized;
73 	uint8_t			 issueing_cmd;
74 	uint8_t			 accepted_cmd;
75 
76 	uint32_t		 sc_curval;
77 	uint32_t		 sc_oldval;
78 };
79 
80 const struct usb_devno utwitch_devs[] = {
81 	{ USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_YUREX},
82 };
83 
84 int utwitch_match(struct device *, void *, void *);
85 void utwitch_attach(struct device *, struct device *, void *);
86 int utwitch_detach(struct device *, int);
87 
88 void utwitch_set_mode(struct utwitch_softc *, uint8_t);
89 void utwitch_read_value_request(struct utwitch_softc *);
90 void utwitch_write_value_request(struct utwitch_softc *, uint32_t);
91 
92 void utwitch_intr(struct uhidev *, void *, u_int);
93 void utwitch_refresh(void *);
94 
95 struct cfdriver utwitch_cd = {
96 	NULL, "utwitch", DV_DULL
97 };
98 
99 const struct cfattach utwitch_ca = {
100 	sizeof(struct utwitch_softc),
101 	utwitch_match,
102 	utwitch_attach,
103 	utwitch_detach
104 };
105 
106 int
107 utwitch_match(struct device *parent, void *match, void *aux)
108 {
109 	struct uhidev_attach_arg *uha = aux;
110 
111 	if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
112 		return (UMATCH_NONE);
113 
114 	return (usb_lookup(utwitch_devs, uha->uaa->vendor, uha->uaa->product) != NULL ?
115 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
116 }
117 
118 void
119 utwitch_attach(struct device *parent, struct device *self, void *aux)
120 {
121 	struct utwitch_softc *sc = (struct utwitch_softc *)self;
122 	struct usb_attach_arg *uaa = aux;
123 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
124 	struct usbd_device *dev = uha->parent->sc_udev;
125 	int size, repid, err;
126 	void *desc;
127 
128 	sc->sc_udev = dev;
129 	sc->sc_hdev.sc_intr = utwitch_intr;
130 	sc->sc_hdev.sc_parent = uha->parent;
131 	sc->sc_hdev.sc_report_id = uha->reportid;
132 
133 	uhidev_get_report_desc(uha->parent, &desc, &size);
134 	repid = uha->reportid;
135 	sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
136 	sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
137 	sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
138 
139 	err = uhidev_open(&sc->sc_hdev);
140 	if (err) {
141 		printf("utwitch_open: uhidev_open %d\n", err);
142 		return;
143 	}
144 	sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK);
145 
146 	printf("\n");
147 
148 
149 	/* attach sensor */
150 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
151 	    sizeof(sc->sc_sensordev.xname));
152 
153 	/* add BBU sensor */
154 	sc->sc_sensor_val.type = SENSOR_INTEGER;
155 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_val);
156 	strlcpy(sc->sc_sensor_val.desc, "BBU",
157 		sizeof(sc->sc_sensor_val.desc));
158 
159 	/* add BBU delta sensor */
160 	sc->sc_sensor_delta.type = SENSOR_INTEGER;
161 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_delta);
162 	strlcpy(sc->sc_sensor_delta.desc, "mBBU/sec",
163 		sizeof(sc->sc_sensor_delta.desc));
164 
165 	sc->sc_sensortask = sensor_task_register(sc, utwitch_refresh, UPDATE_TICK);
166 	if (sc->sc_sensortask == NULL) {
167 		printf(", unable to register update task\n");
168 		return;
169 	}
170 	sensordev_install(&sc->sc_sensordev);
171 
172 	DPRINTF(("utwitch_attach: complete\n"));
173 
174 	/* init device */ /* XXX */
175 	utwitch_set_mode(sc, 0);
176 }
177 
178 int
179 utwitch_detach(struct device *self, int flags)
180 {
181 	struct utwitch_softc *sc = (struct utwitch_softc *)self;
182 	int rv = 0;
183 
184 	wakeup(&sc->sc_sensortask);
185 	sensordev_deinstall(&sc->sc_sensordev);
186 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_val);
187 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_delta);
188 	if (sc->sc_sensortask != NULL)
189 		sensor_task_unregister(sc->sc_sensortask);
190 
191 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
192 		uhidev_close(&sc->sc_hdev);
193 
194 	if (sc->sc_ibuf != NULL) {
195 		free(sc->sc_ibuf, M_USBDEV, 0);
196 		sc->sc_ibuf = NULL;
197 	}
198 
199 	return (rv);
200 }
201 
202 void
203 utwitch_intr(struct uhidev *addr, void *ibuf, u_int len)
204 {
205 	struct utwitch_softc *sc = (struct utwitch_softc *)addr;
206 	uint8_t buf[8];
207 	uint32_t val;
208 
209 	if (sc->sc_ibuf == NULL)
210 		return;
211 
212 	/* process requests */
213 	memcpy(buf, ibuf, 8);
214 	DPRINTF(("intr: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
215 		buf[0], buf[1], buf[2], buf[3],
216 		buf[4], buf[5], buf[6], buf[7]));
217 
218 
219 	switch (buf[0]) {
220 	case CMD_ACK:
221 		if (buf[1] == sc->issueing_cmd) {
222 			DPRINTF(("ack received for cmd 0x%.2x\n", buf[1]));
223 			sc->accepted_cmd = buf[1];
224 		} else {
225 			DPRINTF(("cmd-ack mismatch: recved 0x%.2x, expect 0x%.2x\n",
226 				buf[1], sc->issueing_cmd));
227 			/* discard previous command */
228 			sc->accepted_cmd = CMD_NONE;
229 			sc->issueing_cmd = CMD_NONE;
230 		}
231 		break;
232 	case CMD_READ:
233 	case CMD_VALUE:
234 		val = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8)  + buf[5];
235 		if (!sc->sc_initialized) {
236 			sc->sc_oldval = val;
237 			sc->sc_initialized = 1;
238 		}
239 		sc->sc_sensor_val.value = val;
240 		sc->sc_curval = val;
241 		DPRINTF(("recv value update message: %d\n", val));
242 		break;
243 	default:
244 		DPRINTF(("unknown message: 0x%.2x\n", buf[0]));
245 	}
246 
247 	return;
248 }
249 
250 void
251 utwitch_refresh(void *arg)
252 {
253 	struct utwitch_softc *sc = arg;
254 
255 	if (!sc->sc_initialized) {
256 		utwitch_read_value_request(sc);
257 	} else {
258 		/* calculate delta value */
259 		sc->sc_sensor_delta.value =
260 			(1000 * (sc->sc_curval - sc->sc_oldval)) / UPDATE_TICK;
261 		sc->sc_oldval = sc->sc_curval;
262 	}
263 }
264 
265 void
266 utwitch_set_mode(struct utwitch_softc *sc, uint8_t val)
267 {
268 	uint8_t req[8];
269 
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, sc->sc_olen) != sc->sc_olen) {
276 		printf("uhidev_set_report error:EIO\n");
277 		return;
278 	}
279 
280 	/* wait ack */
281 	tsleep(&sc->sc_sensortask, 0, "utwitch", (1000*hz+999)/1000 + 1);
282 }
283 
284 void
285 utwitch_read_value_request(struct utwitch_softc *sc)
286 {
287 	uint8_t req[8];
288 
289 	memset(req, CMD_PADDING, sizeof(req));
290 	req[0] = CMD_READ;
291 	req[1] = CMD_EOF;
292 	sc->issueing_cmd = CMD_READ;
293 	sc->accepted_cmd = CMD_NONE;
294 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
295 	    sc->sc_hdev.sc_report_id, req, sc->sc_olen) != sc->sc_olen)
296 		return;
297 
298 	/* wait till sensor data are updated, 500ms will be enough */
299 	tsleep(&sc->sc_sensortask, 0, "utwitch", (500*hz+999)/1000 + 1);
300 }
301 
302 void
303 utwitch_write_value_request(struct utwitch_softc *sc, uint32_t val)
304 {
305 	uint32_t v;
306 	uint8_t req[8];
307 
308 	req[0] = CMD_WRITE;
309 	req[1] = 0;
310 	req[6] = CMD_EOF;
311 	req[7] = CMD_PADDING;
312 	v = htobe32(val);
313 	memcpy(req + 2, &v, sizeof(uint32_t));
314 
315 	sc->issueing_cmd = CMD_WRITE;
316 	sc->accepted_cmd = CMD_NONE;
317 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
318 	    sc->sc_hdev.sc_report_id, req, sc->sc_olen) != sc->sc_olen)
319 		return;
320 
321 	/* wait till sensor data are updated, 250ms will be enough */
322 	tsleep(&sc->sc_sensortask, 0, "utwitch", (250*hz+999)/1000 + 1);
323 }
324