mirror of
https://git.FreeBSD.org/src.git
synced 2024-10-19 02:29:40 +00:00
hv_hid: Hyper-V HID driver
Hyper-V HID driver using hidbus/hms.
Reviewed by: wulf
MFC after: 1 week
PR: 221074
Differential revision: https://reviews.freebsd.org/D38140
(cherry picked from commit e4d3f1e40a
)
This commit is contained in:
parent
374f3920c7
commit
21f4e817fd
@ -508,6 +508,7 @@ device kvm_clock # KVM paravirtual clock driver
|
|||||||
|
|
||||||
# Microsoft Hyper-V enhancement support
|
# Microsoft Hyper-V enhancement support
|
||||||
device hyperv # HyperV drivers
|
device hyperv # HyperV drivers
|
||||||
|
device hvhid # HyperV HID device
|
||||||
|
|
||||||
# Xen HVM Guest Optimizations
|
# Xen HVM Guest Optimizations
|
||||||
options XENHVM # Xen HVM kernel infrastructure
|
options XENHVM # Xen HVM kernel infrastructure
|
||||||
|
@ -134,6 +134,7 @@ dev/hwpmc/hwpmc_uncore.c optional hwpmc
|
|||||||
dev/hwpmc/hwpmc_tsc.c optional hwpmc
|
dev/hwpmc/hwpmc_tsc.c optional hwpmc
|
||||||
dev/hwpmc/hwpmc_x86.c optional hwpmc
|
dev/hwpmc/hwpmc_x86.c optional hwpmc
|
||||||
dev/hyperv/hvsock/hv_sock.c optional hyperv
|
dev/hyperv/hvsock/hv_sock.c optional hyperv
|
||||||
|
dev/hyperv/input/hv_hid.c optional hyperv hvhid
|
||||||
dev/hyperv/input/hv_kbd.c optional hyperv
|
dev/hyperv/input/hv_kbd.c optional hyperv
|
||||||
dev/hyperv/input/hv_kbdc.c optional hyperv
|
dev/hyperv/input/hv_kbdc.c optional hyperv
|
||||||
dev/hyperv/pcib/vmbus_pcib.c optional hyperv pci
|
dev/hyperv/pcib/vmbus_pcib.c optional hyperv pci
|
||||||
|
@ -926,5 +926,6 @@ driver_t hidbus_driver = {
|
|||||||
|
|
||||||
MODULE_DEPEND(hidbus, hid, 1, 1, 1);
|
MODULE_DEPEND(hidbus, hid, 1, 1, 1);
|
||||||
MODULE_VERSION(hidbus, 1);
|
MODULE_VERSION(hidbus, 1);
|
||||||
|
DRIVER_MODULE(hidbus, hvhid, hidbus_driver, hidbus_devclass, 0, 0);
|
||||||
DRIVER_MODULE(hidbus, iichid, hidbus_driver, hidbus_devclass, 0, 0);
|
DRIVER_MODULE(hidbus, iichid, hidbus_driver, hidbus_devclass, 0, 0);
|
||||||
DRIVER_MODULE(hidbus, usbhid, hidbus_driver, hidbus_devclass, 0, 0);
|
DRIVER_MODULE(hidbus, usbhid, hidbus_driver, hidbus_devclass, 0, 0);
|
||||||
|
564
sys/dev/hyperv/input/hv_hid.c
Normal file
564
sys/dev/hyperv/input/hv_hid.c
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 2017 Microsoft Corp.
|
||||||
|
* Copyright (c) 2023 Yuri <yuri@aetern.org>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice unmodified, this list of conditions, and the following
|
||||||
|
* disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include <sys/conf.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/lock.h>
|
||||||
|
#include <sys/malloc.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/mutex.h>
|
||||||
|
|
||||||
|
#include <dev/evdev/input.h>
|
||||||
|
|
||||||
|
#include <dev/hid/hid.h>
|
||||||
|
|
||||||
|
#include <dev/hyperv/include/hyperv.h>
|
||||||
|
#include <dev/hyperv/include/vmbus_xact.h>
|
||||||
|
#include <dev/hyperv/utilities/hv_utilreg.h>
|
||||||
|
#include <dev/hyperv/utilities/vmbus_icreg.h>
|
||||||
|
#include <dev/hyperv/utilities/vmbus_icvar.h>
|
||||||
|
|
||||||
|
#include "hid_if.h"
|
||||||
|
#include "vmbus_if.h"
|
||||||
|
|
||||||
|
#define HV_HID_VER_MAJOR 2
|
||||||
|
#define HV_HID_VER_MINOR 0
|
||||||
|
#define HV_HID_VER (HV_HID_VER_MINOR | (HV_HID_VER_MAJOR) << 16)
|
||||||
|
|
||||||
|
#define HV_BUFSIZ (4 * PAGE_SIZE)
|
||||||
|
#define HV_HID_RINGBUFF_SEND_SZ (10 * PAGE_SIZE)
|
||||||
|
#define HV_HID_RINGBUFF_RECV_SZ (10 * PAGE_SIZE)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
device_t dev;
|
||||||
|
struct mtx mtx;
|
||||||
|
/* vmbus */
|
||||||
|
struct vmbus_channel *hs_chan;
|
||||||
|
struct vmbus_xact_ctx *hs_xact_ctx;
|
||||||
|
uint8_t *buf;
|
||||||
|
int buflen;
|
||||||
|
/* hid */
|
||||||
|
struct hid_device_info hdi;
|
||||||
|
hid_intr_t *intr;
|
||||||
|
bool intr_on;
|
||||||
|
void *intr_ctx;
|
||||||
|
uint8_t *rdesc;
|
||||||
|
} hv_hid_sc;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SH_PROTO_REQ,
|
||||||
|
SH_PROTO_RESP,
|
||||||
|
SH_DEVINFO,
|
||||||
|
SH_DEVINFO_ACK,
|
||||||
|
SH_INPUT_REPORT,
|
||||||
|
} sh_msg_type;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sh_msg_type type;
|
||||||
|
uint32_t size;
|
||||||
|
} __packed sh_msg_hdr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sh_msg_hdr hdr;
|
||||||
|
char data[];
|
||||||
|
} __packed sh_msg;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sh_msg_hdr hdr;
|
||||||
|
uint32_t ver;
|
||||||
|
} __packed sh_proto_req;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sh_msg_hdr hdr;
|
||||||
|
uint32_t ver;
|
||||||
|
uint32_t app;
|
||||||
|
} __packed sh_proto_resp;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u_int size;
|
||||||
|
u_short vendor;
|
||||||
|
u_short product;
|
||||||
|
u_short version;
|
||||||
|
u_short reserved[11];
|
||||||
|
} __packed sh_devinfo;
|
||||||
|
|
||||||
|
/* Copied from linux/hid.h */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint16_t wDescriptorLength;
|
||||||
|
} __packed sh_hcdesc;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint16_t bcdHID;
|
||||||
|
uint8_t bCountryCode;
|
||||||
|
uint8_t bNumDescriptors;
|
||||||
|
sh_hcdesc hcdesc[1];
|
||||||
|
} __packed sh_hdesc;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sh_msg_hdr hdr;
|
||||||
|
sh_devinfo devinfo;
|
||||||
|
sh_hdesc hdesc;
|
||||||
|
} __packed sh_devinfo_resp;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sh_msg_hdr hdr;
|
||||||
|
uint8_t rsvd;
|
||||||
|
} __packed sh_devinfo_ack;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sh_msg_hdr hdr;
|
||||||
|
char buffer[];
|
||||||
|
} __packed sh_input_report;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HV_HID_MSG_INVALID,
|
||||||
|
HV_HID_MSG_DATA,
|
||||||
|
} hv_hid_msg_type;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
hv_hid_msg_type type;
|
||||||
|
uint32_t size;
|
||||||
|
char data[];
|
||||||
|
} hv_hid_pmsg;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
hv_hid_msg_type type;
|
||||||
|
uint32_t size;
|
||||||
|
union {
|
||||||
|
sh_msg msg;
|
||||||
|
sh_proto_req req;
|
||||||
|
sh_proto_resp resp;
|
||||||
|
sh_devinfo_resp dresp;
|
||||||
|
sh_devinfo_ack ack;
|
||||||
|
sh_input_report irep;
|
||||||
|
};
|
||||||
|
} hv_hid_msg;
|
||||||
|
|
||||||
|
#define HV_HID_REQ_SZ (sizeof(hv_hid_pmsg) + sizeof(sh_proto_req))
|
||||||
|
#define HV_HID_RESP_SZ (sizeof(hv_hid_pmsg) + sizeof(sh_proto_resp))
|
||||||
|
#define HV_HID_ACK_SZ (sizeof(hv_hid_pmsg) + sizeof(sh_devinfo_ack))
|
||||||
|
|
||||||
|
/* Somewhat arbitrary, enough to get the devinfo response */
|
||||||
|
#define HV_HID_REQ_MAX 256
|
||||||
|
#define HV_HID_RESP_MAX 256
|
||||||
|
|
||||||
|
static const struct vmbus_ic_desc vmbus_hid_descs[] = {
|
||||||
|
{
|
||||||
|
.ic_guid = { .hv_guid = {
|
||||||
|
0x9e, 0xb6, 0xa8, 0xcf, 0x4a, 0x5b, 0xc0, 0x4c,
|
||||||
|
0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a} },
|
||||||
|
.ic_desc = "Hyper-V HID device"
|
||||||
|
},
|
||||||
|
VMBUS_IC_DESC_END
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO: add GUID support to devmatch(8) to export vmbus_hid_descs directly */
|
||||||
|
const struct {
|
||||||
|
char *guid;
|
||||||
|
} vmbus_hid_descs_pnp[] = {{ "cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a" }};
|
||||||
|
|
||||||
|
static int hv_hid_attach(device_t dev);
|
||||||
|
static int hv_hid_detach(device_t dev);
|
||||||
|
|
||||||
|
static int
|
||||||
|
hv_hid_connect_vsp(hv_hid_sc *sc)
|
||||||
|
{
|
||||||
|
struct vmbus_xact *xact;
|
||||||
|
hv_hid_msg *req;
|
||||||
|
const hv_hid_msg *resp;
|
||||||
|
size_t resplen;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
xact = vmbus_xact_get(sc->hs_xact_ctx, HV_HID_REQ_SZ);
|
||||||
|
if (xact == NULL) {
|
||||||
|
device_printf(sc->dev, "no xact for init");
|
||||||
|
return (ENODEV);
|
||||||
|
}
|
||||||
|
req = vmbus_xact_req_data(xact);
|
||||||
|
req->type = HV_HID_MSG_DATA;
|
||||||
|
req->size = sizeof(sh_proto_req);
|
||||||
|
req->req.hdr.type = SH_PROTO_REQ;
|
||||||
|
req->req.hdr.size = sizeof(u_int);
|
||||||
|
req->req.ver = HV_HID_VER;
|
||||||
|
|
||||||
|
vmbus_xact_activate(xact);
|
||||||
|
ret = vmbus_chan_send(sc->hs_chan,
|
||||||
|
VMBUS_CHANPKT_TYPE_INBAND,
|
||||||
|
VMBUS_CHANPKT_FLAG_RC,
|
||||||
|
req, HV_HID_REQ_SZ, (uint64_t)(uintptr_t)xact);
|
||||||
|
if (ret != 0) {
|
||||||
|
device_printf(sc->dev, "failed to send proto req\n");
|
||||||
|
vmbus_xact_deactivate(xact);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
resp = vmbus_chan_xact_wait(sc->hs_chan, xact, &resplen, true);
|
||||||
|
if (resplen != HV_HID_RESP_SZ || !resp->resp.app) {
|
||||||
|
device_printf(sc->dev, "proto req failed\n");
|
||||||
|
ret = ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
vmbus_xact_put(xact);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hv_hid_receive(hv_hid_sc *sc, struct vmbus_chanpkt_hdr *pkt)
|
||||||
|
{
|
||||||
|
const hv_hid_msg *msg;
|
||||||
|
sh_msg_type msg_type;
|
||||||
|
uint32_t msg_len;
|
||||||
|
void *rdesc;
|
||||||
|
|
||||||
|
msg = VMBUS_CHANPKT_CONST_DATA(pkt);
|
||||||
|
msg_len = VMBUS_CHANPKT_DATALEN(pkt);
|
||||||
|
|
||||||
|
if (msg->type != HV_HID_MSG_DATA)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (msg_len <= sizeof(hv_hid_pmsg)) {
|
||||||
|
device_printf(sc->dev, "invalid packet length\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg_type = msg->msg.hdr.type;
|
||||||
|
switch (msg_type) {
|
||||||
|
case SH_PROTO_RESP: {
|
||||||
|
struct vmbus_xact_ctx *xact_ctx;
|
||||||
|
|
||||||
|
xact_ctx = sc->hs_xact_ctx;
|
||||||
|
if (xact_ctx != NULL) {
|
||||||
|
vmbus_xact_ctx_wakeup(xact_ctx,
|
||||||
|
VMBUS_CHANPKT_CONST_DATA(pkt),
|
||||||
|
VMBUS_CHANPKT_DATALEN(pkt));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SH_DEVINFO: {
|
||||||
|
struct vmbus_xact *xact;
|
||||||
|
struct hid_device_info *hdi;
|
||||||
|
hv_hid_msg ack;
|
||||||
|
const sh_devinfo *devinfo;
|
||||||
|
const sh_hdesc *hdesc;
|
||||||
|
|
||||||
|
/* Send ack */
|
||||||
|
ack.type = HV_HID_MSG_DATA;
|
||||||
|
ack.size = sizeof(sh_devinfo_ack);
|
||||||
|
ack.ack.hdr.type = SH_DEVINFO_ACK;
|
||||||
|
ack.ack.hdr.size = 1;
|
||||||
|
ack.ack.rsvd = 0;
|
||||||
|
|
||||||
|
xact = vmbus_xact_get(sc->hs_xact_ctx, HV_HID_ACK_SZ);
|
||||||
|
if (xact == NULL)
|
||||||
|
break;
|
||||||
|
vmbus_xact_activate(xact);
|
||||||
|
(void) vmbus_chan_send(sc->hs_chan, VMBUS_CHANPKT_TYPE_INBAND,
|
||||||
|
0, &ack, HV_HID_ACK_SZ, (uint64_t)(uintptr_t)xact);
|
||||||
|
vmbus_xact_deactivate(xact);
|
||||||
|
vmbus_xact_put(xact);
|
||||||
|
|
||||||
|
/* Check for resume from hibernation */
|
||||||
|
if (sc->rdesc != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Parse devinfo response */
|
||||||
|
devinfo = &msg->dresp.devinfo;
|
||||||
|
hdesc = &msg->dresp.hdesc;
|
||||||
|
if (hdesc->bLength == 0)
|
||||||
|
break;
|
||||||
|
hdi = &sc->hdi;
|
||||||
|
memset(hdi, 0, sizeof(*hdi));
|
||||||
|
hdi->rdescsize = le16toh(hdesc->hcdesc[0].wDescriptorLength);
|
||||||
|
if (hdi->rdescsize == 0)
|
||||||
|
break;
|
||||||
|
strlcpy(hdi->name, "Hyper-V", sizeof(hdi->name));
|
||||||
|
hdi->idBus = BUS_VIRTUAL;
|
||||||
|
hdi->idVendor = le16toh(devinfo->vendor);
|
||||||
|
hdi->idProduct = le16toh(devinfo->product);
|
||||||
|
hdi->idVersion = le16toh(devinfo->version);
|
||||||
|
/* Save rdesc copy */
|
||||||
|
rdesc = malloc(hdi->rdescsize, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||||
|
memcpy(rdesc, (const uint8_t *)hdesc + hdesc->bLength,
|
||||||
|
hdi->rdescsize);
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
sc->rdesc = rdesc;
|
||||||
|
wakeup(sc);
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SH_INPUT_REPORT: {
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
if (sc->intr != NULL && sc->intr_on)
|
||||||
|
sc->intr(sc->intr_ctx,
|
||||||
|
__DECONST(void *, msg->irep.buffer),
|
||||||
|
msg->irep.hdr.size);
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hv_hid_read_channel(struct vmbus_channel *channel, void *ctx)
|
||||||
|
{
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
uint8_t *buf;
|
||||||
|
int buflen;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sc = ctx;
|
||||||
|
buf = sc->buf;
|
||||||
|
buflen = sc->buflen;
|
||||||
|
for (;;) {
|
||||||
|
struct vmbus_chanpkt_hdr *pkt;
|
||||||
|
int rcvd;
|
||||||
|
|
||||||
|
pkt = (struct vmbus_chanpkt_hdr *)buf;
|
||||||
|
rcvd = buflen;
|
||||||
|
ret = vmbus_chan_recv_pkt(channel, pkt, &rcvd);
|
||||||
|
if (__predict_false(ret == ENOBUFS)) {
|
||||||
|
buflen = sc->buflen * 2;
|
||||||
|
while (buflen < rcvd)
|
||||||
|
buflen *= 2;
|
||||||
|
buf = malloc(buflen, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||||
|
device_printf(sc->dev, "expand recvbuf %d -> %d\n",
|
||||||
|
sc->buflen, buflen);
|
||||||
|
free(sc->buf, M_DEVBUF);
|
||||||
|
sc->buf = buf;
|
||||||
|
sc->buflen = buflen;
|
||||||
|
continue;
|
||||||
|
} else if (__predict_false(ret == EAGAIN)) {
|
||||||
|
/* No more channel packets; done! */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
KASSERT(ret == 0, ("vmbus_chan_recv_pkt failed: %d", ret));
|
||||||
|
|
||||||
|
switch (pkt->cph_type) {
|
||||||
|
case VMBUS_CHANPKT_TYPE_COMP:
|
||||||
|
case VMBUS_CHANPKT_TYPE_RXBUF:
|
||||||
|
device_printf(sc->dev, "unhandled event: %d\n",
|
||||||
|
pkt->cph_type);
|
||||||
|
break;
|
||||||
|
case VMBUS_CHANPKT_TYPE_INBAND:
|
||||||
|
hv_hid_receive(sc, pkt);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
device_printf(sc->dev, "unknown event: %d\n",
|
||||||
|
pkt->cph_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hv_hid_probe(device_t dev)
|
||||||
|
{
|
||||||
|
device_t bus;
|
||||||
|
const struct vmbus_ic_desc *d;
|
||||||
|
|
||||||
|
if (resource_disabled(device_get_name(dev), 0))
|
||||||
|
return (ENXIO);
|
||||||
|
|
||||||
|
bus = device_get_parent(dev);
|
||||||
|
for (d = vmbus_hid_descs; d->ic_desc != NULL; ++d) {
|
||||||
|
if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) {
|
||||||
|
device_set_desc(dev, d->ic_desc);
|
||||||
|
return (BUS_PROBE_DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ENXIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hv_hid_attach(device_t dev)
|
||||||
|
{
|
||||||
|
device_t child;
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sc = device_get_softc(dev);
|
||||||
|
sc->dev = dev;
|
||||||
|
mtx_init(&sc->mtx, "hvhid lock", NULL, MTX_DEF);
|
||||||
|
sc->hs_chan = vmbus_get_channel(dev);
|
||||||
|
sc->hs_xact_ctx = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
|
||||||
|
HV_HID_REQ_MAX, HV_HID_RESP_MAX, 0);
|
||||||
|
if (sc->hs_xact_ctx == NULL) {
|
||||||
|
ret = ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
sc->buflen = HV_BUFSIZ;
|
||||||
|
sc->buf = malloc(sc->buflen, M_DEVBUF, M_WAITOK | M_ZERO);
|
||||||
|
vmbus_chan_set_readbatch(sc->hs_chan, false);
|
||||||
|
ret = vmbus_chan_open(sc->hs_chan, HV_HID_RINGBUFF_SEND_SZ,
|
||||||
|
HV_HID_RINGBUFF_RECV_SZ, NULL, 0, hv_hid_read_channel, sc);
|
||||||
|
if (ret != 0)
|
||||||
|
goto out;
|
||||||
|
ret = hv_hid_connect_vsp(sc);
|
||||||
|
if (ret != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* Wait until we have devinfo (or arbitrary timeout of 3s) */
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
if (sc->rdesc == NULL)
|
||||||
|
ret = mtx_sleep(sc, &sc->mtx, 0, "hvhid", hz * 3);
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
if (ret != 0) {
|
||||||
|
ret = ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
child = device_add_child(sc->dev, "hidbus", -1);
|
||||||
|
if (child == NULL) {
|
||||||
|
device_printf(sc->dev, "failed to add hidbus\n");
|
||||||
|
ret = ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
device_set_ivars(child, &sc->hdi);
|
||||||
|
ret = bus_generic_attach(dev);
|
||||||
|
if (ret != 0)
|
||||||
|
device_printf(sc->dev, "failed to attach hidbus\n");
|
||||||
|
out:
|
||||||
|
if (ret != 0)
|
||||||
|
hv_hid_detach(dev);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hv_hid_detach(device_t dev)
|
||||||
|
{
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sc = device_get_softc(dev);
|
||||||
|
ret = device_delete_children(dev);
|
||||||
|
if (ret != 0)
|
||||||
|
return (ret);
|
||||||
|
if (sc->hs_xact_ctx != NULL)
|
||||||
|
vmbus_xact_ctx_destroy(sc->hs_xact_ctx);
|
||||||
|
vmbus_chan_close(vmbus_get_channel(dev));
|
||||||
|
free(sc->buf, M_DEVBUF);
|
||||||
|
free(sc->rdesc, M_DEVBUF);
|
||||||
|
mtx_destroy(&sc->mtx);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hv_hid_intr_setup(device_t dev, hid_intr_t intr, void *ctx,
|
||||||
|
struct hid_rdesc_info *rdesc)
|
||||||
|
{
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
|
||||||
|
if (intr == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sc = device_get_softc(dev);
|
||||||
|
sc->intr = intr;
|
||||||
|
sc->intr_on = false;
|
||||||
|
sc->intr_ctx = ctx;
|
||||||
|
rdesc->rdsize = rdesc->isize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hv_hid_intr_unsetup(device_t dev)
|
||||||
|
{
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
|
||||||
|
sc = device_get_softc(dev);
|
||||||
|
sc->intr = NULL;
|
||||||
|
sc->intr_on = false;
|
||||||
|
sc->intr_ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hv_hid_intr_start(device_t dev)
|
||||||
|
{
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
|
||||||
|
sc = device_get_softc(dev);
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
sc->intr_on = true;
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hv_hid_intr_stop(device_t dev)
|
||||||
|
{
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
|
||||||
|
sc = device_get_softc(dev);
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
sc->intr_on = false;
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hv_hid_get_rdesc(device_t dev, void *buf, hid_size_t len)
|
||||||
|
{
|
||||||
|
hv_hid_sc *sc;
|
||||||
|
|
||||||
|
sc = device_get_softc(dev);
|
||||||
|
if (len < sc->hdi.rdescsize)
|
||||||
|
return (EMSGSIZE);
|
||||||
|
memcpy(buf, sc->rdesc, len);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static device_method_t hv_hid_methods[] = {
|
||||||
|
DEVMETHOD(device_probe, hv_hid_probe),
|
||||||
|
DEVMETHOD(device_attach, hv_hid_attach),
|
||||||
|
DEVMETHOD(device_detach, hv_hid_detach),
|
||||||
|
|
||||||
|
DEVMETHOD(hid_intr_setup, hv_hid_intr_setup),
|
||||||
|
DEVMETHOD(hid_intr_unsetup, hv_hid_intr_unsetup),
|
||||||
|
DEVMETHOD(hid_intr_start, hv_hid_intr_start),
|
||||||
|
DEVMETHOD(hid_intr_stop, hv_hid_intr_stop),
|
||||||
|
|
||||||
|
DEVMETHOD(hid_get_rdesc, hv_hid_get_rdesc),
|
||||||
|
DEVMETHOD_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
static driver_t hv_hid_driver = {
|
||||||
|
.name = "hvhid",
|
||||||
|
.methods = hv_hid_methods,
|
||||||
|
.size = sizeof(hv_hid_sc),
|
||||||
|
};
|
||||||
|
|
||||||
|
DRIVER_MODULE(hv_hid, vmbus, hv_hid_driver, NULL, NULL);
|
||||||
|
MODULE_VERSION(hv_hid, 1);
|
||||||
|
MODULE_DEPEND(hv_hid, hidbus, 1, 1, 1);
|
||||||
|
MODULE_DEPEND(hv_hid, hms, 1, 1, 1);
|
||||||
|
MODULE_DEPEND(hv_hid, vmbus, 1, 1, 1);
|
||||||
|
MODULE_PNP_INFO("Z:classid", vmbus, hv_hid, vmbus_hid_descs_pnp,
|
||||||
|
nitems(vmbus_hid_descs_pnp));
|
@ -725,6 +725,7 @@ device kvm_clock # KVM paravirtual clock driver
|
|||||||
|
|
||||||
options HYPERV
|
options HYPERV
|
||||||
device hyperv # HyperV drivers
|
device hyperv # HyperV drivers
|
||||||
|
device hvhid # HyperV HID device
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# $FreeBSD$
|
# $FreeBSD$
|
||||||
|
|
||||||
SUBDIR = vmbus netvsc storvsc utilities hvsock
|
SUBDIR = vmbus netvsc storvsc utilities hvsock hid
|
||||||
|
|
||||||
.include <bsd.subdir.mk>
|
.include <bsd.subdir.mk>
|
||||||
|
12
sys/modules/hyperv/hid/Makefile
Normal file
12
sys/modules/hyperv/hid/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# $FreeBSD$
|
||||||
|
|
||||||
|
.PATH: ${SRCTOP}/sys/dev/hyperv/input
|
||||||
|
|
||||||
|
KMOD= hv_hid
|
||||||
|
SRCS= hv_hid.c
|
||||||
|
SRCS+= bus_if.h device_if.h hid_if.h vmbus_if.h
|
||||||
|
|
||||||
|
CFLAGS+= -I${SRCTOP}/sys/dev/hyperv/include \
|
||||||
|
-I${SRCTOP}/sys/dev/hyperv/vmbus
|
||||||
|
|
||||||
|
.include <bsd.kmod.mk>
|
Loading…
Reference in New Issue
Block a user