Page MenuHomeFreeBSD

D39395.id119789.diff
No OneTemporary

D39395.id119789.diff

diff --git a/sys/dev/hyperv/hvfb/hv_fb.c b/sys/dev/hyperv/hvfb/hv_fb.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/hyperv/hvfb/hv_fb.c
@@ -0,0 +1,684 @@
+/*-
+ * Copyright (c) 2017 Microsoft Corp.
+ *
+ * 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/fbio.h>
+#include <sys/kernel.h>
+#include <sys/linker.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <machine/metadata.h>
+#include <vm/vm.h>
+#include <vm/pmap.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 <dev/vt/vt.h>
+#include <dev/vt/hw/fb/vt_fb.h>
+
+#include "vmbus_if.h"
+
+/* Protocol details taken from linux hyperv_fb driver */
+
+#define SV_VER(major, minor) ((minor) << 16 | (major))
+#define SV_VER_MAJOR(ver) (ver & 0x0000ffff)
+#define SV_VER_MINOR(ver) ((ver & 0xffff0000) >> 16)
+
+#define SV_VER_WIN7 SV_VER(3, 0)
+#define SV_VER_WIN8 SV_VER(3, 2)
+#define SV_VER_WIN10 SV_VER(3, 5)
+
+#define HV_BUFSIZ (4 * PAGE_SIZE)
+#define HV_RINGBUFF_SZ (10 * PAGE_SIZE)
+
+typedef struct {
+ device_t dev;
+ struct mtx mtx;
+ bool ready;
+ /* vmbus */
+ struct vmbus_channel *hs_chan;
+ struct vmbus_xact_ctx *hs_xact_ctx;
+ uint8_t *buf;
+ int buflen;
+ /* fb */
+ struct fb_info fb;
+} hv_fb_sc;
+
+typedef enum {
+ SV_VER_REQ = 1,
+ SV_VER_RESP = 2,
+ SV_VRAM = 3,
+ SV_VRAM_ACK = 4,
+ SV_SITU = 5,
+ SV_SITU_ACK = 6,
+ SV_PTR_POS = 7,
+ SV_PTR_SHAPE = 8,
+ SV_FEAT_CHG = 9,
+ SV_DIRT = 10,
+ SV_RES_REQ = 13,
+ SV_RES_RESP = 14,
+} sv_msg_type;
+
+typedef struct {
+ sv_msg_type type;
+ uint32_t size; /* includes header size */
+} __packed sv_msg_hdr;
+
+typedef struct {
+ uint32_t ver;
+} __packed sv_ver_req;
+
+typedef struct {
+ uint32_t ver;
+ uint8_t is_acc;
+ uint8_t max_vo;
+} __packed sv_ver_resp;
+
+typedef struct {
+ uint8_t max_res_cnt;
+} __packed sv_res_req;
+
+typedef struct {
+ uint16_t width;
+ uint16_t height;
+} __packed sv_scr_info;
+
+#define SV_EDID_SZ 128
+#define SV_MAX_RES_CNT 64
+
+typedef struct {
+ uint8_t edid[SV_EDID_SZ];
+ uint8_t res_cnt;
+ uint8_t def_res_idx;
+ uint8_t is_std;
+ sv_scr_info res[SV_MAX_RES_CNT];
+} __packed sv_res_resp;
+
+typedef struct {
+ uint64_t ctx;
+ uint8_t is_gpa;
+ uint64_t gpa;
+} __packed sv_vram;
+
+typedef struct {
+ uint64_t ctx;
+} __packed sv_vram_ack;
+
+typedef struct {
+ uint8_t active;
+ uint32_t vram_off;
+ uint8_t depth;
+ uint32_t width;
+ uint32_t height;
+ uint32_t pitch;
+} __packed vo_situ;
+
+typedef struct {
+ uint64_t ctx;
+ uint8_t vo_cnt;
+ vo_situ vo[1];
+} __packed sv_situ;
+
+typedef struct {
+ uint64_t ctx;
+} __packed sv_situ_ack;
+
+typedef struct {
+ uint8_t is_visible;
+ uint8_t vo;
+ int32_t img_x;
+ int32_t img_y;
+} __packed sv_ptr_pos;
+
+typedef struct {
+ uint8_t part_idx;
+ uint8_t is_argb;
+ uint32_t width;
+ uint32_t height;
+ uint32_t hs_x;
+ uint32_t hs_y;
+ uint8_t data[4];
+} __packed sv_ptr_shape;
+
+typedef struct {
+ uint8_t dirt;
+ uint8_t ptr_pos;
+ uint8_t ptr_shape;
+ uint8_t situ;
+} __packed sv_feat_chg;
+
+typedef struct {
+ int32_t x1;
+ int32_t y1;
+ int32_t x2;
+ int32_t y2;
+} __packed sv_rect;
+
+typedef struct {
+ uint8_t vo;
+ uint8_t cnt;
+ sv_rect rect[1];
+} __packed sv_dirt;
+
+typedef enum {
+ HV_MSG_INVALID,
+ HV_MSG_DATA,
+} hv_msg_type;
+
+typedef struct {
+ hv_msg_type type;
+ uint32_t size; /* does not include header size */
+} hv_fb_hdr;
+
+typedef struct {
+ hv_fb_hdr hdr;
+ sv_msg_hdr sv_hdr;
+ union {
+ sv_ver_req ver_req;
+ sv_ver_resp ver_resp;
+ sv_vram vram;
+ sv_vram_ack vram_ack;
+ sv_situ situ;
+ sv_situ_ack situ_ack;
+ sv_ptr_pos ptr_pos;
+ sv_ptr_shape ptr_shape;
+ sv_feat_chg feat_chg;
+ sv_dirt dirt;
+ sv_res_req res_req;
+ sv_res_resp res_resp;
+ };
+} __packed hv_fb_msg;
+
+static inline int
+hv_fb_send(hv_fb_sc *sc, hv_fb_msg *req, const hv_fb_msg **resp)
+{
+ struct vmbus_xact *xact;
+ hv_fb_msg *xreq;
+ const hv_fb_msg *xresp;
+ size_t xreqlen;
+ size_t xresplen;
+ int ret;
+
+ xreqlen = sizeof(hv_fb_hdr) + req->sv_hdr.size;
+ xact = vmbus_xact_get(sc->hs_xact_ctx, xreqlen);
+ if (xact == NULL) {
+ device_printf(sc->dev, "failed to get xact");
+ return (ENOMEM);
+ }
+ xreq = vmbus_xact_req_data(xact);
+ memcpy(xreq, req, xreqlen);
+ xreq->hdr.type = HV_MSG_DATA;
+ xreq->hdr.size = req->sv_hdr.size;
+
+ vmbus_xact_activate(xact);
+ ret = vmbus_chan_send(sc->hs_chan, VMBUS_CHANPKT_TYPE_INBAND,
+ VMBUS_CHANPKT_FLAG_RC, xreq, xreqlen, (uint64_t)(uintptr_t)xact);
+ if (ret != 0 || resp == NULL) {
+ vmbus_xact_deactivate(xact);
+ vmbus_xact_put(xact);
+ return (ret);
+ }
+ xresp = vmbus_chan_xact_wait(sc->hs_chan, xact, &xresplen, true);
+ *resp = xresp;
+ vmbus_xact_put(xact);
+ return (0);
+}
+
+static int
+hv_fb_neg_ver(hv_fb_sc *sc, uint32_t ver)
+{
+ hv_fb_msg req = { 0 };
+ const hv_fb_msg *resp;
+
+ req.sv_hdr.type = SV_VER_REQ;
+ req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_ver_req);
+ req.ver_req.ver = ver;
+
+ if (hv_fb_send(sc, &req, &resp) != 0 ||
+ resp->ver_resp.is_acc == 0)
+ return (ENODEV);
+
+ device_printf(sc->dev, "version %d.%d\n",
+ SV_VER_MAJOR(ver), SV_VER_MINOR(ver));
+ return (0);
+}
+
+static int
+hv_fb_get_res(hv_fb_sc *sc)
+{
+ hv_fb_msg req = { 0 };
+ const hv_fb_msg *resp;
+ uint8_t idx;
+ uint8_t i;
+
+ req.sv_hdr.type = SV_RES_REQ;
+ req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_res_req);
+ req.res_req.max_res_cnt = SV_MAX_RES_CNT;
+
+ if (hv_fb_send(sc, &req, &resp) != 0)
+ return (ENODEV);
+ if (resp->res_resp.res_cnt == 0) {
+ device_printf(sc->dev, "no supported resolutions");
+ return (ENODEV);
+ }
+ idx = resp->res_resp.def_res_idx;
+ if (idx >= resp->res_resp.res_cnt) {
+ device_printf(sc->dev, "invalid resolution index: %d\n", idx);
+ return (ENODEV);
+ }
+ device_printf(sc->dev, "supported resolutions:\n");
+ for (i = 0; i < resp->res_resp.res_cnt; i++) {
+ device_printf(sc->dev, " (%s%2d) %dx%d\n",
+ i == idx ? "*" : " ", i,
+ resp->res_resp.res[i].width,
+ resp->res_resp.res[i].height);
+ }
+
+ sc->fb.fb_width = resp->res_resp.res[idx].width;
+ sc->fb.fb_height = resp->res_resp.res[idx].height;
+
+ return (0);
+}
+
+static int
+hv_fb_connect_vsp(hv_fb_sc *sc)
+{
+ int ret;
+
+ /*
+ * Negotiate version.
+ * Everything prior to WIN10 is already unsupported by Microsoft, more
+ * so I have no means of testing on previous versions.
+ */
+ switch (vmbus_current_version) {
+ default:
+ case VMBUS_VERSION_WIN10:
+ ret = hv_fb_neg_ver(sc, SV_VER_WIN10);
+ break;
+ }
+ if (ret != 0) {
+ device_printf(sc->dev, "failed to negotiate version\n");
+ return (ret);
+ }
+
+ /* Get supported resolution list */
+ ret = hv_fb_get_res(sc);
+
+ return (ret);
+}
+
+/* Essentially this hides the builtin pointer */
+static void
+hv_fb_send_ptr(hv_fb_sc *sc)
+{
+ hv_fb_msg req;
+
+ memset(&req, 0, sizeof(req));
+ req.sv_hdr.type = SV_PTR_POS;
+ req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_ptr_pos);
+ req.ptr_pos.is_visible = 1;
+ req.ptr_pos.vo = 0;
+ req.ptr_pos.img_x = 0;
+ req.ptr_pos.img_y = 0;
+ if (hv_fb_send(sc, &req, NULL) != 0)
+ device_printf(sc->dev, "failed to send ptr pos\n");
+
+ memset(&req, 0, sizeof(req));
+ req.sv_hdr.type = SV_PTR_SHAPE;
+ req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_ptr_shape);
+ req.ptr_shape.part_idx = -1;
+ req.ptr_shape.is_argb = 1;
+ req.ptr_shape.width = 1;
+ req.ptr_shape.height = 1;
+ req.ptr_shape.hs_x = 0;
+ req.ptr_shape.hs_y = 0;
+ req.ptr_shape.data[0] = 0;
+ req.ptr_shape.data[1] = 1;
+ req.ptr_shape.data[2] = 1;
+ req.ptr_shape.data[3] = 1;
+ if (hv_fb_send(sc, &req, NULL) != 0)
+ device_printf(sc->dev, "failed to send ptr shape\n");
+}
+
+static void
+hv_fb_send_situ(hv_fb_sc *sc)
+{
+ hv_fb_msg req;
+
+ memset(&req, 0, sizeof(req));
+ req.sv_hdr.type = SV_SITU;
+ req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_situ);
+ req.situ.ctx = 0;
+ req.situ.vo_cnt = 1;
+ req.situ.vo[0].active = 1;
+ req.situ.vo[0].vram_off = 0;
+ req.situ.vo[0].depth = sc->fb.fb_depth;
+ req.situ.vo[0].width = sc->fb.fb_width;
+ req.situ.vo[0].height = sc->fb.fb_height;
+ req.situ.vo[0].pitch = sc->fb.fb_stride;
+ if (hv_fb_send(sc, &req, NULL) != 0)
+ device_printf(sc->dev, "failed to send situ\n");
+}
+
+static int
+hv_fb_send_config(hv_fb_sc *sc)
+{
+ hv_fb_msg req;
+ const hv_fb_msg *resp;
+
+ memset(&req, 0, sizeof(req));
+ req.sv_hdr.type = SV_VRAM;
+ req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_vram);
+ req.vram.ctx = req.vram.gpa = sc->fb.fb_pbase;
+ req.vram.is_gpa = 1;
+
+ if (hv_fb_send(sc, &req, &resp) != 0 ||
+ resp->vram_ack.ctx != sc->fb.fb_pbase) {
+ device_printf(sc->dev, "failed to set vram\n");
+ return (ENODEV);
+ }
+
+ hv_fb_send_ptr(sc);
+ hv_fb_send_situ(sc);
+
+ return (0);
+}
+
+static void
+hv_fb_update(hv_fb_sc *sc, int x1, int y1, int x2, int y2)
+{
+ hv_fb_msg req;
+
+ if (x2 == -1)
+ x2 = sc->fb.fb_width;
+ if (y2 == -1)
+ y2 = sc->fb.fb_height;
+
+ memset(&req, 0, sizeof(req));
+ req.sv_hdr.type = SV_DIRT;
+ req.sv_hdr.size = sizeof(sv_msg_hdr) + sizeof(sv_dirt);
+ req.dirt.vo = 0;
+ req.dirt.cnt = 1;
+ req.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1;
+ req.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1;
+ req.dirt.rect[0].x2 =
+ (x2 < x1 || x2 > sc->fb.fb_width) ? sc->fb.fb_width : x2;
+ req.dirt.rect[0].y2 =
+ (y2 < y1 || y2 > sc->fb.fb_height) ? sc->fb.fb_height : y2;
+ hv_fb_send(sc, &req, NULL);
+}
+
+static void
+hv_fb_receive(hv_fb_sc *sc, struct vmbus_chanpkt_hdr *pkt)
+{
+ const hv_fb_msg *msg;
+ uint32_t msg_len;
+
+ msg = VMBUS_CHANPKT_CONST_DATA(pkt);
+ msg_len = VMBUS_CHANPKT_DATALEN(pkt);
+
+ if (msg->hdr.type != HV_MSG_DATA)
+ return;
+
+ switch (msg->sv_hdr.type) {
+ case SV_VER_RESP:
+ case SV_RES_RESP:
+ case SV_VRAM_ACK:
+ if (sc->hs_xact_ctx != NULL)
+ vmbus_xact_ctx_wakeup(sc->hs_xact_ctx, msg, msg_len);
+ break;
+ case SV_SITU_ACK:
+ /* Nothing interesting here */
+ break;
+ case SV_FEAT_CHG:
+ if (sc->ready) {
+ hv_fb_send_ptr(sc);
+ hv_fb_send_situ(sc);
+ }
+ break;
+ default:
+ device_printf(sc->dev, "unhandled type: %d\n",
+ msg->sv_hdr.type);
+ break;
+ }
+}
+
+static void
+hv_fb_read_channel(struct vmbus_channel *channel, void *ctx)
+{
+ hv_fb_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_fb_receive(sc, pkt);
+ break;
+ default:
+ device_printf(sc->dev, "unknown event: %d\n",
+ pkt->cph_type);
+ break;
+ }
+ }
+}
+
+static const struct vmbus_ic_desc vmbus_fb_descs[] = {
+ {
+ .ic_guid = { .hv_guid = {
+ 0x02, 0x78, 0x0a, 0xda, 0x77, 0xe3, 0xac, 0x4a,
+ 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8} },
+ .ic_desc = "Hyper-V framebuffer device"
+ },
+ VMBUS_IC_DESC_END
+};
+
+/* TODO: add GUID support to devmatch(8) to export vmbus_fb_descs directly */
+const struct {
+ char *guid;
+} vmbus_fb_descs_pnp[] = { { "da0a7802-e377-4aac-8e77-0558eb1073f8" } };
+
+static int hv_fb_attach(device_t dev);
+static int hv_fb_detach(device_t dev);
+
+static int
+hv_fb_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_fb_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_fb_attach(device_t dev)
+{
+ hv_fb_sc *sc;
+ struct efi_fb *efifb;
+ caddr_t kmdp;
+ int ret;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->ready = false;
+ sc->buflen = HV_BUFSIZ;
+ sc->buf = malloc(sc->buflen, M_DEVBUF, M_WAITOK | M_ZERO);
+ mtx_init(&sc->mtx, "hvfb lock", NULL, MTX_DEF);
+
+ ret = ENODEV;
+ sc->hs_chan = vmbus_get_channel(dev);
+ sc->hs_xact_ctx = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
+ HV_RINGBUFF_SZ, HV_RINGBUFF_SZ, 0);
+ if (sc->hs_xact_ctx == NULL) {
+ device_printf(sc->dev, "failed to create xact ctx\n");
+ ret = ENOMEM;
+ goto out;
+ }
+ vmbus_chan_set_readbatch(sc->hs_chan, false);
+ if (vmbus_chan_open(sc->hs_chan, HV_RINGBUFF_SZ, HV_RINGBUFF_SZ, NULL,
+ 0, hv_fb_read_channel, sc) != 0) {
+ device_printf(sc->dev, "failed to open vmbus chan\n");
+ goto out;
+ }
+
+ if (hv_fb_connect_vsp(sc) != 0)
+ goto out;
+
+ /* Get the framebuffer information from EFI loader */
+ kmdp = preload_search_by_type("elf kernel");
+ if (kmdp == NULL)
+ kmdp = preload_search_by_type("elf64 kernel");
+ efifb = (struct efi_fb *)preload_search_info(kmdp,
+ MODINFO_METADATA | MODINFOMD_EFI_FB);
+ if (efifb == NULL) {
+ device_printf(sc->dev,
+ "failed to find framebuffer information\n");
+ goto out;
+ }
+
+ sc->fb.fb_type = FBTYPE_PCIMISC;
+ sc->fb.fb_pbase = efifb->fb_addr;
+ sc->fb.fb_size = efifb->fb_size;
+ sc->fb.fb_vbase = (intptr_t)pmap_mapdev_attr(sc->fb.fb_pbase,
+ sc->fb.fb_size, VM_MEMATTR_WRITE_COMBINING);
+ sc->fb.fb_depth = 32; /* XXX */
+ sc->fb.fb_bpp = roundup2(sc->fb.fb_depth, NBBY);
+ sc->fb.fb_stride = sc->fb.fb_width * (sc->fb.fb_bpp / NBBY);
+ sc->fb.fb_cmsize = 0;
+
+ if (hv_fb_send_config(sc) != 0)
+ goto out;
+
+ sc->fb.fb_fbd_dev = device_add_child(dev, "fbd", -1);
+ if (sc->fb.fb_fbd_dev == NULL || vt_fb_attach(&sc->fb) != 0) {
+ device_printf(sc->dev, "failed to attach fbd");
+ goto out;
+ }
+
+ sc->ready = true;
+ ret = 0;
+
+ /* This allows updates for a minute on load */
+ for (int i = 0; i < 1200; i++) {
+ hv_fb_update(sc, 0, 0, -1, -1);
+ pause("test", hz / 20);
+ }
+out:
+ if (ret != 0)
+ hv_fb_detach(dev);
+ return (ret);
+}
+
+static int
+hv_fb_detach(device_t dev)
+{
+ hv_fb_sc *sc;
+
+ sc = device_get_softc(dev);
+ sc->ready = false;
+ if (sc->fb.fb_fbd_dev != NULL) {
+ mtx_lock(&Giant);
+ device_delete_child(dev, sc->fb.fb_fbd_dev);
+ mtx_unlock(&Giant);
+ sc->fb.fb_fbd_dev = NULL;
+ }
+ vt_fb_detach(&sc->fb);
+
+ 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);
+ mtx_destroy(&sc->mtx);
+
+ return (0);
+}
+
+static device_method_t hv_fb_methods[] = {
+ DEVMETHOD(device_probe, hv_fb_probe),
+ DEVMETHOD(device_attach, hv_fb_attach),
+ DEVMETHOD(device_detach, hv_fb_detach),
+ DEVMETHOD_END,
+};
+
+static driver_t hv_fb_driver = {
+ .name = "hvfb",
+ .methods = hv_fb_methods,
+ .size = sizeof(hv_fb_sc),
+};
+
+DRIVER_MODULE(hv_fb, vmbus, hv_fb_driver, NULL, NULL);
+MODULE_VERSION(hv_fb, 1);
+MODULE_DEPEND(hv_fb, vmbus, 1, 1, 1);
+MODULE_DEPEND(hv_fb, fbd, 1, 1, 1);
diff --git a/sys/modules/hyperv/fb/Makefile b/sys/modules/hyperv/fb/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/hyperv/fb/Makefile
@@ -0,0 +1,11 @@
+.PATH: ${SRCTOP}/sys/dev/hyperv/hvfb
+
+KMOD= hv_fb
+SRCS= hv_fb.c
+SRCS+= bus_if.h device_if.h vmbus_if.h
+SRCS+= opt_splash.h opt_syscons.h opt_teken.h
+
+CFLAGS+= -I${SRCTOP}/sys/dev/hyperv/include \
+ -I${SRCTOP}/sys/dev/hyperv/vmbus
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Thu, Jun 25, 6:53 AM (51 m, 45 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34309519
Default Alt Text
D39395.id119789.diff (16 KB)

Event Timeline