2 * Copyright (c) 2017 Microsoft Corp.
3 * Copyright (c) 2023 Yuri <yuri@aetern.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include <sys/param.h>
29 #include <sys/cdefs.h>
31 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/module.h>
35 #include <sys/mutex.h>
37 #include <dev/evdev/input.h>
39 #include <dev/hid/hid.h>
41 #include <dev/hyperv/include/hyperv.h>
42 #include <dev/hyperv/include/vmbus_xact.h>
43 #include <dev/hyperv/utilities/hv_utilreg.h>
44 #include <dev/hyperv/utilities/vmbus_icreg.h>
45 #include <dev/hyperv/utilities/vmbus_icvar.h>
50 #define HV_HID_VER_MAJOR 2
51 #define HV_HID_VER_MINOR 0
52 #define HV_HID_VER (HV_HID_VER_MINOR | (HV_HID_VER_MAJOR) << 16)
54 #define HV_BUFSIZ (4 * PAGE_SIZE)
55 #define HV_HID_RINGBUFF_SEND_SZ (10 * PAGE_SIZE)
56 #define HV_HID_RINGBUFF_RECV_SZ (10 * PAGE_SIZE)
62 struct vmbus_channel *hs_chan;
63 struct vmbus_xact_ctx *hs_xact_ctx;
67 struct hid_device_info hdi;
85 } __packed sh_msg_hdr;
95 } __packed sh_proto_req;
101 } __packed sh_proto_resp;
108 u_short reserved[11];
109 } __packed sh_devinfo;
111 /* Copied from linux/hid.h */
113 uint8_t bDescriptorType;
114 uint16_t wDescriptorLength;
115 } __packed sh_hcdesc;
119 uint8_t bDescriptorType;
121 uint8_t bCountryCode;
122 uint8_t bNumDescriptors;
130 } __packed sh_devinfo_resp;
135 } __packed sh_devinfo_ack;
140 } __packed sh_input_report;
148 hv_hid_msg_type type;
154 hv_hid_msg_type type;
160 sh_devinfo_resp dresp;
162 sh_input_report irep;
166 #define HV_HID_REQ_SZ (sizeof(hv_hid_pmsg) + sizeof(sh_proto_req))
167 #define HV_HID_RESP_SZ (sizeof(hv_hid_pmsg) + sizeof(sh_proto_resp))
168 #define HV_HID_ACK_SZ (sizeof(hv_hid_pmsg) + sizeof(sh_devinfo_ack))
170 /* Somewhat arbitrary, enough to get the devinfo response */
171 #define HV_HID_REQ_MAX 256
172 #define HV_HID_RESP_MAX 256
174 static const struct vmbus_ic_desc vmbus_hid_descs[] = {
176 .ic_guid = { .hv_guid = {
177 0x9e, 0xb6, 0xa8, 0xcf, 0x4a, 0x5b, 0xc0, 0x4c,
178 0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a} },
179 .ic_desc = "Hyper-V HID device"
184 /* TODO: add GUID support to devmatch(8) to export vmbus_hid_descs directly */
187 } vmbus_hid_descs_pnp[] = {{ "cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a" }};
189 static int hv_hid_attach(device_t dev);
190 static int hv_hid_detach(device_t dev);
193 hv_hid_connect_vsp(hv_hid_sc *sc)
195 struct vmbus_xact *xact;
197 const hv_hid_msg *resp;
201 xact = vmbus_xact_get(sc->hs_xact_ctx, HV_HID_REQ_SZ);
203 device_printf(sc->dev, "no xact for init");
206 req = vmbus_xact_req_data(xact);
207 req->type = HV_HID_MSG_DATA;
208 req->size = sizeof(sh_proto_req);
209 req->req.hdr.type = SH_PROTO_REQ;
210 req->req.hdr.size = sizeof(u_int);
211 req->req.ver = HV_HID_VER;
213 vmbus_xact_activate(xact);
214 ret = vmbus_chan_send(sc->hs_chan,
215 VMBUS_CHANPKT_TYPE_INBAND,
216 VMBUS_CHANPKT_FLAG_RC,
217 req, HV_HID_REQ_SZ, (uint64_t)(uintptr_t)xact);
219 device_printf(sc->dev, "failed to send proto req\n");
220 vmbus_xact_deactivate(xact);
223 resp = vmbus_chan_xact_wait(sc->hs_chan, xact, &resplen, true);
224 if (resplen != HV_HID_RESP_SZ || !resp->resp.app) {
225 device_printf(sc->dev, "proto req failed\n");
229 vmbus_xact_put(xact);
234 hv_hid_receive(hv_hid_sc *sc, struct vmbus_chanpkt_hdr *pkt)
236 const hv_hid_msg *msg;
237 sh_msg_type msg_type;
241 msg = VMBUS_CHANPKT_CONST_DATA(pkt);
242 msg_len = VMBUS_CHANPKT_DATALEN(pkt);
244 if (msg->type != HV_HID_MSG_DATA)
247 if (msg_len <= sizeof(hv_hid_pmsg)) {
248 device_printf(sc->dev, "invalid packet length\n");
251 msg_type = msg->msg.hdr.type;
253 case SH_PROTO_RESP: {
254 struct vmbus_xact_ctx *xact_ctx;
256 xact_ctx = sc->hs_xact_ctx;
257 if (xact_ctx != NULL) {
258 vmbus_xact_ctx_wakeup(xact_ctx,
259 VMBUS_CHANPKT_CONST_DATA(pkt),
260 VMBUS_CHANPKT_DATALEN(pkt));
265 struct vmbus_xact *xact;
266 struct hid_device_info *hdi;
268 const sh_devinfo *devinfo;
269 const sh_hdesc *hdesc;
272 ack.type = HV_HID_MSG_DATA;
273 ack.size = sizeof(sh_devinfo_ack);
274 ack.ack.hdr.type = SH_DEVINFO_ACK;
275 ack.ack.hdr.size = 1;
278 xact = vmbus_xact_get(sc->hs_xact_ctx, HV_HID_ACK_SZ);
281 vmbus_xact_activate(xact);
282 (void) vmbus_chan_send(sc->hs_chan, VMBUS_CHANPKT_TYPE_INBAND,
283 0, &ack, HV_HID_ACK_SZ, (uint64_t)(uintptr_t)xact);
284 vmbus_xact_deactivate(xact);
285 vmbus_xact_put(xact);
287 /* Check for resume from hibernation */
288 if (sc->rdesc != NULL)
291 /* Parse devinfo response */
292 devinfo = &msg->dresp.devinfo;
293 hdesc = &msg->dresp.hdesc;
294 if (hdesc->bLength == 0)
297 memset(hdi, 0, sizeof(*hdi));
298 hdi->rdescsize = le16toh(hdesc->hcdesc[0].wDescriptorLength);
299 if (hdi->rdescsize == 0)
301 strlcpy(hdi->name, "Hyper-V", sizeof(hdi->name));
302 hdi->idBus = BUS_VIRTUAL;
303 hdi->idVendor = le16toh(devinfo->vendor);
304 hdi->idProduct = le16toh(devinfo->product);
305 hdi->idVersion = le16toh(devinfo->version);
306 /* Save rdesc copy */
307 rdesc = malloc(hdi->rdescsize, M_DEVBUF, M_WAITOK | M_ZERO);
308 memcpy(rdesc, (const uint8_t *)hdesc + hdesc->bLength,
313 mtx_unlock(&sc->mtx);
316 case SH_INPUT_REPORT: {
318 if (sc->intr != NULL && sc->intr_on)
319 sc->intr(sc->intr_ctx,
320 __DECONST(void *, msg->irep.buffer),
322 mtx_unlock(&sc->mtx);
331 hv_hid_read_channel(struct vmbus_channel *channel, void *ctx)
342 struct vmbus_chanpkt_hdr *pkt;
345 pkt = (struct vmbus_chanpkt_hdr *)buf;
347 ret = vmbus_chan_recv_pkt(channel, pkt, &rcvd);
348 if (__predict_false(ret == ENOBUFS)) {
349 buflen = sc->buflen * 2;
350 while (buflen < rcvd)
352 buf = malloc(buflen, M_DEVBUF, M_WAITOK | M_ZERO);
353 device_printf(sc->dev, "expand recvbuf %d -> %d\n",
355 free(sc->buf, M_DEVBUF);
359 } else if (__predict_false(ret == EAGAIN)) {
360 /* No more channel packets; done! */
363 KASSERT(ret == 0, ("vmbus_chan_recv_pkt failed: %d", ret));
365 switch (pkt->cph_type) {
366 case VMBUS_CHANPKT_TYPE_COMP:
367 case VMBUS_CHANPKT_TYPE_RXBUF:
368 device_printf(sc->dev, "unhandled event: %d\n",
371 case VMBUS_CHANPKT_TYPE_INBAND:
372 hv_hid_receive(sc, pkt);
375 device_printf(sc->dev, "unknown event: %d\n",
383 hv_hid_probe(device_t dev)
386 const struct vmbus_ic_desc *d;
388 if (resource_disabled(device_get_name(dev), 0))
391 bus = device_get_parent(dev);
392 for (d = vmbus_hid_descs; d->ic_desc != NULL; ++d) {
393 if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) {
394 device_set_desc(dev, d->ic_desc);
395 return (BUS_PROBE_DEFAULT);
403 hv_hid_attach(device_t dev)
409 sc = device_get_softc(dev);
411 mtx_init(&sc->mtx, "hvhid lock", NULL, MTX_DEF);
412 sc->hs_chan = vmbus_get_channel(dev);
413 sc->hs_xact_ctx = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
414 HV_HID_REQ_MAX, HV_HID_RESP_MAX, 0);
415 if (sc->hs_xact_ctx == NULL) {
419 sc->buflen = HV_BUFSIZ;
420 sc->buf = malloc(sc->buflen, M_DEVBUF, M_WAITOK | M_ZERO);
421 vmbus_chan_set_readbatch(sc->hs_chan, false);
422 ret = vmbus_chan_open(sc->hs_chan, HV_HID_RINGBUFF_SEND_SZ,
423 HV_HID_RINGBUFF_RECV_SZ, NULL, 0, hv_hid_read_channel, sc);
426 ret = hv_hid_connect_vsp(sc);
430 /* Wait until we have devinfo (or arbitrary timeout of 3s) */
432 if (sc->rdesc == NULL)
433 ret = mtx_sleep(sc, &sc->mtx, 0, "hvhid", hz * 3);
434 mtx_unlock(&sc->mtx);
439 child = device_add_child(sc->dev, "hidbus", -1);
441 device_printf(sc->dev, "failed to add hidbus\n");
445 device_set_ivars(child, &sc->hdi);
446 ret = bus_generic_attach(dev);
448 device_printf(sc->dev, "failed to attach hidbus\n");
456 hv_hid_detach(device_t dev)
461 sc = device_get_softc(dev);
462 ret = device_delete_children(dev);
465 if (sc->hs_xact_ctx != NULL)
466 vmbus_xact_ctx_destroy(sc->hs_xact_ctx);
467 vmbus_chan_close(vmbus_get_channel(dev));
468 free(sc->buf, M_DEVBUF);
469 free(sc->rdesc, M_DEVBUF);
470 mtx_destroy(&sc->mtx);
476 hv_hid_intr_setup(device_t dev, hid_intr_t intr, void *ctx,
477 struct hid_rdesc_info *rdesc)
484 sc = device_get_softc(dev);
488 rdesc->rdsize = rdesc->isize;
492 hv_hid_intr_unsetup(device_t dev)
496 sc = device_get_softc(dev);
503 hv_hid_intr_start(device_t dev)
507 sc = device_get_softc(dev);
510 mtx_unlock(&sc->mtx);
515 hv_hid_intr_stop(device_t dev)
519 sc = device_get_softc(dev);
522 mtx_unlock(&sc->mtx);
527 hv_hid_get_rdesc(device_t dev, void *buf, hid_size_t len)
531 sc = device_get_softc(dev);
532 if (len < sc->hdi.rdescsize)
534 memcpy(buf, sc->rdesc, len);
538 static device_method_t hv_hid_methods[] = {
539 DEVMETHOD(device_probe, hv_hid_probe),
540 DEVMETHOD(device_attach, hv_hid_attach),
541 DEVMETHOD(device_detach, hv_hid_detach),
543 DEVMETHOD(hid_intr_setup, hv_hid_intr_setup),
544 DEVMETHOD(hid_intr_unsetup, hv_hid_intr_unsetup),
545 DEVMETHOD(hid_intr_start, hv_hid_intr_start),
546 DEVMETHOD(hid_intr_stop, hv_hid_intr_stop),
548 DEVMETHOD(hid_get_rdesc, hv_hid_get_rdesc),
552 static driver_t hv_hid_driver = {
554 .methods = hv_hid_methods,
555 .size = sizeof(hv_hid_sc),
558 DRIVER_MODULE(hv_hid, vmbus, hv_hid_driver, NULL, NULL);
559 MODULE_VERSION(hv_hid, 1);
560 MODULE_DEPEND(hv_hid, hidbus, 1, 1, 1);
561 MODULE_DEPEND(hv_hid, hms, 1, 1, 1);
562 MODULE_DEPEND(hv_hid, vmbus, 1, 1, 1);
563 MODULE_PNP_INFO("Z:classid", vmbus, hv_hid, vmbus_hid_descs_pnp,
564 nitems(vmbus_hid_descs_pnp));