Import bhyve_graphics into CURRENT. Thanks to all who tested

this on the branch.

Original commit message:
  Initial bhyve native graphics support.

  This adds emulations for a raw framebuffer device, PS2 keyboard/mouse,
  XHCI USB controller and a USB tablet.

  A simple VNC server is provided for keyboard/mouse input, and graphics
  output.

  A VGA emulation is included, but is currently disconnected until an
  additional bhyve change to block out VGA memory is committed.

  Credits:
   - raw framebuffer, VNC server, XHCI controller, USB bus/device emulation
      and UEFI f/w support by Leon Dang
   - VGA, console/g, initial VNC server  by tychon@
   - PS2 keyboard/mouse jointly done by tychon@ and Leon Dang
   - hypervisor framebuffer mem support by neel@

  Tested by: Michael Dexter, in a number of revisions of this code.

  With the appropriate UEFI image, FreeBSD, Windows and Linux guests can
  installed and run in graphics mode using the UEFI/GOP framebuffer.

Approved by:	re (gjb)
This commit is contained in:
Peter Grehan 2016-07-04 03:19:06 +00:00
commit 2cf9911fc1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=302332
24 changed files with 9037 additions and 19 deletions

View File

@ -14,9 +14,11 @@ BHYVE_SYSDIR?=${SRCTOP}
SRCS= \
atkbdc.c \
acpi.c \
bhyvegc.c \
bhyverun.c \
block_if.c \
bootrom.c \
console.c \
consport.c \
dbgport.c \
fwctl.c \
@ -27,6 +29,7 @@ SRCS= \
mptbl.c \
pci_ahci.c \
pci_emul.c \
pci_fbuf.c \
pci_hostbridge.c \
pci_irq.c \
pci_lpc.c \
@ -35,20 +38,30 @@ SRCS= \
pci_virtio_net.c \
pci_virtio_rnd.c \
pci_uart.c \
pci_xhci.c \
pm.c \
post.c \
ps2kbd.c \
ps2mouse.c \
rfb.c \
rtc.c \
smbiostbl.c \
sockstream.c \
task_switch.c \
uart_emul.c \
usb_emul.c \
usb_mouse.c \
virtio.c \
vga.c \
xmsr.c \
spinup_ap.c
.PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm
SRCS+= vmm_instruction_emul.c
LIBADD= vmmapi md pthread
LIBADD= vmmapi md pthread z
CFLAGS+= -I${BHYVE_SYSDIR}/sys/dev/usb/controller
WARNS?= 2

View File

@ -1,5 +1,6 @@
/*-
* Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2015 Nahanni Systems Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -35,56 +36,548 @@ __FBSDID("$FreeBSD$");
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <pthread_np.h>
#include "acpi.h"
#include "inout.h"
#include "pci_emul.h"
#include "pci_irq.h"
#include "pci_lpc.h"
#include "ps2kbd.h"
#include "ps2mouse.h"
#define KBD_DATA_PORT 0x60
#define KBD_STS_CTL_PORT 0x64
#define KBD_SYS_FLAG 0x4
#define KBDC_RESET 0xfe
#define KBD_DEV_IRQ 1
#define AUX_DEV_IRQ 12
/* controller commands */
#define KBDC_SET_COMMAND_BYTE 0x60
#define KBDC_GET_COMMAND_BYTE 0x20
#define KBDC_DISABLE_AUX_PORT 0xa7
#define KBDC_ENABLE_AUX_PORT 0xa8
#define KBDC_TEST_AUX_PORT 0xa9
#define KBDC_TEST_CTRL 0xaa
#define KBDC_TEST_KBD_PORT 0xab
#define KBDC_DISABLE_KBD_PORT 0xad
#define KBDC_ENABLE_KBD_PORT 0xae
#define KBDC_READ_INPORT 0xc0
#define KBDC_READ_OUTPORT 0xd0
#define KBDC_WRITE_OUTPORT 0xd1
#define KBDC_WRITE_KBD_OUTBUF 0xd2
#define KBDC_WRITE_AUX_OUTBUF 0xd3
#define KBDC_WRITE_TO_AUX 0xd4
/* controller command byte (set by KBDC_SET_COMMAND_BYTE) */
#define KBD_TRANSLATION 0x40
#define KBD_SYS_FLAG_BIT 0x04
#define KBD_DISABLE_KBD_PORT 0x10
#define KBD_DISABLE_AUX_PORT 0x20
#define KBD_ENABLE_AUX_INT 0x02
#define KBD_ENABLE_KBD_INT 0x01
#define KBD_KBD_CONTROL_BITS (KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT)
#define KBD_AUX_CONTROL_BITS (KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT)
/* controller status bits */
#define KBDS_KBD_BUFFER_FULL 0x01
#define KBDS_SYS_FLAG 0x04
#define KBDS_CTRL_FLAG 0x08
#define KBDS_AUX_BUFFER_FULL 0x20
/* controller output port */
#define KBDO_KBD_OUTFULL 0x10
#define KBDO_AUX_OUTFULL 0x20
#define RAMSZ 32
#define FIFOSZ 15
#define CTRL_CMD_FLAG 0x8000
struct kbd_dev {
bool irq_active;
int irq;
uint8_t buffer[FIFOSZ];
int brd, bwr;
int bcnt;
};
struct aux_dev {
bool irq_active;
int irq;
};
struct atkbdc_softc {
struct vmctx *ctx;
pthread_mutex_t mtx;
struct ps2kbd_softc *ps2kbd_sc;
struct ps2mouse_softc *ps2mouse_sc;
uint8_t status; /* status register */
uint8_t outport; /* controller output port */
uint8_t ram[RAMSZ]; /* byte0 = controller config */
uint32_t curcmd; /* current command for next byte */
uint32_t ctrlbyte;
struct kbd_dev kbd;
struct aux_dev aux;
};
static void
atkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
{
if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
sc->kbd.irq_active = true;
vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
}
}
static void
atkbdc_assert_aux_intr(struct atkbdc_softc *sc)
{
if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
sc->aux.irq_active = true;
vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
}
}
static int
atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
if (sc->kbd.bcnt < FIFOSZ) {
sc->kbd.buffer[sc->kbd.bwr] = val;
sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
sc->kbd.bcnt++;
sc->status |= KBDS_KBD_BUFFER_FULL;
sc->outport |= KBDO_KBD_OUTFULL;
} else {
printf("atkbd data buffer full\n");
}
return (sc->kbd.bcnt < FIFOSZ);
}
static void
atkbdc_kbd_read(struct atkbdc_softc *sc)
{
const uint8_t translation[256] = {
0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
uint8_t val;
uint8_t release = 0;
assert(pthread_mutex_isowned_np(&sc->mtx));
if (sc->ram[0] & KBD_TRANSLATION) {
while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) {
if (val == 0xf0) {
release = 0x80;
continue;
} else {
val = translation[val] | release;
}
atkbdc_kbd_queue_data(sc, val);
break;
}
} else {
while (sc->kbd.bcnt < FIFOSZ) {
if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
atkbdc_kbd_queue_data(sc, val);
else
break;
}
}
if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
atkbdc_assert_kbd_intr(sc);
}
static void
atkbdc_aux_poll(struct atkbdc_softc *sc)
{
if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
sc->outport |= KBDO_AUX_OUTFULL;
atkbdc_assert_aux_intr(sc);
}
}
static void
atkbdc_kbd_poll(struct atkbdc_softc *sc)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
atkbdc_kbd_read(sc);
}
static void
atkbdc_poll(struct atkbdc_softc *sc)
{
atkbdc_aux_poll(sc);
atkbdc_kbd_poll(sc);
}
static void
atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
if (sc->kbd.bcnt == 0)
sc->status &= ~(KBDS_AUX_BUFFER_FULL |
KBDS_KBD_BUFFER_FULL);
else
sc->status &= ~(KBDS_AUX_BUFFER_FULL);
sc->outport &= ~KBDO_AUX_OUTFULL;
}
atkbdc_poll(sc);
return;
}
if (sc->kbd.bcnt > 0) {
*buf = sc->kbd.buffer[sc->kbd.brd];
sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
sc->kbd.bcnt--;
if (sc->kbd.bcnt == 0) {
sc->status &= ~KBDS_KBD_BUFFER_FULL;
sc->outport &= ~KBDO_KBD_OUTFULL;
}
atkbdc_poll(sc);
}
if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
}
}
static int
atkbdc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
struct atkbdc_softc *sc;
uint8_t buf;
int retval;
if (bytes != 1)
return (-1);
sc = arg;
retval = 0;
*eax = 0;
pthread_mutex_lock(&sc->mtx);
if (in) {
sc->curcmd = 0;
if (sc->ctrlbyte != 0) {
*eax = sc->ctrlbyte & 0xff;
sc->ctrlbyte = 0;
} else {
/* read device buffer; includes kbd cmd responses */
atkbdc_dequeue_data(sc, &buf);
*eax = buf;
}
return (0);
sc->status &= ~KBDS_CTRL_FLAG;
pthread_mutex_unlock(&sc->mtx);
return (retval);
}
if (sc->status & KBDS_CTRL_FLAG) {
/*
* Command byte for the controller.
*/
switch (sc->curcmd) {
case KBDC_SET_COMMAND_BYTE:
sc->ram[0] = *eax;
if (sc->ram[0] & KBD_SYS_FLAG_BIT)
sc->status |= KBDS_SYS_FLAG;
else
sc->status &= ~KBDS_SYS_FLAG;
break;
case KBDC_WRITE_OUTPORT:
sc->outport = *eax;
break;
case KBDC_WRITE_TO_AUX:
ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
atkbdc_poll(sc);
break;
case KBDC_WRITE_KBD_OUTBUF:
atkbdc_kbd_queue_data(sc, *eax);
break;
case KBDC_WRITE_AUX_OUTBUF:
ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
atkbdc_aux_poll(sc);
break;
default:
/* write to particular RAM byte */
if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) {
int byten;
byten = (sc->curcmd - 0x60) & 0x1f;
sc->ram[byten] = *eax & 0xff;
}
break;
}
sc->curcmd = 0;
sc->status &= ~KBDS_CTRL_FLAG;
pthread_mutex_unlock(&sc->mtx);
return (retval);
}
/*
* Data byte for the device.
*/
ps2kbd_write(sc->ps2kbd_sc, *eax);
atkbdc_poll(sc);
pthread_mutex_unlock(&sc->mtx);
return (retval);
}
static int
atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
int bytes, uint32_t *eax, void *arg)
{
int error, retval;
struct atkbdc_softc *sc;
int error, retval;
if (bytes != 1)
return (-1);
sc = arg;
retval = 0;
pthread_mutex_lock(&sc->mtx);
if (in) {
*eax = KBD_SYS_FLAG; /* system passed POST */
} else {
switch (*eax) {
case KBDC_RESET: /* Pulse "reset" line. */
error = vm_suspend(ctx, VM_SUSPEND_RESET);
assert(error == 0 || errno == EALREADY);
break;
/* read status register */
*eax = sc->status;
pthread_mutex_unlock(&sc->mtx);
return (retval);
}
sc->curcmd = 0;
sc->status |= KBDS_CTRL_FLAG;
sc->ctrlbyte = 0;
switch (*eax) {
case KBDC_GET_COMMAND_BYTE:
sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
break;
case KBDC_TEST_CTRL:
sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
break;
case KBDC_TEST_AUX_PORT:
case KBDC_TEST_KBD_PORT:
sc->ctrlbyte = CTRL_CMD_FLAG | 0;
break;
case KBDC_READ_INPORT:
sc->ctrlbyte = CTRL_CMD_FLAG | 0;
break;
case KBDC_READ_OUTPORT:
sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
break;
case KBDC_SET_COMMAND_BYTE:
case KBDC_WRITE_OUTPORT:
case KBDC_WRITE_KBD_OUTBUF:
case KBDC_WRITE_AUX_OUTBUF:
sc->curcmd = *eax;
break;
case KBDC_DISABLE_KBD_PORT:
sc->ram[0] |= KBD_DISABLE_KBD_PORT;
break;
case KBDC_ENABLE_KBD_PORT:
sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
if (sc->kbd.bcnt > 0)
sc->status |= KBDS_KBD_BUFFER_FULL;
atkbdc_poll(sc);
break;
case KBDC_WRITE_TO_AUX:
sc->curcmd = *eax;
break;
case KBDC_DISABLE_AUX_PORT:
sc->ram[0] |= KBD_DISABLE_AUX_PORT;
ps2mouse_toggle(sc->ps2mouse_sc, 0);
sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
sc->outport &= ~KBDS_AUX_BUFFER_FULL;
break;
case KBDC_ENABLE_AUX_PORT:
sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
ps2mouse_toggle(sc->ps2mouse_sc, 1);
if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
break;
case KBDC_RESET: /* Pulse "reset" line */
error = vm_suspend(ctx, VM_SUSPEND_RESET);
assert(error == 0 || errno == EALREADY);
break;
default:
if (*eax >= 0x21 && *eax <= 0x3f) {
/* read "byte N" from RAM */
int byten;
byten = (*eax - 0x20) & 0x1f;
sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
}
break;
}
pthread_mutex_unlock(&sc->mtx);
if (sc->ctrlbyte != 0) {
sc->status |= KBDS_KBD_BUFFER_FULL;
sc->status &= ~KBDS_AUX_BUFFER_FULL;
atkbdc_assert_kbd_intr(sc);
} else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
(sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
atkbdc_assert_aux_intr(sc);
} else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
sc->status |= KBDS_KBD_BUFFER_FULL;
atkbdc_assert_kbd_intr(sc);
}
return (retval);
}
INOUT_PORT(atkdbc, KBD_DATA_PORT, IOPORT_F_INOUT, atkbdc_data_handler);
SYSRES_IO(KBD_DATA_PORT, 1);
INOUT_PORT(atkbdc, KBD_STS_CTL_PORT, IOPORT_F_INOUT,
atkbdc_sts_ctl_handler);
SYSRES_IO(KBD_STS_CTL_PORT, 1);
void
atkbdc_event(struct atkbdc_softc *sc, int iskbd)
{
pthread_mutex_lock(&sc->mtx);
if (iskbd)
atkbdc_kbd_poll(sc);
else
atkbdc_aux_poll(sc);
pthread_mutex_unlock(&sc->mtx);
}
void
atkbdc_init(struct vmctx *ctx)
{
struct inout_port iop;
struct atkbdc_softc *sc;
int error;
sc = calloc(1, sizeof(struct atkbdc_softc));
sc->ctx = ctx;
pthread_mutex_init(&sc->mtx, NULL);
bzero(&iop, sizeof(struct inout_port));
iop.name = "atkdbc";
iop.port = KBD_STS_CTL_PORT;
iop.size = 1;
iop.flags = IOPORT_F_INOUT;
iop.handler = atkbdc_sts_ctl_handler;
iop.arg = sc;
error = register_inout(&iop);
assert(error == 0);
bzero(&iop, sizeof(struct inout_port));
iop.name = "atkdbc";
iop.port = KBD_DATA_PORT;
iop.size = 1;
iop.flags = IOPORT_F_INOUT;
iop.handler = atkbdc_data_handler;
iop.arg = sc;
error = register_inout(&iop);
assert(error == 0);
pci_irq_reserve(KBD_DEV_IRQ);
sc->kbd.irq = KBD_DEV_IRQ;
pci_irq_reserve(AUX_DEV_IRQ);
sc->aux.irq = AUX_DEV_IRQ;
sc->ps2kbd_sc = ps2kbd_init(sc);
sc->ps2mouse_sc = ps2mouse_init(sc);
}
static void
atkbdc_dsdt(void)
{
dsdt_line("");
dsdt_line("Device (KBD)");
dsdt_line("{");
dsdt_line(" Name (_HID, EisaId (\"PNP0303\"))");
dsdt_line(" Name (_CRS, ResourceTemplate ()");
dsdt_line(" {");
dsdt_indent(2);
dsdt_fixed_ioport(KBD_DATA_PORT, 1);
dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
dsdt_fixed_irq(1);
dsdt_unindent(2);
dsdt_line(" })");
dsdt_line("}");
dsdt_line("");
dsdt_line("Device (MOU)");
dsdt_line("{");
dsdt_line(" Name (_HID, EisaId (\"PNP0F13\"))");
dsdt_line(" Name (_CRS, ResourceTemplate ()");
dsdt_line(" {");
dsdt_indent(2);
dsdt_fixed_ioport(KBD_DATA_PORT, 1);
dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
dsdt_fixed_irq(12);
dsdt_unindent(2);
dsdt_line(" })");
dsdt_line("}");
}
LPC_DSDT(atkbdc_dsdt);

38
usr.sbin/bhyve/atkbdc.h Normal file
View File

@ -0,0 +1,38 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _ATKBDC_H_
#define _ATKBDC_H_
struct atkbdc_softc;
struct vmctx;
void atkbdc_init(struct vmctx *ctx);
void atkbdc_event(struct atkbdc_softc *sc, int iskbd);
#endif /* _ATKBDC_H_ */

72
usr.sbin/bhyve/bhyvegc.c Normal file
View File

@ -0,0 +1,72 @@
#include <sys/cdefs.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bhyvegc.h"
struct bhyvegc {
struct bhyvegc_image *gc_image;
int raw;
};
struct bhyvegc *
bhyvegc_init(int width, int height, void *fbaddr)
{
struct bhyvegc *gc;
struct bhyvegc_image *gc_image;
gc = calloc(1, sizeof (struct bhyvegc));
gc_image = calloc(1, sizeof(struct bhyvegc_image));
gc_image->width = width;
gc_image->height = height;
if (fbaddr) {
gc_image->data = fbaddr;
gc->raw = 1;
} else {
gc_image->data = calloc(width * height, sizeof (uint32_t));
gc->raw = 0;
}
gc->gc_image = gc_image;
return (gc);
}
void
bhyvegc_set_fbaddr(struct bhyvegc *gc, void *fbaddr)
{
gc->raw = 1;
if (gc->gc_image->data && gc->gc_image->data != fbaddr)
free(gc->gc_image->data);
gc->gc_image->data = fbaddr;
}
void
bhyvegc_resize(struct bhyvegc *gc, int width, int height)
{
struct bhyvegc_image *gc_image;
gc_image = gc->gc_image;
gc_image->width = width;
gc_image->height = height;
if (!gc->raw) {
gc_image->data = realloc(gc_image->data,
sizeof (uint32_t) * width * height);
memset(gc_image->data, 0, width * height * sizeof (uint32_t));
}
}
struct bhyvegc_image *
bhyvegc_get_image(struct bhyvegc *gc)
{
if (gc == NULL)
return (NULL);
return (gc->gc_image);
}

46
usr.sbin/bhyve/bhyvegc.h Normal file
View File

@ -0,0 +1,46 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _BHYVEGC_H_
#define _BHYVEGC_H_
struct bhyvegc;
struct bhyvegc_image {
int vgamode;
int width;
int height;
uint32_t *data;
};
struct bhyvegc *bhyvegc_init(int width, int height, void *fbaddr);
void bhyvegc_set_fbaddr(struct bhyvegc *gc, void *fbaddr);
void bhyvegc_resize(struct bhyvegc *gc, int width, int height);
struct bhyvegc_image *bhyvegc_get_image(struct bhyvegc *gc);
#endif /* _BHYVEGC_H_ */

View File

@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include "bhyverun.h"
#include "acpi.h"
#include "atkbdc.h"
#include "inout.h"
#include "dbgport.h"
#include "fwctl.h"
@ -125,7 +126,7 @@ usage(int code)
fprintf(stderr,
"Usage: %s [-abehuwxACHPSWY] [-c vcpus] [-g <gdb port>] [-l <lpc>]\n"
" %*s [-m memsize[K|k|M|m|G|g|T|t]] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] <vm>\n"
" %*s [-m mem] [-p vcpu:hostcpu] [-s <pci>] [-U uuid] <vm>\n"
" -a: local apic is in xAPIC mode (deprecated)\n"
" -A: create ACPI tables\n"
" -c: # cpus (default 1)\n"
@ -135,7 +136,7 @@ usage(int code)
" -h: help\n"
" -H: vmexit from the guest on hlt\n"
" -l: LPC device configuration\n"
" -m: memory size\n"
" -m: memory size in MB\n"
" -p: pin 'vcpu' to 'hostcpu'\n"
" -P: vmexit from the guest on pause\n"
" -s: <slot,driver,configinfo> PCI slot config\n"
@ -901,6 +902,7 @@ main(int argc, char *argv[])
init_mem();
init_inout();
atkbdc_init(ctx);
pci_irq_init(ctx);
ioapic_init(ctx);

118
usr.sbin/bhyve/console.c Normal file
View File

@ -0,0 +1,118 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include "bhyvegc.h"
#include "console.h"
static struct {
struct bhyvegc *gc;
fb_render_func_t fb_render_cb;
void *fb_arg;
kbd_event_func_t kbd_event_cb;
void *kbd_arg;
int kbd_priority;
ptr_event_func_t ptr_event_cb;
void *ptr_arg;
int ptr_priority;
} console;
void
console_init(int w, int h, void *fbaddr)
{
console.gc = bhyvegc_init(w, h, fbaddr);
}
void
console_set_fbaddr(void *fbaddr)
{
bhyvegc_set_fbaddr(console.gc, fbaddr);
}
struct bhyvegc_image *
console_get_image(void)
{
struct bhyvegc_image *bhyvegc_image;
bhyvegc_image = bhyvegc_get_image(console.gc);
return (bhyvegc_image);
}
void
console_fb_register(fb_render_func_t render_cb, void *arg)
{
console.fb_render_cb = render_cb;
console.fb_arg = arg;
}
void
console_refresh(void)
{
if (console.fb_render_cb)
(*console.fb_render_cb)(console.gc, console.fb_arg);
}
void
console_kbd_register(kbd_event_func_t event_cb, void *arg, int pri)
{
if (pri > console.kbd_priority) {
console.kbd_event_cb = event_cb;
console.kbd_arg = arg;
console.kbd_priority = pri;
}
}
void
console_ptr_register(ptr_event_func_t event_cb, void *arg, int pri)
{
if (pri > console.ptr_priority) {
console.ptr_event_cb = event_cb;
console.ptr_arg = arg;
console.ptr_priority = pri;
}
}
void
console_key_event(int down, uint32_t keysym)
{
if (console.kbd_event_cb)
(*console.kbd_event_cb)(down, keysym, console.kbd_arg);
}
void
console_ptr_event(uint8_t button, int x, int y)
{
if (console.ptr_event_cb)
(*console.ptr_event_cb)(button, x, y, console.ptr_arg);
}

53
usr.sbin/bhyve/console.h Normal file
View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _CONSOLE_H_
#define _CONSOLE_H_
struct bhyvegc;
typedef void (*fb_render_func_t)(struct bhyvegc *gc, void *arg);
typedef void (*kbd_event_func_t)(int down, uint32_t keysym, void *arg);
typedef void (*ptr_event_func_t)(uint8_t mask, int x, int y, void *arg);
void console_init(int w, int h, void *fbaddr);
void console_set_fbaddr(void *fbaddr);
struct bhyvegc_image *console_get_image(void);
void console_fb_register(fb_render_func_t render_cb, void *arg);
void console_refresh(void);
void console_kbd_register(kbd_event_func_t event_cb, void *arg, int pri);
void console_key_event(int down, uint32_t keysym);
void console_ptr_register(ptr_event_func_t event_cb, void *arg, int pri);
void console_ptr_event(uint8_t button, int x, int y);
#endif /* _CONSOLE_H_ */

406
usr.sbin/bhyve/pci_fbuf.c Normal file
View File

@ -0,0 +1,406 @@
/*-
* Copyright (c) 2015 Nahanni Systems, Inc.
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/mman.h>
#include <machine/vmm.h>
#include <vmmapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "bhyvegc.h"
#include "bhyverun.h"
#include "console.h"
#include "inout.h"
#include "pci_emul.h"
#include "rfb.h"
#include "vga.h"
/*
* bhyve Framebuffer device emulation.
* BAR0 points to the current mode information.
* BAR1 is the 32-bit framebuffer address.
*
* -s <b>,fbuf,wait,tcp=<ip>:port,w=width,h=height
*/
static int fbuf_debug = 1;
#define DEBUG_INFO 1
#define DEBUG_VERBOSE 4
#define DPRINTF(level, params) if (level <= fbuf_debug) printf params
#define KB (1024UL)
#define MB (1024 * 1024UL)
#define DMEMSZ 128
#define FB_SIZE (16*MB)
#define COLS_MAX 1920
#define ROWS_MAX 1200
#define COLS_DEFAULT 1024
#define ROWS_DEFAULT 768
#define COLS_MIN 640
#define ROWS_MIN 480
struct pci_fbuf_softc {
struct pci_devinst *fsc_pi;
struct {
uint32_t fbsize;
uint16_t width;
uint16_t height;
uint16_t depth;
uint16_t refreshrate;
uint8_t reserved[116];
} __packed memregs;
/* rfb server */
char *rfb_host;
int rfb_port;
int rfb_wait;
int use_vga;
uint32_t fbaddr;
char *fb_base;
uint16_t gc_width;
uint16_t gc_height;
void *vgasc;
struct bhyvegc_image *gc_image;
};
static struct pci_fbuf_softc *fbuf_sc;
#define PCI_FBUF_MSI_MSGS 4
static void
pci_fbuf_usage(char *opt)
{
fprintf(stderr, "Invalid fbuf emulation \"%s\"\r\n", opt);
fprintf(stderr, "fbuf: {wait,}tcp=<ip>:port\r\n");
}
static void
pci_fbuf_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
int baridx, uint64_t offset, int size, uint64_t value)
{
struct pci_fbuf_softc *sc;
uint8_t *p;
assert(baridx == 0);
sc = pi->pi_arg;
DPRINTF(DEBUG_VERBOSE,
("fbuf wr: offset 0x%lx, size: %d, value: 0x%lx\n",
offset, size, value));
if (offset + size > DMEMSZ) {
printf("fbuf: write too large, offset %ld size %d\n",
offset, size);
return;
}
p = (uint8_t *)&sc->memregs + offset;
switch (size) {
case 1:
*p = value;
break;
case 2:
*(uint16_t *)p = value;
break;
case 4:
*(uint32_t *)p = value;
break;
case 8:
*(uint64_t *)p = value;
break;
default:
printf("fbuf: write unknown size %d\n", size);
break;
}
if (!sc->gc_image->vgamode && sc->memregs.width == 0 &&
sc->memregs.height == 0) {
DPRINTF(DEBUG_INFO, ("switching to VGA mode\r\n"));
sc->gc_image->vgamode = 1;
sc->gc_width = 0;
sc->gc_height = 0;
} else if (sc->gc_image->vgamode && sc->memregs.width != 0 &&
sc->memregs.height != 0) {
DPRINTF(DEBUG_INFO, ("switching to VESA mode\r\n"));
sc->gc_image->vgamode = 0;
}
}
uint64_t
pci_fbuf_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
int baridx, uint64_t offset, int size)
{
struct pci_fbuf_softc *sc;
uint8_t *p;
uint64_t value;
assert(baridx == 0);
sc = pi->pi_arg;
if (offset + size > DMEMSZ) {
printf("fbuf: read too large, offset %ld size %d\n",
offset, size);
return (0);
}
p = (uint8_t *)&sc->memregs + offset;
value = 0;
switch (size) {
case 1:
value = *p;
break;
case 2:
value = *(uint16_t *)p;
break;
case 4:
value = *(uint32_t *)p;
break;
case 8:
value = *(uint64_t *)p;
break;
default:
printf("fbuf: read unknown size %d\n", size);
break;
}
DPRINTF(DEBUG_VERBOSE,
("fbuf rd: offset 0x%lx, size: %d, value: 0x%lx\n",
offset, size, value));
return (value);
}
static int
pci_fbuf_parse_opts(struct pci_fbuf_softc *sc, char *opts)
{
char *uopts, *xopts, *config;
char *tmpstr;
int ret;
ret = 0;
uopts = strdup(opts);
for (xopts = strtok(uopts, ",");
xopts != NULL;
xopts = strtok(NULL, ",")) {
if (strcmp(xopts, "wait") == 0) {
sc->rfb_wait = 1;
continue;
}
#if 0 /* notyet */
if (strcmp(xopts, "vga") == 0) {
sc->use_vga = 1;
continue;
}
#endif
if ((config = strchr(xopts, '=')) == NULL) {
pci_fbuf_usage(xopts);
ret = -1;
goto done;
}
*config++ = '\0';
DPRINTF(DEBUG_VERBOSE, ("pci_fbuf option %s = %s\r\n",
xopts, config));
if (!strcmp(xopts, "tcp")) {
/* parse host-ip:port */
tmpstr = strsep(&config, ":");
if (!config)
sc->rfb_port = atoi(tmpstr);
else {
sc->rfb_port = atoi(config);
sc->rfb_host = tmpstr;
}
} else if (!strcmp(xopts, "w")) {
sc->memregs.width = atoi(config);
if (sc->memregs.width > COLS_MAX) {
pci_fbuf_usage(xopts);
ret = -1;
goto done;
} else if (sc->memregs.width == 0)
sc->memregs.width = 1920;
} else if (!strcmp(xopts, "h")) {
sc->memregs.height = atoi(config);
if (sc->memregs.height > ROWS_MAX) {
pci_fbuf_usage(xopts);
ret = -1;
goto done;
} else if (sc->memregs.height == 0)
sc->memregs.height = 1080;
} else {
pci_fbuf_usage(xopts);
ret = -1;
goto done;
}
}
done:
return (ret);
}
extern void vga_render(struct bhyvegc *gc, void *arg);
void
pci_fbuf_render(struct bhyvegc *gc, void *arg)
{
struct pci_fbuf_softc *sc;
sc = arg;
if (sc->use_vga && sc->gc_image->vgamode) {
/* TODO: mode switching to vga and vesa should use the special
* EFI-bhyve protocol port.
*/
vga_render(gc, sc->vgasc);
return;
}
if (sc->gc_width != sc->memregs.width ||
sc->gc_height != sc->memregs.height) {
bhyvegc_resize(gc, sc->memregs.width, sc->memregs.height);
sc->gc_width = sc->memregs.width;
sc->gc_height = sc->memregs.height;
}
return;
}
static int
pci_fbuf_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
{
int error, prot;
struct pci_fbuf_softc *sc;
if (fbuf_sc != NULL) {
fprintf(stderr, "Only one frame buffer device is allowed.\n");
return (-1);
}
sc = calloc(1, sizeof(struct pci_fbuf_softc));
pi->pi_arg = sc;
/* initialize config space */
pci_set_cfgdata16(pi, PCIR_DEVICE, 0x40FB);
pci_set_cfgdata16(pi, PCIR_VENDOR, 0xFB5D);
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_DISPLAY);
pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_DISPLAY_VGA);
error = pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, DMEMSZ);
assert(error == 0);
error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, FB_SIZE);
assert(error == 0);
error = pci_emul_add_msicap(pi, PCI_FBUF_MSI_MSGS);
assert(error == 0);
sc->fbaddr = pi->pi_bar[1].addr;
sc->memregs.fbsize = FB_SIZE;
sc->memregs.width = COLS_DEFAULT;
sc->memregs.height = ROWS_DEFAULT;
sc->memregs.depth = 32;
sc->fsc_pi = pi;
error = pci_fbuf_parse_opts(sc, opts);
if (error != 0)
goto done;
sc->fb_base = vm_create_devmem(ctx, VM_FRAMEBUFFER, "framebuffer", FB_SIZE);
if (sc->fb_base == MAP_FAILED) {
error = -1;
goto done;
}
DPRINTF(DEBUG_INFO, ("fbuf frame buffer base: %p [sz %lu]\r\n",
sc->fb_base, FB_SIZE));
/*
* Map the framebuffer into the guest address space.
* XXX This may fail if the BAR is different than a prior
* run. In this case flag the error. This will be fixed
* when a change_memseg api is available.
*/
prot = PROT_READ | PROT_WRITE;
if (vm_mmap_memseg(ctx, sc->fbaddr, VM_FRAMEBUFFER, 0, FB_SIZE, prot) != 0) {
fprintf(stderr, "pci_fbuf: mapseg failed - try deleting VM and restarting\n");
error = -1;
goto done;
}
console_init(sc->memregs.width, sc->memregs.height, sc->fb_base);
console_fb_register(pci_fbuf_render, sc);
sc->vgasc = vga_init(!sc->use_vga);
sc->gc_image = console_get_image();
fbuf_sc = sc;
memset((void *)sc->fb_base, 0, FB_SIZE);
error = rfb_init(sc->rfb_host, sc->rfb_port, sc->rfb_wait);
done:
if (error)
free(sc);
return (error);
}
struct pci_devemu pci_fbuf = {
.pe_emu = "fbuf",
.pe_init = pci_fbuf_init,
.pe_barwrite = pci_fbuf_write,
.pe_barread = pci_fbuf_read
};
PCI_EMUL_SET(pci_fbuf);

2828
usr.sbin/bhyve/pci_xhci.c Normal file

File diff suppressed because it is too large Load Diff

353
usr.sbin/bhyve/pci_xhci.h Normal file
View File

@ -0,0 +1,353 @@
/*-
* Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
* All rights reserved.
*
* 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, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _PCI_XHCI_H_
#define _PCI_XHCI_H_
#define PCI_USBREV 0x60 /* USB protocol revision */
enum { /* dsc_slotstate */
XHCI_ST_DISABLED,
XHCI_ST_ENABLED,
XHCI_ST_DEFAULT,
XHCI_ST_ADDRESSED,
XHCI_ST_CONFIGURED,
XHCI_ST_MAX
};
enum {
XHCI_ST_SLCTX_DISABLED,
XHCI_ST_SLCTX_DEFAULT,
XHCI_ST_SLCTX_ADDRESSED,
XHCI_ST_SLCTX_CONFIGURED
};
enum {
XHCI_ST_EPCTX_DISABLED,
XHCI_ST_EPCTX_RUNNING,
XHCI_ST_EPCTX_HALTED,
XHCI_ST_EPCTX_STOPPED,
XHCI_ST_EPCTX_ERROR
};
#define XHCI_MAX_DEVICES MIN(USB_MAX_DEVICES, 128)
#define XHCI_MAX_ENDPOINTS 32 /* hardcoded - do not change */
#define XHCI_MAX_SCRATCHPADS 32
#define XHCI_MAX_EVENTS (16 * 13)
#define XHCI_MAX_COMMANDS (16 * 1)
#define XHCI_MAX_RSEG 1
#define XHCI_MAX_TRANSFERS 4
#if USB_MAX_EP_STREAMS == 8
#define XHCI_MAX_STREAMS 8
#define XHCI_MAX_STREAMS_LOG 3
#elif USB_MAX_EP_STREAMS == 1
#define XHCI_MAX_STREAMS 1
#define XHCI_MAX_STREAMS_LOG 0
#else
#error "The USB_MAX_EP_STREAMS value is not supported."
#endif
#define XHCI_DEV_CTX_ADDR_ALIGN 64 /* bytes */
#define XHCI_DEV_CTX_ALIGN 64 /* bytes */
#define XHCI_INPUT_CTX_ALIGN 64 /* bytes */
#define XHCI_SLOT_CTX_ALIGN 32 /* bytes */
#define XHCI_ENDP_CTX_ALIGN 32 /* bytes */
#define XHCI_STREAM_CTX_ALIGN 16 /* bytes */
#define XHCI_TRANS_RING_SEG_ALIGN 16 /* bytes */
#define XHCI_CMD_RING_SEG_ALIGN 64 /* bytes */
#define XHCI_EVENT_RING_SEG_ALIGN 64 /* bytes */
#define XHCI_SCRATCH_BUF_ARRAY_ALIGN 64 /* bytes */
#define XHCI_SCRATCH_BUFFER_ALIGN USB_PAGE_SIZE
#define XHCI_TRB_ALIGN 16 /* bytes */
#define XHCI_TD_ALIGN 64 /* bytes */
#define XHCI_PAGE_SIZE 4096 /* bytes */
struct xhci_slot_ctx {
volatile uint32_t dwSctx0;
#define XHCI_SCTX_0_ROUTE_SET(x) ((x) & 0xFFFFF)
#define XHCI_SCTX_0_ROUTE_GET(x) ((x) & 0xFFFFF)
#define XHCI_SCTX_0_SPEED_SET(x) (((x) & 0xF) << 20)
#define XHCI_SCTX_0_SPEED_GET(x) (((x) >> 20) & 0xF)
#define XHCI_SCTX_0_MTT_SET(x) (((x) & 0x1) << 25)
#define XHCI_SCTX_0_MTT_GET(x) (((x) >> 25) & 0x1)
#define XHCI_SCTX_0_HUB_SET(x) (((x) & 0x1) << 26)
#define XHCI_SCTX_0_HUB_GET(x) (((x) >> 26) & 0x1)
#define XHCI_SCTX_0_CTX_NUM_SET(x) (((x) & 0x1F) << 27)
#define XHCI_SCTX_0_CTX_NUM_GET(x) (((x) >> 27) & 0x1F)
volatile uint32_t dwSctx1;
#define XHCI_SCTX_1_MAX_EL_SET(x) ((x) & 0xFFFF)
#define XHCI_SCTX_1_MAX_EL_GET(x) ((x) & 0xFFFF)
#define XHCI_SCTX_1_RH_PORT_SET(x) (((x) & 0xFF) << 16)
#define XHCI_SCTX_1_RH_PORT_GET(x) (((x) >> 16) & 0xFF)
#define XHCI_SCTX_1_NUM_PORTS_SET(x) (((x) & 0xFF) << 24)
#define XHCI_SCTX_1_NUM_PORTS_GET(x) (((x) >> 24) & 0xFF)
volatile uint32_t dwSctx2;
#define XHCI_SCTX_2_TT_HUB_SID_SET(x) ((x) & 0xFF)
#define XHCI_SCTX_2_TT_HUB_SID_GET(x) ((x) & 0xFF)
#define XHCI_SCTX_2_TT_PORT_NUM_SET(x) (((x) & 0xFF) << 8)
#define XHCI_SCTX_2_TT_PORT_NUM_GET(x) (((x) >> 8) & 0xFF)
#define XHCI_SCTX_2_TT_THINK_TIME_SET(x) (((x) & 0x3) << 16)
#define XHCI_SCTX_2_TT_THINK_TIME_GET(x) (((x) >> 16) & 0x3)
#define XHCI_SCTX_2_IRQ_TARGET_SET(x) (((x) & 0x3FF) << 22)
#define XHCI_SCTX_2_IRQ_TARGET_GET(x) (((x) >> 22) & 0x3FF)
volatile uint32_t dwSctx3;
#define XHCI_SCTX_3_DEV_ADDR_SET(x) ((x) & 0xFF)
#define XHCI_SCTX_3_DEV_ADDR_GET(x) ((x) & 0xFF)
#define XHCI_SCTX_3_SLOT_STATE_SET(x) (((x) & 0x1F) << 27)
#define XHCI_SCTX_3_SLOT_STATE_GET(x) (((x) >> 27) & 0x1F)
volatile uint32_t dwSctx4;
volatile uint32_t dwSctx5;
volatile uint32_t dwSctx6;
volatile uint32_t dwSctx7;
};
struct xhci_endp_ctx {
volatile uint32_t dwEpCtx0;
#define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7)
#define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7)
#define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8)
#define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3)
#define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10)
#define XHCI_EPCTX_0_MAXP_STREAMS_GET(x) (((x) >> 10) & 0x1F)
#define XHCI_EPCTX_0_LSA_SET(x) (((x) & 0x1) << 15)
#define XHCI_EPCTX_0_LSA_GET(x) (((x) >> 15) & 0x1)
#define XHCI_EPCTX_0_IVAL_SET(x) (((x) & 0xFF) << 16)
#define XHCI_EPCTX_0_IVAL_GET(x) (((x) >> 16) & 0xFF)
volatile uint32_t dwEpCtx1;
#define XHCI_EPCTX_1_CERR_SET(x) (((x) & 0x3) << 1)
#define XHCI_EPCTX_1_CERR_GET(x) (((x) >> 1) & 0x3)
#define XHCI_EPCTX_1_EPTYPE_SET(x) (((x) & 0x7) << 3)
#define XHCI_EPCTX_1_EPTYPE_GET(x) (((x) >> 3) & 0x7)
#define XHCI_EPCTX_1_HID_SET(x) (((x) & 0x1) << 7)
#define XHCI_EPCTX_1_HID_GET(x) (((x) >> 7) & 0x1)
#define XHCI_EPCTX_1_MAXB_SET(x) (((x) & 0xFF) << 8)
#define XHCI_EPCTX_1_MAXB_GET(x) (((x) >> 8) & 0xFF)
#define XHCI_EPCTX_1_MAXP_SIZE_SET(x) (((x) & 0xFFFF) << 16)
#define XHCI_EPCTX_1_MAXP_SIZE_GET(x) (((x) >> 16) & 0xFFFF)
volatile uint64_t qwEpCtx2;
#define XHCI_EPCTX_2_DCS_SET(x) ((x) & 0x1)
#define XHCI_EPCTX_2_DCS_GET(x) ((x) & 0x1)
#define XHCI_EPCTX_2_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
volatile uint32_t dwEpCtx4;
#define XHCI_EPCTX_4_AVG_TRB_LEN_SET(x) ((x) & 0xFFFF)
#define XHCI_EPCTX_4_AVG_TRB_LEN_GET(x) ((x) & 0xFFFF)
#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x) (((x) & 0xFFFF) << 16)
#define XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_GET(x) (((x) >> 16) & 0xFFFF)
volatile uint32_t dwEpCtx5;
volatile uint32_t dwEpCtx6;
volatile uint32_t dwEpCtx7;
};
struct xhci_input_ctx {
#define XHCI_INCTX_NON_CTRL_MASK 0xFFFFFFFCU
volatile uint32_t dwInCtx0;
#define XHCI_INCTX_0_DROP_MASK(n) (1U << (n))
volatile uint32_t dwInCtx1;
#define XHCI_INCTX_1_ADD_MASK(n) (1U << (n))
volatile uint32_t dwInCtx2;
volatile uint32_t dwInCtx3;
volatile uint32_t dwInCtx4;
volatile uint32_t dwInCtx5;
volatile uint32_t dwInCtx6;
volatile uint32_t dwInCtx7;
};
struct xhci_input_dev_ctx {
struct xhci_input_ctx ctx_input;
union {
struct xhci_slot_ctx u_slot;
struct xhci_endp_ctx u_ep[XHCI_MAX_ENDPOINTS];
} ctx_dev_slep;
};
struct xhci_dev_ctx {
union {
struct xhci_slot_ctx u_slot;
struct xhci_endp_ctx u_ep[XHCI_MAX_ENDPOINTS];
} ctx_dev_slep;
} __aligned(XHCI_DEV_CTX_ALIGN);
#define ctx_slot ctx_dev_slep.u_slot
#define ctx_ep ctx_dev_slep.u_ep
struct xhci_stream_ctx {
volatile uint64_t qwSctx0;
#define XHCI_SCTX_0_DCS_GET(x) ((x) & 0x1)
#define XHCI_SCTX_0_DCS_SET(x) ((x) & 0x1)
#define XHCI_SCTX_0_SCT_SET(x) (((x) & 0x7) << 1)
#define XHCI_SCTX_0_SCT_GET(x) (((x) >> 1) & 0x7)
#define XHCI_SCTX_0_SCT_SEC_TR_RING 0x0
#define XHCI_SCTX_0_SCT_PRIM_TR_RING 0x1
#define XHCI_SCTX_0_SCT_PRIM_SSA_8 0x2
#define XHCI_SCTX_0_SCT_PRIM_SSA_16 0x3
#define XHCI_SCTX_0_SCT_PRIM_SSA_32 0x4
#define XHCI_SCTX_0_SCT_PRIM_SSA_64 0x5
#define XHCI_SCTX_0_SCT_PRIM_SSA_128 0x6
#define XHCI_SCTX_0_SCT_PRIM_SSA_256 0x7
#define XHCI_SCTX_0_TR_DQ_PTR_MASK 0xFFFFFFFFFFFFFFF0U
volatile uint32_t dwSctx2;
volatile uint32_t dwSctx3;
};
struct xhci_trb {
volatile uint64_t qwTrb0;
#define XHCI_TRB_0_DIR_IN_MASK (0x80ULL << 0)
#define XHCI_TRB_0_WLENGTH_MASK (0xFFFFULL << 48)
volatile uint32_t dwTrb2;
#define XHCI_TRB_2_ERROR_GET(x) (((x) >> 24) & 0xFF)
#define XHCI_TRB_2_ERROR_SET(x) (((x) & 0xFF) << 24)
#define XHCI_TRB_2_TDSZ_GET(x) (((x) >> 17) & 0x1F)
#define XHCI_TRB_2_TDSZ_SET(x) (((x) & 0x1F) << 17)
#define XHCI_TRB_2_REM_GET(x) ((x) & 0xFFFFFF)
#define XHCI_TRB_2_REM_SET(x) ((x) & 0xFFFFFF)
#define XHCI_TRB_2_BYTES_GET(x) ((x) & 0x1FFFF)
#define XHCI_TRB_2_BYTES_SET(x) ((x) & 0x1FFFF)
#define XHCI_TRB_2_IRQ_GET(x) (((x) >> 22) & 0x3FF)
#define XHCI_TRB_2_IRQ_SET(x) (((x) & 0x3FF) << 22)
#define XHCI_TRB_2_STREAM_GET(x) (((x) >> 16) & 0xFFFF)
#define XHCI_TRB_2_STREAM_SET(x) (((x) & 0xFFFF) << 16)
volatile uint32_t dwTrb3;
#define XHCI_TRB_3_TYPE_GET(x) (((x) >> 10) & 0x3F)
#define XHCI_TRB_3_TYPE_SET(x) (((x) & 0x3F) << 10)
#define XHCI_TRB_3_CYCLE_BIT (1U << 0)
#define XHCI_TRB_3_TC_BIT (1U << 1) /* command ring only */
#define XHCI_TRB_3_ENT_BIT (1U << 1) /* transfer ring only */
#define XHCI_TRB_3_ISP_BIT (1U << 2)
#define XHCI_TRB_3_ED_BIT (1U << 2)
#define XHCI_TRB_3_NSNOOP_BIT (1U << 3)
#define XHCI_TRB_3_CHAIN_BIT (1U << 4)
#define XHCI_TRB_3_IOC_BIT (1U << 5)
#define XHCI_TRB_3_IDT_BIT (1U << 6)
#define XHCI_TRB_3_TBC_GET(x) (((x) >> 7) & 3)
#define XHCI_TRB_3_TBC_SET(x) (((x) & 3) << 7)
#define XHCI_TRB_3_BEI_BIT (1U << 9)
#define XHCI_TRB_3_DCEP_BIT (1U << 9)
#define XHCI_TRB_3_PRSV_BIT (1U << 9)
#define XHCI_TRB_3_BSR_BIT (1U << 9)
#define XHCI_TRB_3_TRT_MASK (3U << 16)
#define XHCI_TRB_3_TRT_NONE (0U << 16)
#define XHCI_TRB_3_TRT_OUT (2U << 16)
#define XHCI_TRB_3_TRT_IN (3U << 16)
#define XHCI_TRB_3_DIR_IN (1U << 16)
#define XHCI_TRB_3_TLBPC_GET(x) (((x) >> 16) & 0xF)
#define XHCI_TRB_3_TLBPC_SET(x) (((x) & 0xF) << 16)
#define XHCI_TRB_3_EP_GET(x) (((x) >> 16) & 0x1F)
#define XHCI_TRB_3_EP_SET(x) (((x) & 0x1F) << 16)
#define XHCI_TRB_3_FRID_GET(x) (((x) >> 20) & 0x7FF)
#define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20)
#define XHCI_TRB_3_ISO_SIA_BIT (1U << 31)
#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23)
#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF)
#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24)
/* Commands */
#define XHCI_TRB_TYPE_RESERVED 0x00
#define XHCI_TRB_TYPE_NORMAL 0x01
#define XHCI_TRB_TYPE_SETUP_STAGE 0x02
#define XHCI_TRB_TYPE_DATA_STAGE 0x03
#define XHCI_TRB_TYPE_STATUS_STAGE 0x04
#define XHCI_TRB_TYPE_ISOCH 0x05
#define XHCI_TRB_TYPE_LINK 0x06
#define XHCI_TRB_TYPE_EVENT_DATA 0x07
#define XHCI_TRB_TYPE_NOOP 0x08
#define XHCI_TRB_TYPE_ENABLE_SLOT 0x09
#define XHCI_TRB_TYPE_DISABLE_SLOT 0x0A
#define XHCI_TRB_TYPE_ADDRESS_DEVICE 0x0B
#define XHCI_TRB_TYPE_CONFIGURE_EP 0x0C
#define XHCI_TRB_TYPE_EVALUATE_CTX 0x0D
#define XHCI_TRB_TYPE_RESET_EP 0x0E
#define XHCI_TRB_TYPE_STOP_EP 0x0F
#define XHCI_TRB_TYPE_SET_TR_DEQUEUE 0x10
#define XHCI_TRB_TYPE_RESET_DEVICE 0x11
#define XHCI_TRB_TYPE_FORCE_EVENT 0x12
#define XHCI_TRB_TYPE_NEGOTIATE_BW 0x13
#define XHCI_TRB_TYPE_SET_LATENCY_TOL 0x14
#define XHCI_TRB_TYPE_GET_PORT_BW 0x15
#define XHCI_TRB_TYPE_FORCE_HEADER 0x16
#define XHCI_TRB_TYPE_NOOP_CMD 0x17
/* Events */
#define XHCI_TRB_EVENT_TRANSFER 0x20
#define XHCI_TRB_EVENT_CMD_COMPLETE 0x21
#define XHCI_TRB_EVENT_PORT_STS_CHANGE 0x22
#define XHCI_TRB_EVENT_BW_REQUEST 0x23
#define XHCI_TRB_EVENT_DOORBELL 0x24
#define XHCI_TRB_EVENT_HOST_CTRL 0x25
#define XHCI_TRB_EVENT_DEVICE_NOTIFY 0x26
#define XHCI_TRB_EVENT_MFINDEX_WRAP 0x27
/* Error codes */
#define XHCI_TRB_ERROR_INVALID 0x00
#define XHCI_TRB_ERROR_SUCCESS 0x01
#define XHCI_TRB_ERROR_DATA_BUF 0x02
#define XHCI_TRB_ERROR_BABBLE 0x03
#define XHCI_TRB_ERROR_XACT 0x04
#define XHCI_TRB_ERROR_TRB 0x05
#define XHCI_TRB_ERROR_STALL 0x06
#define XHCI_TRB_ERROR_RESOURCE 0x07
#define XHCI_TRB_ERROR_BANDWIDTH 0x08
#define XHCI_TRB_ERROR_NO_SLOTS 0x09
#define XHCI_TRB_ERROR_STREAM_TYPE 0x0A
#define XHCI_TRB_ERROR_SLOT_NOT_ON 0x0B
#define XHCI_TRB_ERROR_ENDP_NOT_ON 0x0C
#define XHCI_TRB_ERROR_SHORT_PKT 0x0D
#define XHCI_TRB_ERROR_RING_UNDERRUN 0x0E
#define XHCI_TRB_ERROR_RING_OVERRUN 0x0F
#define XHCI_TRB_ERROR_VF_RING_FULL 0x10
#define XHCI_TRB_ERROR_PARAMETER 0x11
#define XHCI_TRB_ERROR_BW_OVERRUN 0x12
#define XHCI_TRB_ERROR_CONTEXT_STATE 0x13
#define XHCI_TRB_ERROR_NO_PING_RESP 0x14
#define XHCI_TRB_ERROR_EV_RING_FULL 0x15
#define XHCI_TRB_ERROR_INCOMPAT_DEV 0x16
#define XHCI_TRB_ERROR_MISSED_SERVICE 0x17
#define XHCI_TRB_ERROR_CMD_RING_STOP 0x18
#define XHCI_TRB_ERROR_CMD_ABORTED 0x19
#define XHCI_TRB_ERROR_STOPPED 0x1A
#define XHCI_TRB_ERROR_LENGTH 0x1B
#define XHCI_TRB_ERROR_BAD_MELAT 0x1D
#define XHCI_TRB_ERROR_ISOC_OVERRUN 0x1F
#define XHCI_TRB_ERROR_EVENT_LOST 0x20
#define XHCI_TRB_ERROR_UNDEFINED 0x21
#define XHCI_TRB_ERROR_INVALID_SID 0x22
#define XHCI_TRB_ERROR_SEC_BW 0x23
#define XHCI_TRB_ERROR_SPLIT_XACT 0x24
} __aligned(4);
struct xhci_dev_endpoint_trbs {
struct xhci_trb trb[(XHCI_MAX_STREAMS *
XHCI_MAX_TRANSFERS) + XHCI_MAX_STREAMS];
};
struct xhci_event_ring_seg {
volatile uint64_t qwEvrsTablePtr;
volatile uint32_t dwEvrsTableSize;
volatile uint32_t dwEvrsReserved;
};
#endif /* _PCI_XHCI_H_ */

481
usr.sbin/bhyve/ps2kbd.c Normal file
View File

@ -0,0 +1,481 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2015 Nahanni Systems Inc.
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include <pthread_np.h>
#include "atkbdc.h"
#include "console.h"
/* keyboard device commands */
#define PS2KC_RESET_DEV 0xff
#define PS2KC_DISABLE 0xf5
#define PS2KC_ENABLE 0xf4
#define PS2KC_SET_TYPEMATIC 0xf3
#define PS2KC_SEND_DEV_ID 0xf2
#define PS2KC_SET_SCANCODE_SET 0xf0
#define PS2KC_ECHO 0xee
#define PS2KC_SET_LEDS 0xed
#define PS2KC_BAT_SUCCESS 0xaa
#define PS2KC_ACK 0xfa
#define PS2KBD_FIFOSZ 16
struct fifo {
uint8_t buf[PS2KBD_FIFOSZ];
int rindex; /* index to read from */
int windex; /* index to write to */
int num; /* number of bytes in the fifo */
int size; /* size of the fifo */
};
struct ps2kbd_softc {
struct atkbdc_softc *atkbdc_sc;
pthread_mutex_t mtx;
bool enabled;
struct fifo fifo;
uint8_t curcmd; /* current command for next byte */
};
static void
fifo_init(struct ps2kbd_softc *sc)
{
struct fifo *fifo;
fifo = &sc->fifo;
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static void
fifo_reset(struct ps2kbd_softc *sc)
{
struct fifo *fifo;
fifo = &sc->fifo;
bzero(fifo, sizeof(struct fifo));
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static int
fifo_available(struct ps2kbd_softc *sc)
{
struct fifo *fifo;
fifo = &sc->fifo;
return (fifo->num < fifo->size);
}
static void
fifo_put(struct ps2kbd_softc *sc, uint8_t val)
{
struct fifo *fifo;
fifo = &sc->fifo;
if (fifo->num < fifo->size) {
fifo->buf[fifo->windex] = val;
fifo->windex = (fifo->windex + 1) % fifo->size;
fifo->num++;
}
}
static int
fifo_get(struct ps2kbd_softc *sc, uint8_t *val)
{
struct fifo *fifo;
fifo = &sc->fifo;
if (fifo->num > 0) {
*val = fifo->buf[fifo->rindex];
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->num--;
return (0);
}
return (-1);
}
int
ps2kbd_read(struct ps2kbd_softc *sc, uint8_t *val)
{
int retval;
pthread_mutex_lock(&sc->mtx);
retval = fifo_get(sc, val);
pthread_mutex_unlock(&sc->mtx);
return (retval);
}
void
ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val)
{
pthread_mutex_lock(&sc->mtx);
if (sc->curcmd) {
switch (sc->curcmd) {
case PS2KC_SET_TYPEMATIC:
fifo_put(sc, PS2KC_ACK);
break;
case PS2KC_SET_SCANCODE_SET:
fifo_put(sc, PS2KC_ACK);
break;
case PS2KC_SET_LEDS:
fifo_put(sc, PS2KC_ACK);
break;
default:
fprintf(stderr, "Unhandled ps2 keyboard current "
"command byte 0x%02x\n", val);
break;
}
sc->curcmd = 0;
} else {
switch (val) {
case 0x00:
fifo_put(sc, PS2KC_ACK);
break;
case PS2KC_RESET_DEV:
fifo_reset(sc);
fifo_put(sc, PS2KC_ACK);
fifo_put(sc, PS2KC_BAT_SUCCESS);
break;
case PS2KC_DISABLE:
sc->enabled = false;
fifo_put(sc, PS2KC_ACK);
break;
case PS2KC_ENABLE:
sc->enabled = true;
fifo_reset(sc);
fifo_put(sc, PS2KC_ACK);
break;
case PS2KC_SET_TYPEMATIC:
sc->curcmd = val;
fifo_put(sc, PS2KC_ACK);
break;
case PS2KC_SEND_DEV_ID:
fifo_put(sc, PS2KC_ACK);
fifo_put(sc, 0xab);
fifo_put(sc, 0x83);
break;
case PS2KC_SET_SCANCODE_SET:
sc->curcmd = val;
fifo_put(sc, PS2KC_ACK);
break;
case PS2KC_ECHO:
fifo_put(sc, PS2KC_ECHO);
break;
case PS2KC_SET_LEDS:
sc->curcmd = val;
fifo_put(sc, PS2KC_ACK);
break;
default:
fprintf(stderr, "Unhandled ps2 keyboard command "
"0x%02x\n", val);
break;
}
}
pthread_mutex_unlock(&sc->mtx);
}
/*
* Translate keysym to type 2 scancode and insert into keyboard buffer.
*/
static void
ps2kbd_keysym_queue(struct ps2kbd_softc *sc,
int down, uint32_t keysym)
{
/* ASCII to type 2 scancode lookup table */
const uint8_t translation[128] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x29, 0x16, 0x52, 0x26, 0x25, 0x2e, 0x3d, 0x52,
0x46, 0x45, 0x3e, 0x55, 0x41, 0x4e, 0x49, 0x4a,
0x45, 0x16, 0x1e, 0x26, 0x25, 0x2e, 0x36, 0x3d,
0x3e, 0x46, 0x4c, 0x4c, 0x41, 0x55, 0x49, 0x4a,
0x1e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x36, 0x4e,
0x0e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34,
0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44,
0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d,
0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x0e, 0x00,
};
assert(pthread_mutex_isowned_np(&sc->mtx));
switch (keysym) {
case 0x0 ... 0x7f:
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, translation[keysym]);
break;
case 0xff08: /* Back space */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x66);
break;
case 0xff09: /* Tab */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x0d);
break;
case 0xff0d: /* Return */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x5a);
break;
case 0xff1b: /* Escape */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x76);
break;
case 0xff50: /* Home */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x6c);
break;
case 0xff51: /* Left arrow */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x6b);
break;
case 0xff52: /* Up arrow */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x75);
break;
case 0xff53: /* Right arrow */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x74);
break;
case 0xff54: /* Down arrow */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x72);
break;
case 0xff55: /* PgUp */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x7d);
break;
case 0xff56: /* PgDwn */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x7a);
break;
case 0xff57: /* End */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x69);
break;
case 0xff63: /* Ins */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x70);
break;
case 0xff8d: /* Keypad Enter */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x5a);
break;
case 0xffe1: /* Left shift */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x12);
break;
case 0xffe2: /* Right shift */
/* XXX */
break;
case 0xffe3: /* Left control */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x14);
break;
case 0xffe4: /* Right control */
/* XXX */
break;
case 0xffe7: /* Left meta */
/* XXX */
break;
case 0xffe8: /* Right meta */
/* XXX */
break;
case 0xffe9: /* Left alt */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x11);
break;
case 0xffea: /* Right alt */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x11);
break;
case 0xffeb: /* Left Windows */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x1f);
break;
case 0xffec: /* Right Windows */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x27);
break;
case 0xffbe: /* F1 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x05);
break;
case 0xffbf: /* F2 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x06);
break;
case 0xffc0: /* F3 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x04);
break;
case 0xffc1: /* F4 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x0C);
break;
case 0xffc2: /* F5 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x03);
break;
case 0xffc3: /* F6 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x0B);
break;
case 0xffc4: /* F7 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x83);
break;
case 0xffc5: /* F8 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x0A);
break;
case 0xffc6: /* F9 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x01);
break;
case 0xffc7: /* F10 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x09);
break;
case 0xffc8: /* F11 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x78);
break;
case 0xffc9: /* F12 */
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x07);
break;
case 0xffff: /* Del */
fifo_put(sc, 0xe0);
if (!down)
fifo_put(sc, 0xf0);
fifo_put(sc, 0x71);
break;
default:
fprintf(stderr, "Unhandled ps2 keyboard keysym 0x%x\n",
keysym);
break;
}
}
static void
ps2kbd_event(int down, uint32_t keysym, void *arg)
{
struct ps2kbd_softc *sc = arg;
int fifo_full;
pthread_mutex_lock(&sc->mtx);
if (!sc->enabled) {
pthread_mutex_unlock(&sc->mtx);
return;
}
fifo_full = sc->fifo.num == PS2KBD_FIFOSZ;
ps2kbd_keysym_queue(sc, down, keysym);
pthread_mutex_unlock(&sc->mtx);
if (!fifo_full)
atkbdc_event(sc->atkbdc_sc, 1);
}
struct ps2kbd_softc *
ps2kbd_init(struct atkbdc_softc *atkbdc_sc)
{
struct ps2kbd_softc *sc;
sc = calloc(1, sizeof (struct ps2kbd_softc));
pthread_mutex_init(&sc->mtx, NULL);
fifo_init(sc);
sc->atkbdc_sc = atkbdc_sc;
console_kbd_register(ps2kbd_event, sc, 1);
return (sc);
}

39
usr.sbin/bhyve/ps2kbd.h Normal file
View File

@ -0,0 +1,39 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _PS2KBD_H_
#define _PS2KBD_H_
struct atkbdc_softc;
struct ps2kbd_softc *ps2kbd_init(struct atkbdc_softc *sc);
int ps2kbd_read(struct ps2kbd_softc *sc, uint8_t *val);
void ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val);
#endif /* _PS2KBD_H_ */

405
usr.sbin/bhyve/ps2mouse.c Normal file
View File

@ -0,0 +1,405 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2015 Nahanni Systems Inc.
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include <pthread_np.h>
#include "atkbdc.h"
#include "console.h"
/* mouse device commands */
#define PS2MC_RESET_DEV 0xff
#define PS2MC_SET_DEFAULTS 0xf6
#define PS2MC_DISABLE 0xf5
#define PS2MC_ENABLE 0xf4
#define PS2MC_SET_SAMPLING_RATE 0xf3
#define PS2MC_SEND_DEV_ID 0xf2
#define PS2MC_SET_REMOTE_MODE 0xf0
#define PS2MC_SEND_DEV_DATA 0xeb
#define PS2MC_SET_STREAM_MODE 0xea
#define PS2MC_SEND_DEV_STATUS 0xe9
#define PS2MC_SET_RESOLUTION 0xe8
#define PS2MC_SET_SCALING1 0xe7
#define PS2MC_SET_SCALING2 0xe6
#define PS2MC_BAT_SUCCESS 0xaa
#define PS2MC_ACK 0xfa
/* mouse device id */
#define PS2MOUSE_DEV_ID 0x0
/* mouse status bits */
#define PS2M_STS_REMOTE_MODE 0x40
#define PS2M_STS_ENABLE_DEV 0x20
#define PS2M_STS_SCALING_21 0x10
#define PS2M_STS_MID_BUTTON 0x04
#define PS2M_STS_RIGHT_BUTTON 0x02
#define PS2M_STS_LEFT_BUTTON 0x01
#define PS2MOUSE_FIFOSZ 16
struct fifo {
uint8_t buf[PS2MOUSE_FIFOSZ];
int rindex; /* index to read from */
int windex; /* index to write to */
int num; /* number of bytes in the fifo */
int size; /* size of the fifo */
};
struct ps2mouse_softc {
struct atkbdc_softc *atkbdc_sc;
pthread_mutex_t mtx;
uint8_t status;
uint8_t resolution;
uint8_t sampling_rate;
int ctrlenable;
struct fifo fifo;
uint8_t curcmd; /* current command for next byte */
int cur_x, cur_y;
int delta_x, delta_y;
};
static void
fifo_init(struct ps2mouse_softc *sc)
{
struct fifo *fifo;
fifo = &sc->fifo;
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static void
fifo_reset(struct ps2mouse_softc *sc)
{
struct fifo *fifo;
fifo = &sc->fifo;
bzero(fifo, sizeof(struct fifo));
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static void
fifo_put(struct ps2mouse_softc *sc, uint8_t val)
{
struct fifo *fifo;
fifo = &sc->fifo;
if (fifo->num < fifo->size) {
fifo->buf[fifo->windex] = val;
fifo->windex = (fifo->windex + 1) % fifo->size;
fifo->num++;
}
}
static int
fifo_get(struct ps2mouse_softc *sc, uint8_t *val)
{
struct fifo *fifo;
fifo = &sc->fifo;
if (fifo->num > 0) {
*val = fifo->buf[fifo->rindex];
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->num--;
return (0);
}
return (-1);
}
static void
movement_reset(struct ps2mouse_softc *sc)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
sc->delta_x = 0;
sc->delta_y = 0;
}
static void
movement_update(struct ps2mouse_softc *sc, int x, int y)
{
sc->delta_x += x - sc->cur_x;
sc->delta_y += sc->cur_y - y;
sc->cur_x = x;
sc->cur_y = y;
}
static void
movement_get(struct ps2mouse_softc *sc)
{
uint8_t val0, val1, val2;
assert(pthread_mutex_isowned_np(&sc->mtx));
val0 = sc->status & (PS2M_STS_LEFT_BUTTON |
PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
if (sc->delta_x >= 0) {
if (sc->delta_x > 255) {
val0 |= (1 << 6);
val1 = 255;
} else
val1 = sc->delta_x;
} else {
val0 |= (1 << 4);
if (sc->delta_x < -255) {
val0 |= (1 << 6);
val1 = 255;
} else
val1 = sc->delta_x;
}
sc->delta_x = 0;
if (sc->delta_y >= 0) {
if (sc->delta_y > 255) {
val0 |= (1 << 7);
val2 = 255;
} else
val2 = sc->delta_y;
} else {
val0 |= (1 << 5);
if (sc->delta_y < -255) {
val0 |= (1 << 7);
val2 = 255;
} else
val2 = sc->delta_y;
}
sc->delta_y = 0;
if (sc->fifo.num < (sc->fifo.size - 3)) {
fifo_put(sc, val0);
fifo_put(sc, val1);
fifo_put(sc, val2);
}
}
static void
ps2mouse_reset(struct ps2mouse_softc *sc)
{
assert(pthread_mutex_isowned_np(&sc->mtx));
fifo_reset(sc);
movement_reset(sc);
sc->status = PS2M_STS_ENABLE_DEV;
sc->resolution = 4;
sc->sampling_rate = 100;
sc->cur_x = 0;
sc->cur_y = 0;
sc->delta_x = 0;
sc->delta_y = 0;
}
int
ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val)
{
int retval;
pthread_mutex_lock(&sc->mtx);
retval = fifo_get(sc, val);
pthread_mutex_unlock(&sc->mtx);
return (retval);
}
int
ps2mouse_fifocnt(struct ps2mouse_softc *sc)
{
return (sc->fifo.num);
}
void
ps2mouse_toggle(struct ps2mouse_softc *sc, int enable)
{
pthread_mutex_lock(&sc->mtx);
if (enable)
sc->ctrlenable = 1;
else {
sc->ctrlenable = 0;
sc->fifo.rindex = 0;
sc->fifo.windex = 0;
sc->fifo.num = 0;
}
pthread_mutex_unlock(&sc->mtx);
}
void
ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert)
{
pthread_mutex_lock(&sc->mtx);
fifo_reset(sc);
if (sc->curcmd) {
switch (sc->curcmd) {
case PS2MC_SET_SAMPLING_RATE:
sc->sampling_rate = val;
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_SET_RESOLUTION:
sc->resolution = val;
fifo_put(sc, PS2MC_ACK);
break;
default:
fprintf(stderr, "Unhandled ps2 mouse current "
"command byte 0x%02x\n", val);
break;
}
sc->curcmd = 0;
} else if (insert) {
fifo_put(sc, val);
} else {
switch (val) {
case 0x00:
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_RESET_DEV:
ps2mouse_reset(sc);
fifo_put(sc, PS2MC_ACK);
fifo_put(sc, PS2MC_BAT_SUCCESS);
fifo_put(sc, PS2MOUSE_DEV_ID);
break;
case PS2MC_SET_DEFAULTS:
ps2mouse_reset(sc);
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_DISABLE:
fifo_reset(sc);
sc->status &= ~PS2M_STS_ENABLE_DEV;
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_ENABLE:
fifo_reset(sc);
sc->status |= PS2M_STS_ENABLE_DEV;
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_SET_SAMPLING_RATE:
sc->curcmd = val;
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_SEND_DEV_ID:
fifo_put(sc, PS2MC_ACK);
fifo_put(sc, PS2MOUSE_DEV_ID);
break;
case PS2MC_SET_REMOTE_MODE:
sc->status |= PS2M_STS_REMOTE_MODE;
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_SEND_DEV_DATA:
fifo_put(sc, PS2MC_ACK);
movement_get(sc);
break;
case PS2MC_SET_STREAM_MODE:
sc->status &= ~PS2M_STS_REMOTE_MODE;
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_SEND_DEV_STATUS:
fifo_put(sc, PS2MC_ACK);
fifo_put(sc, sc->status);
fifo_put(sc, sc->resolution);
fifo_put(sc, sc->sampling_rate);
break;
case PS2MC_SET_RESOLUTION:
sc->curcmd = val;
fifo_put(sc, PS2MC_ACK);
break;
case PS2MC_SET_SCALING1:
case PS2MC_SET_SCALING2:
fifo_put(sc, PS2MC_ACK);
break;
default:
fifo_put(sc, PS2MC_ACK);
fprintf(stderr, "Unhandled ps2 mouse command "
"0x%02x\n", val);
break;
}
}
pthread_mutex_unlock(&sc->mtx);
}
static void
ps2mouse_event(uint8_t button, int x, int y, void *arg)
{
struct ps2mouse_softc *sc = arg;
pthread_mutex_lock(&sc->mtx);
movement_update(sc, x, y);
sc->status &= ~(PS2M_STS_LEFT_BUTTON |
PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
if (button & (1 << 0))
sc->status |= PS2M_STS_LEFT_BUTTON;
if (button & (1 << 1))
sc->status |= PS2M_STS_MID_BUTTON;
if (button & (1 << 2))
sc->status |= PS2M_STS_RIGHT_BUTTON;
if ((sc->status & PS2M_STS_ENABLE_DEV) == 0 || !sc->ctrlenable) {
/* no data reporting */
pthread_mutex_unlock(&sc->mtx);
return;
}
movement_get(sc);
pthread_mutex_unlock(&sc->mtx);
if (sc->fifo.num > 0)
atkbdc_event(sc->atkbdc_sc, 0);
}
struct ps2mouse_softc *
ps2mouse_init(struct atkbdc_softc *atkbdc_sc)
{
struct ps2mouse_softc *sc;
sc = calloc(1, sizeof (struct ps2mouse_softc));
pthread_mutex_init(&sc->mtx, NULL);
fifo_init(sc);
sc->atkbdc_sc = atkbdc_sc;
pthread_mutex_lock(&sc->mtx);
ps2mouse_reset(sc);
pthread_mutex_unlock(&sc->mtx);
console_ptr_register(ps2mouse_event, sc, 1);
return (sc);
}

41
usr.sbin/bhyve/ps2mouse.h Normal file
View File

@ -0,0 +1,41 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _PS2MOUSE_H_
#define _PS2MOUSE_H_
struct atkbdc_softc;
struct ps2mouse_softc *ps2mouse_init(struct atkbdc_softc *sc);
int ps2mouse_read(struct ps2mouse_softc *sc, uint8_t *val);
void ps2mouse_write(struct ps2mouse_softc *sc, uint8_t val, int insert);
void ps2mouse_toggle(struct ps2mouse_softc *sc, int enable);
int ps2mouse_fifocnt(struct ps2mouse_softc *sc);
#endif /* _PS2MOUSE_H_ */

949
usr.sbin/bhyve/rfb.c Normal file
View File

@ -0,0 +1,949 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* Copyright (c) 2015 Leon Dang
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <pthread.h>
#include <pthread_np.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
#include <cpuid.h>
#include "bhyvegc.h"
#include "console.h"
#include "rfb.h"
#include "sockstream.h"
static int rfb_debug = 0;
#define DPRINTF(params) if (rfb_debug) printf params
#define WPRINTF(params) printf params
struct rfb_softc {
int sfd;
pthread_t tid;
int cfd;
int width, height;
bool enc_raw_ok;
bool enc_zlib_ok;
bool enc_resize_ok;
z_stream zstream;
uint8_t *zbuf;
int zbuflen;
int conn_wait;
int sending;
pthread_mutex_t mtx;
pthread_cond_t cond;
int hw_crc;
uint32_t *crc; /* WxH crc cells */
uint32_t *crc_tmp; /* buffer to store single crc row */
int crc_width, crc_height;
};
struct rfb_pixfmt {
uint8_t bpp;
uint8_t depth;
uint8_t bigendian;
uint8_t truecolor;
uint16_t red_max;
uint16_t green_max;
uint16_t blue_max;
uint8_t red_shift;
uint8_t green_shift;
uint8_t blue_shift;
uint8_t pad[3];
};
struct rfb_srvr_info {
uint16_t width;
uint16_t height;
struct rfb_pixfmt pixfmt;
uint32_t namelen;
};
struct rfb_pixfmt_msg {
uint8_t type;
uint8_t pad[3];
struct rfb_pixfmt pixfmt;
};
#define RFB_ENCODING_RAW 0
#define RFB_ENCODING_ZLIB 6
#define RFB_ENCODING_RESIZE -223
#define RFB_MAX_WIDTH 2000
#define RFB_MAX_HEIGHT 1200
#define RFB_ZLIB_BUFSZ RFB_MAX_WIDTH*RFB_MAX_HEIGHT*4
/* percentage changes to screen before sending the entire screen */
#define RFB_SEND_ALL_THRESH 25
struct rfb_enc_msg {
uint8_t type;
uint8_t pad;
uint16_t numencs;
};
struct rfb_updt_msg {
uint8_t type;
uint8_t incremental;
uint16_t x;
uint16_t y;
uint16_t width;
uint16_t height;
};
struct rfb_key_msg {
uint8_t type;
uint8_t down;
uint16_t pad;
uint32_t code;
};
struct rfb_ptr_msg {
uint8_t type;
uint8_t button;
uint16_t x;
uint16_t y;
};
struct rfb_srvr_updt_msg {
uint8_t type;
uint8_t pad;
uint16_t numrects;
};
struct rfb_srvr_rect_hdr {
uint16_t x;
uint16_t y;
uint16_t width;
uint16_t height;
uint32_t encoding;
};
struct rfb_cuttext_msg {
uint8_t type;
uint8_t padding[3];
uint32_t length;
};
static void
rfb_send_server_init_msg(int cfd)
{
struct bhyvegc_image *gc_image;
struct rfb_srvr_info sinfo;
int len;
gc_image = console_get_image();
sinfo.width = htons(gc_image->width);
sinfo.height = htons(gc_image->height);
sinfo.pixfmt.bpp = 32;
sinfo.pixfmt.depth = 32;
sinfo.pixfmt.bigendian = 0;
sinfo.pixfmt.truecolor = 1;
sinfo.pixfmt.red_max = htons(255);
sinfo.pixfmt.green_max = htons(255);
sinfo.pixfmt.blue_max = htons(255);
sinfo.pixfmt.red_shift = 16;
sinfo.pixfmt.green_shift = 8;
sinfo.pixfmt.blue_shift = 0;
sinfo.namelen = htonl(strlen("bhyve"));
len = stream_write(cfd, &sinfo, sizeof(sinfo));
len = stream_write(cfd, "bhyve", strlen("bhyve"));
}
static void
rfb_send_resize_update_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_srvr_updt_msg supdt_msg;
struct rfb_srvr_rect_hdr srect_hdr;
/* Number of rectangles: 1 */
supdt_msg.type = 0;
supdt_msg.pad = 0;
supdt_msg.numrects = htons(1);
stream_write(cfd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
/* Rectangle header */
srect_hdr.x = htons(0);
srect_hdr.y = htons(0);
srect_hdr.width = htons(rc->width);
srect_hdr.height = htons(rc->height);
srect_hdr.encoding = htonl(RFB_ENCODING_RESIZE);
stream_write(cfd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
}
static void
rfb_recv_set_pixfmt_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_pixfmt_msg pixfmt_msg;
int len;
len = stream_read(cfd, ((void *)&pixfmt_msg)+1, sizeof(pixfmt_msg)-1);
}
static void
rfb_recv_set_encodings_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_enc_msg enc_msg;
int len, i;
uint32_t encoding;
assert((sizeof(enc_msg) - 1) == 3);
len = stream_read(cfd, ((void *)&enc_msg)+1, sizeof(enc_msg)-1);
for (i = 0; i < htons(enc_msg.numencs); i++) {
len = stream_read(cfd, &encoding, sizeof(encoding));
switch (htonl(encoding)) {
case RFB_ENCODING_RAW:
rc->enc_raw_ok = true;
break;
case RFB_ENCODING_ZLIB:
rc->enc_zlib_ok = true;
deflateInit(&rc->zstream, Z_BEST_SPEED);
break;
case RFB_ENCODING_RESIZE:
rc->enc_resize_ok = true;
break;
}
}
}
static void
rfb_resize_update(struct rfb_softc *rc, int fd)
{
struct rfb_srvr_updt_msg supdt_msg;
struct rfb_srvr_rect_hdr srect_hdr;
/* Number of rectangles: 1 */
supdt_msg.type = 0;
supdt_msg.pad = 0;
supdt_msg.numrects = htons(1);
stream_write(fd, &supdt_msg, sizeof(struct rfb_srvr_updt_msg));
/* Rectangle header */
srect_hdr.x = htons(0);
srect_hdr.y = htons(0);
srect_hdr.width = htons(rc->width);
srect_hdr.height = htons(rc->height);
srect_hdr.encoding = htonl(RFB_ENCODING_RESIZE);
stream_write(fd, &srect_hdr, sizeof(struct rfb_srvr_rect_hdr));
}
/*
* Calculate CRC32 using SSE4.2; Intel or AMD Bulldozer+ CPUs only
*/
static __inline uint32_t
fast_crc32(void *buf, int len, uint32_t crcval)
{
uint32_t q = len / sizeof(uint32_t);
uint32_t *p = (uint32_t *)buf;
while (q--) {
asm volatile (
".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
:"=S" (crcval)
:"0" (crcval), "c" (*p)
);
p++;
}
return (crcval);
}
static int
rfb_send_rect(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc,
int x, int y, int w, int h)
{
struct rfb_srvr_updt_msg supdt_msg;
struct rfb_srvr_rect_hdr srect_hdr;
unsigned long zlen;
ssize_t nwrite, total;
int err;
uint32_t *p;
uint8_t *zbufp;
/*
* Send a single rectangle of the given x, y, w h dimensions.
*/
/* Number of rectangles: 1 */
supdt_msg.type = 0;
supdt_msg.pad = 0;
supdt_msg.numrects = htons(1);
nwrite = stream_write(cfd, &supdt_msg,
sizeof(struct rfb_srvr_updt_msg));
if (nwrite <= 0)
return (nwrite);
/* Rectangle header */
srect_hdr.x = htons(x);
srect_hdr.y = htons(y);
srect_hdr.width = htons(w);
srect_hdr.height = htons(h);
h = y + h;
w *= sizeof(uint32_t);
if (rc->enc_zlib_ok) {
zbufp = rc->zbuf;
rc->zstream.total_in = 0;
rc->zstream.total_out = 0;
for (p = &gc->data[y * gc->width + x]; y < h; y++) {
rc->zstream.next_in = (Bytef *)p;
rc->zstream.avail_in = w;
rc->zstream.next_out = (Bytef *)zbufp;
rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16 -
rc->zstream.total_out;
rc->zstream.data_type = Z_BINARY;
/* Compress with zlib */
err = deflate(&rc->zstream, Z_SYNC_FLUSH);
if (err != Z_OK) {
WPRINTF(("zlib[rect] deflate err: %d\n", err));
rc->enc_zlib_ok = false;
deflateEnd(&rc->zstream);
goto doraw;
}
zbufp = rc->zbuf + rc->zstream.total_out;
p += gc->width;
}
srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
nwrite = stream_write(cfd, &srect_hdr,
sizeof(struct rfb_srvr_rect_hdr));
if (nwrite <= 0)
return (nwrite);
zlen = htonl(rc->zstream.total_out);
nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
if (nwrite <= 0)
return (nwrite);
return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
}
doraw:
total = 0;
zbufp = rc->zbuf;
for (p = &gc->data[y * gc->width + x]; y < h; y++) {
memcpy(zbufp, p, w);
zbufp += w;
total += w;
p += gc->width;
}
srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
nwrite = stream_write(cfd, &srect_hdr,
sizeof(struct rfb_srvr_rect_hdr));
if (nwrite <= 0)
return (nwrite);
total = stream_write(cfd, rc->zbuf, total);
return (total);
}
static int
rfb_send_all(struct rfb_softc *rc, int cfd, struct bhyvegc_image *gc)
{
struct rfb_srvr_updt_msg supdt_msg;
struct rfb_srvr_rect_hdr srect_hdr;
ssize_t nwrite;
unsigned long zlen;
int err;
/*
* Send the whole thing
*/
/* Number of rectangles: 1 */
supdt_msg.type = 0;
supdt_msg.pad = 0;
supdt_msg.numrects = htons(1);
nwrite = stream_write(cfd, &supdt_msg,
sizeof(struct rfb_srvr_updt_msg));
if (nwrite <= 0)
return (nwrite);
/* Rectangle header */
srect_hdr.x = 0;
srect_hdr.y = 0;
srect_hdr.width = htons(gc->width);
srect_hdr.height = htons(gc->height);
if (rc->enc_zlib_ok) {
rc->zstream.next_in = (Bytef *)gc->data;
rc->zstream.avail_in = gc->width * gc->height *
sizeof(uint32_t);
rc->zstream.next_out = (Bytef *)rc->zbuf;
rc->zstream.avail_out = RFB_ZLIB_BUFSZ + 16;
rc->zstream.data_type = Z_BINARY;
rc->zstream.total_in = 0;
rc->zstream.total_out = 0;
/* Compress with zlib */
err = deflate(&rc->zstream, Z_SYNC_FLUSH);
if (err != Z_OK) {
WPRINTF(("zlib deflate err: %d\n", err));
rc->enc_zlib_ok = false;
deflateEnd(&rc->zstream);
goto doraw;
}
srect_hdr.encoding = htonl(RFB_ENCODING_ZLIB);
nwrite = stream_write(cfd, &srect_hdr,
sizeof(struct rfb_srvr_rect_hdr));
if (nwrite <= 0)
return (nwrite);
zlen = htonl(rc->zstream.total_out);
nwrite = stream_write(cfd, &zlen, sizeof(uint32_t));
if (nwrite <= 0)
return (nwrite);
return (stream_write(cfd, rc->zbuf, rc->zstream.total_out));
}
doraw:
srect_hdr.encoding = htonl(RFB_ENCODING_RAW);
nwrite = stream_write(cfd, &srect_hdr,
sizeof(struct rfb_srvr_rect_hdr));
if (nwrite <= 0)
return (nwrite);
nwrite = stream_write(cfd, gc->data,
gc->width * gc->height * sizeof(uint32_t));
return (nwrite);
}
#define PIX_PER_CELL 32
#define PIXCELL_SHIFT 5
#define PIXCELL_MASK 0x1F
static int
rfb_send_screen(struct rfb_softc *rc, int cfd, int all)
{
struct bhyvegc_image *gc_image;
ssize_t nwrite;
int x, y;
int celly, cellwidth;
int xcells, ycells;
int w, h;
uint32_t *p;
int rem_x, rem_y; /* remainder for resolutions not x32 pixels ratio */
int retval;
uint32_t *crc_p, *orig_crc;
int changes;
console_refresh();
gc_image = console_get_image();
pthread_mutex_lock(&rc->mtx);
if (rc->sending) {
pthread_mutex_unlock(&rc->mtx);
return (1);
}
rc->sending = 1;
pthread_mutex_unlock(&rc->mtx);
retval = 0;
if (all) {
retval = rfb_send_all(rc, cfd, gc_image);
goto done;
}
/*
* Calculate the checksum for each 32x32 cell. Send each that
* has changed since the last scan.
*/
/* Resolution changed */
rc->crc_width = gc_image->width;
rc->crc_height = gc_image->height;
w = rc->crc_width;
h = rc->crc_height;
xcells = howmany(rc->crc_width, PIX_PER_CELL);
ycells = howmany(rc->crc_height, PIX_PER_CELL);
rem_x = w & PIXCELL_MASK;
rem_y = h & PIXCELL_MASK;
if (!rem_y)
rem_y = PIX_PER_CELL;
p = gc_image->data;
/*
* Go through all cells and calculate crc. If significant number
* of changes, then send entire screen.
* crc_tmp is dual purpose: to store the new crc and to flag as
* a cell that has changed.
*/
crc_p = rc->crc_tmp - xcells;
orig_crc = rc->crc - xcells;
changes = 0;
memset(rc->crc_tmp, 0, sizeof(uint32_t) * xcells * ycells);
for (y = 0; y < h; y++) {
if ((y & PIXCELL_MASK) == 0) {
crc_p += xcells;
orig_crc += xcells;
}
for (x = 0; x < xcells; x++) {
if (rc->hw_crc)
crc_p[x] = fast_crc32(p,
PIX_PER_CELL * sizeof(uint32_t),
crc_p[x]);
else
crc_p[x] = (uint32_t)crc32(crc_p[x],
(Bytef *)p,
PIX_PER_CELL * sizeof(uint32_t));
p += PIX_PER_CELL;
/* check for crc delta if last row in cell */
if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
if (orig_crc[x] != crc_p[x]) {
orig_crc[x] = crc_p[x];
crc_p[x] = 1;
changes++;
} else {
crc_p[x] = 0;
}
}
}
if (rem_x) {
if (rc->hw_crc)
crc_p[x] = fast_crc32(p,
rem_x * sizeof(uint32_t),
crc_p[x]);
else
crc_p[x] = (uint32_t)crc32(crc_p[x],
(Bytef *)p,
rem_x * sizeof(uint32_t));
p += rem_x;
if ((y & PIXCELL_MASK) == PIXCELL_MASK || y == (h-1)) {
if (orig_crc[x] != crc_p[x]) {
orig_crc[x] = crc_p[x];
crc_p[x] = 1;
changes++;
} else {
crc_p[x] = 0;
}
}
}
}
/* If number of changes is > THRESH percent, send the whole screen */
if (((changes * 100) / (xcells * ycells)) >= RFB_SEND_ALL_THRESH) {
retval = rfb_send_all(rc, cfd, gc_image);
goto done;
}
/* Go through all cells, and send only changed ones */
crc_p = rc->crc_tmp;
for (y = 0; y < h; y += PIX_PER_CELL) {
/* previous cell's row */
celly = (y >> PIXCELL_SHIFT);
/* Delta check crc to previous set */
for (x = 0; x < xcells; x++) {
if (*crc_p++ == 0)
continue;
if (x == (xcells - 1) && rem_x > 0)
cellwidth = rem_x;
else
cellwidth = PIX_PER_CELL;
nwrite = rfb_send_rect(rc, cfd,
gc_image,
x * PIX_PER_CELL,
celly * PIX_PER_CELL,
cellwidth,
y + PIX_PER_CELL >= h ? rem_y : PIX_PER_CELL);
if (nwrite <= 0) {
retval = nwrite;
goto done;
}
}
}
retval = 1;
done:
pthread_mutex_lock(&rc->mtx);
rc->sending = 0;
pthread_mutex_unlock(&rc->mtx);
return (retval);
}
static void
rfb_recv_update_msg(struct rfb_softc *rc, int cfd, int discardonly)
{
struct rfb_updt_msg updt_msg;
struct bhyvegc_image *gc_image;
int len;
len = stream_read(cfd, ((void *)&updt_msg) + 1 , sizeof(updt_msg) - 1);
console_refresh();
gc_image = console_get_image();
updt_msg.x = htons(updt_msg.x);
updt_msg.y = htons(updt_msg.y);
updt_msg.width = htons(updt_msg.width);
updt_msg.height = htons(updt_msg.height);
if (updt_msg.width != gc_image->width ||
updt_msg.height != gc_image->height) {
rc->width = gc_image->width;
rc->height = gc_image->height;
if (rc->enc_resize_ok)
rfb_send_resize_update_msg(rc, cfd);
}
if (discardonly)
return;
rfb_send_screen(rc, cfd, 1);
}
static void
rfb_recv_key_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_key_msg key_msg;
int len;
len = stream_read(cfd, ((void *)&key_msg) + 1, sizeof(key_msg) - 1);
console_key_event(key_msg.down, htonl(key_msg.code));
}
static void
rfb_recv_ptr_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_ptr_msg ptr_msg;
int len;
len = stream_read(cfd, ((void *)&ptr_msg) + 1, sizeof(ptr_msg) - 1);
console_ptr_event(ptr_msg.button, htons(ptr_msg.x), htons(ptr_msg.y));
}
static void
rfb_recv_cuttext_msg(struct rfb_softc *rc, int cfd)
{
struct rfb_cuttext_msg ct_msg;
unsigned char buf[32];
int len;
len = stream_read(cfd, ((void *)&ct_msg) + 1, sizeof(ct_msg) - 1);
ct_msg.length = htonl(ct_msg.length);
while (ct_msg.length > 0) {
len = stream_read(cfd, buf, ct_msg.length > sizeof(buf) ?
sizeof(buf) : ct_msg.length);
ct_msg.length -= len;
}
}
static int64_t
timeval_delta(struct timeval *prev, struct timeval *now)
{
int64_t n1, n2;
n1 = now->tv_sec * 1000000 + now->tv_usec;
n2 = prev->tv_sec * 1000000 + prev->tv_usec;
return (n1 - n2);
}
static void *
rfb_wr_thr(void *arg)
{
struct rfb_softc *rc;
fd_set rfds;
struct timeval tv;
struct timeval prev_tv;
int64_t tdiff;
int cfd;
int err;
rc = arg;
cfd = rc->cfd;
prev_tv.tv_sec = 0;
prev_tv.tv_usec = 0;
while (rc->cfd >= 0) {
FD_ZERO(&rfds);
FD_SET(cfd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 10000;
err = select(cfd+1, &rfds, NULL, NULL, &tv);
if (err < 0)
return (NULL);
/* Determine if its time to push screen; ~24hz */
gettimeofday(&tv, NULL);
tdiff = timeval_delta(&prev_tv, &tv);
if (tdiff > 40000) {
prev_tv.tv_sec = tv.tv_sec;
prev_tv.tv_usec = tv.tv_usec;
if (rfb_send_screen(rc, cfd, 0) <= 0) {
return (NULL);
}
} else {
/* sleep */
usleep(40000 - tdiff);
}
}
return (NULL);
}
void
rfb_handle(struct rfb_softc *rc, int cfd)
{
const char *vbuf = "RFB 003.008\n";
unsigned char buf[80];
pthread_t tid;
uint32_t sres;
int len;
rc->cfd = cfd;
/* 1a. Send server version */
stream_write(cfd, vbuf, strlen(vbuf));
/* 1b. Read client version */
len = read(cfd, buf, sizeof(buf));
/* 2a. Send security type 'none' */
buf[0] = 1;
buf[1] = 1; /* none */
stream_write(cfd, buf, 2);
/* 2b. Read agreed security type */
len = stream_read(cfd, buf, 1);
/* 2c. Write back a status of 0 */
sres = 0;
stream_write(cfd, &sres, 4);
/* 3a. Read client shared-flag byte */
len = stream_read(cfd, buf, 1);
/* 4a. Write server-init info */
rfb_send_server_init_msg(cfd);
if (!rc->zbuf) {
rc->zbuf = malloc(RFB_ZLIB_BUFSZ + 16);
assert(rc->zbuf != NULL);
}
rfb_send_screen(rc, cfd, 1);
pthread_create(&tid, NULL, rfb_wr_thr, rc);
pthread_set_name_np(tid, "rfbout");
/* Now read in client requests. 1st byte identifies type */
for (;;) {
len = read(cfd, buf, 1);
if (len <= 0) {
DPRINTF(("rfb client exiting\r\n"));
break;
}
switch (buf[0]) {
case 0:
rfb_recv_set_pixfmt_msg(rc, cfd);
break;
case 2:
rfb_recv_set_encodings_msg(rc, cfd);
break;
case 3:
rfb_recv_update_msg(rc, cfd, 1);
break;
case 4:
rfb_recv_key_msg(rc, cfd);
break;
case 5:
rfb_recv_ptr_msg(rc, cfd);
break;
case 6:
rfb_recv_cuttext_msg(rc, cfd);
break;
default:
WPRINTF(("rfb unknown cli-code %d!\n", buf[0] & 0xff));
goto done;
}
}
done:
rc->cfd = -1;
pthread_join(tid, NULL);
if (rc->enc_zlib_ok)
deflateEnd(&rc->zstream);
}
static void *
rfb_thr(void *arg)
{
struct rfb_softc *rc;
sigset_t set;
int cfd;
rc = arg;
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
perror("pthread_sigmask");
return (NULL);
}
for (;;) {
rc->enc_raw_ok = false;
rc->enc_zlib_ok = false;
rc->enc_resize_ok = false;
cfd = accept(rc->sfd, NULL, NULL);
if (rc->conn_wait) {
pthread_mutex_lock(&rc->mtx);
pthread_cond_signal(&rc->cond);
pthread_mutex_unlock(&rc->mtx);
rc->conn_wait = 0;
}
rfb_handle(rc, cfd);
close(cfd);
}
/* NOTREACHED */
return (NULL);
}
static int
sse42_supported()
{
unsigned int eax, ebx, ecx, edx;
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
return ((ecx & bit_SSE42) != 0);
}
int
rfb_init(char *hostname, int port, int wait)
{
struct rfb_softc *rc;
struct sockaddr_in sin;
int on = 1;
rc = calloc(1, sizeof(struct rfb_softc));
rc->crc = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
sizeof(uint32_t));
rc->crc_tmp = calloc(howmany(RFB_MAX_WIDTH * RFB_MAX_HEIGHT, 32),
sizeof(uint32_t));
rc->crc_width = RFB_MAX_WIDTH;
rc->crc_height = RFB_MAX_HEIGHT;
rc->sfd = socket(AF_INET, SOCK_STREAM, 0);
if (rc->sfd < 0) {
perror("socket");
return (-1);
}
setsockopt(rc->sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if (hostname && strlen(hostname) > 0)
inet_pton(AF_INET, hostname, &(sin.sin_addr));
else
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(rc->sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("bind");
return (-1);
}
if (listen(rc->sfd, 1) < 0) {
perror("listen");
return (-1);
}
rc->hw_crc = sse42_supported();
rc->conn_wait = wait;
if (wait) {
pthread_mutex_init(&rc->mtx, NULL);
pthread_cond_init(&rc->cond, NULL);
}
pthread_create(&rc->tid, NULL, rfb_thr, rc);
pthread_set_name_np(rc->tid, "rfb");
if (wait) {
DPRINTF(("Waiting for rfb client...\n"));
pthread_mutex_lock(&rc->mtx);
pthread_cond_wait(&rc->cond, &rc->mtx);
pthread_mutex_unlock(&rc->mtx);
}
return (0);
}

36
usr.sbin/bhyve/rfb.h Normal file
View File

@ -0,0 +1,36 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _RFB_H_
#define _RFB_H_
#define RFB_PORT 5900
int rfb_init(char *hostname, int port, int wait);
#endif /* _RFB_H_ */

View File

@ -0,0 +1,86 @@
/*-
* Copyright (c) 2015 Nahanni Systems, Inc.
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "sockstream.h"
ssize_t
stream_read(int fd, void *buf, ssize_t nbytes)
{
uint8_t *p;
ssize_t len = 0;
ssize_t n;
p = buf;
while (len < nbytes) {
n = read(fd, p + len, nbytes - len);
if (n == 0)
break;
if (n < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return (n);
}
len += n;
}
return (len);
}
ssize_t
stream_write(int fd, const void *buf, ssize_t nbytes)
{
const uint8_t *p;
ssize_t len = 0;
ssize_t n;
p = buf;
while (len < nbytes) {
n = write(fd, p + len, nbytes - len);
if (n == 0)
break;
if (n < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return (n);
}
len += n;
}
return (len);
}

View File

@ -0,0 +1,33 @@
/*-
* Copyright (c) 2015 Nahanni Systems, Inc.
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <unistd.h>
ssize_t stream_read(int fd, void *buf, ssize_t nbytes);
ssize_t stream_write(int fd, const void *buf, ssize_t nbytes);

76
usr.sbin/bhyve/usb_emul.c Normal file
View File

@ -0,0 +1,76 @@
/*-
* Copyright (c) 2014 Nahanni Systems Inc.
* All rights reserved.
*
* 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, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/queue.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "usb_emul.h"
SET_DECLARE(usb_emu_set, struct usb_devemu);
struct usb_devemu *
usb_emu_finddev(char *name)
{
struct usb_devemu **udpp, *udp;
SET_FOREACH(udpp, usb_emu_set) {
udp = *udpp;
if (!strcmp(udp->ue_emu, name))
return (udp);
}
return (NULL);
}
struct usb_data_xfer_block *
usb_data_xfer_append(struct usb_data_xfer *xfer, void *buf, int blen,
void *hci_data, int ccs)
{
struct usb_data_xfer_block *xb;
if (xfer->ndata >= USB_MAX_XFER_BLOCKS)
return (NULL);
xb = &xfer->data[xfer->tail];
xb->buf = buf;
xb->blen = blen;
xb->hci_data = hci_data;
xb->ccs = ccs;
xb->processed = 0;
xb->bdone = 0;
xfer->ndata++;
xfer->tail = (xfer->tail + 1) % USB_MAX_XFER_BLOCKS;
return (xb);
}

156
usr.sbin/bhyve/usb_emul.h Normal file
View File

@ -0,0 +1,156 @@
/*-
* Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
* All rights reserved.
*
* 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, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _USB_EMUL_H_
#define _USB_EMUL_H_
#include <stdlib.h>
#include <sys/linker_set.h>
#include <pthread.h>
#define USB_MAX_XFER_BLOCKS 8
#define USB_XFER_OUT 0
#define USB_XFER_IN 1
struct usb_hci;
struct usb_device_request;
struct usb_data_xfer;
/* Device emulation handlers */
struct usb_devemu {
char *ue_emu; /* name of device emulation */
int ue_usbver; /* usb version: 2 or 3 */
int ue_usbspeed; /* usb device speed */
/* instance creation */
void *(*ue_init)(struct usb_hci *hci, char *opt);
/* handlers */
int (*ue_request)(void *sc, struct usb_data_xfer *xfer);
int (*ue_data)(void *sc, struct usb_data_xfer *xfer, int dir,
int epctx);
int (*ue_reset)(void *sc);
int (*ue_remove)(void *sc);
int (*ue_stop)(void *sc);
};
#define USB_EMUL_SET(x) DATA_SET(usb_emu_set, x);
/*
* USB device events to notify HCI when state changes
*/
enum hci_usbev {
USBDEV_ATTACH,
USBDEV_RESET,
USBDEV_STOP,
USBDEV_REMOVE,
};
/* usb controller, ie xhci, ehci */
struct usb_hci {
int (*hci_intr)(struct usb_hci *hci, int epctx);
int (*hci_event)(struct usb_hci *hci, enum hci_usbev evid,
void *param);
void *hci_sc; /* private softc for hci */
/* controller managed fields */
int hci_address;
int hci_port;
};
/*
* Each xfer block is mapped to the hci transfer block.
* On input into the device handler, blen is set to the lenght of buf.
* The device handler is to update blen to reflect on the residual size
* of the buffer, i.e. len(buf) - len(consumed).
*/
struct usb_data_xfer_block {
void *buf; /* IN or OUT pointer */
int blen; /* in:len(buf), out:len(remaining) */
int bdone; /* bytes transferred */
uint32_t processed; /* device processed this + errcode */
void *hci_data; /* HCI private reference */
int ccs;
uint32_t streamid;
uint64_t trbnext; /* next TRB guest address */
};
struct usb_data_xfer {
struct usb_data_xfer_block data[USB_MAX_XFER_BLOCKS];
struct usb_device_request *ureq; /* setup ctl request */
int ndata; /* # of data items */
int head;
int tail;
pthread_mutex_t mtx;
};
enum USB_ERRCODE {
USB_ACK,
USB_NAK,
USB_STALL,
USB_NYET,
USB_ERR,
USB_SHORT
};
#define USB_DATA_GET_ERRCODE(x) (x)->processed >> 8
#define USB_DATA_SET_ERRCODE(x,e) do { \
(x)->processed = ((x)->processed & 0xFF) | (e << 8); \
} while (0)
#define USB_DATA_OK(x,i) ((x)->data[(i)].buf != NULL)
#define USB_DATA_XFER_INIT(x) do { \
memset((x), 0, sizeof(*(x))); \
pthread_mutex_init(&((x)->mtx), NULL); \
} while (0)
#define USB_DATA_XFER_RESET(x) do { \
memset((x)->data, 0, sizeof((x)->data)); \
(x)->ndata = 0; \
(x)->head = (x)->tail = 0; \
} while (0)
#define USB_DATA_XFER_LOCK(x) do { \
pthread_mutex_lock(&((x)->mtx)); \
} while (0)
#define USB_DATA_XFER_UNLOCK(x) do { \
pthread_mutex_unlock(&((x)->mtx)); \
} while (0)
struct usb_devemu *usb_emu_finddev(char *name);
struct usb_data_xfer_block *usb_data_xfer_append(struct usb_data_xfer *xfer,
void *buf, int blen, void *hci_data, int ccs);
#endif /* _USB_EMUL_H_ */

803
usr.sbin/bhyve/usb_mouse.c Normal file
View File

@ -0,0 +1,803 @@
/*-
* Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
* All rights reserved.
*
* 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, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/time.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include "usb_emul.h"
#include "console.h"
#include "bhyvegc.h"
static int umouse_debug = 0;
#define DPRINTF(params) if (umouse_debug) printf params
#define WPRINTF(params) printf params
/* USB endpoint context (1-15) for reporting mouse data events*/
#define UMOUSE_INTR_ENDPT 1
#define UMOUSE_REPORT_DESC_TYPE 0x22
#define UMOUSE_GET_REPORT 0x01
#define UMOUSE_GET_IDLE 0x02
#define UMOUSE_GET_PROTOCOL 0x03
#define UMOUSE_SET_REPORT 0x09
#define UMOUSE_SET_IDLE 0x0A
#define UMOUSE_SET_PROTOCOL 0x0B
#define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
enum {
UMSTR_LANG,
UMSTR_MANUFACTURER,
UMSTR_PRODUCT,
UMSTR_SERIAL,
UMSTR_CONFIG,
UMSTR_MAX
};
static const char *umouse_desc_strings[] = {
"\x04\x09",
"BHYVE",
"HID Tablet",
"01",
"HID Tablet Device",
};
struct umouse_hid_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bcdHID[2];
uint8_t bCountryCode;
uint8_t bNumDescriptors;
uint8_t bReportDescriptorType;
uint8_t wItemLength[2];
} __packed;
struct umouse_config_desc {
struct usb_config_descriptor confd;
struct usb_interface_descriptor ifcd;
struct umouse_hid_descriptor hidd;
struct usb_endpoint_descriptor endpd;
struct usb_endpoint_ss_comp_descriptor sscompd;
} __packed;
#define MOUSE_MAX_X 0x8000
#define MOUSE_MAX_Y 0x8000
static const uint8_t umouse_report_desc[] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x02, /* USAGE (Mouse) */
0xa1, 0x01, /* COLLECTION (Application) */
0x09, 0x01, /* USAGE (Pointer) */
0xa1, 0x00, /* COLLECTION (Physical) */
0x05, 0x09, /* USAGE_PAGE (Button) */
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x03, /* REPORT_COUNT (3) */
0x81, 0x02, /* INPUT (Data,Var,Abs); 3 buttons */
0x75, 0x05, /* REPORT_SIZE (5) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs); padding */
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x30, /* USAGE (X) */
0x09, 0x31, /* USAGE (Y) */
0x35, 0x00, /* PHYSICAL_MINIMUM (0) */
0x46, 0xff, 0x7f, /* PHYSICAL_MAXIMUM (0x7fff) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x26, 0xff, 0x7f, /* LOGICAL_MAXIMUM (0x7fff) */
0x75, 0x10, /* REPORT_SIZE (16) */
0x95, 0x02, /* REPORT_COUNT (2) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x05, 0x01, /* USAGE Page (Generic Desktop) */
0x09, 0x38, /* USAGE (Wheel) */
0x35, 0x00, /* PHYSICAL_MINIMUM (0) */
0x45, 0x00, /* PHYSICAL_MAXIMUM (0) */
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x81, 0x06, /* INPUT (Data,Var,Rel) */
0xc0, /* END_COLLECTION */
0xc0 /* END_COLLECTION */
};
struct umouse_report {
uint8_t buttons; /* bits: 0 left, 1 right, 2 middle */
int16_t x; /* x position */
int16_t y; /* y position */
int8_t z; /* z wheel position */
} __packed;
#define MSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
static struct usb_device_descriptor umouse_dev_desc = {
.bLength = sizeof(umouse_dev_desc),
.bDescriptorType = UDESC_DEVICE,
MSETW(.bcdUSB, UD_USB_3_0),
.bMaxPacketSize = 8, /* max packet size */
MSETW(.idVendor, 0xFB5D), /* vendor */
MSETW(.idProduct, 0x0001), /* product */
MSETW(.bcdDevice, 0), /* device version */
.iManufacturer = UMSTR_MANUFACTURER,
.iProduct = UMSTR_PRODUCT,
.iSerialNumber = UMSTR_SERIAL,
.bNumConfigurations = 1,
};
static struct umouse_config_desc umouse_confd = {
.confd = {
.bLength = sizeof(umouse_confd.confd),
.bDescriptorType = UDESC_CONFIG,
.wTotalLength[0] = sizeof(umouse_confd),
.bNumInterface = 1,
.bConfigurationValue = 1,
.iConfiguration = UMSTR_CONFIG,
.bmAttributes = UC_BUS_POWERED | UC_REMOTE_WAKEUP,
.bMaxPower = 0,
},
.ifcd = {
.bLength = sizeof(umouse_confd.ifcd),
.bDescriptorType = UDESC_INTERFACE,
.bNumEndpoints = 1,
.bInterfaceClass = UICLASS_HID,
.bInterfaceSubClass = UISUBCLASS_BOOT,
.bInterfaceProtocol = UIPROTO_MOUSE,
},
.hidd = {
.bLength = sizeof(umouse_confd.hidd),
.bDescriptorType = 0x21,
.bcdHID = { 0x01, 0x10 },
.bCountryCode = 0,
.bNumDescriptors = 1,
.bReportDescriptorType = UMOUSE_REPORT_DESC_TYPE,
.wItemLength = { sizeof(umouse_report_desc), 0 },
},
.endpd = {
.bLength = sizeof(umouse_confd.endpd),
.bDescriptorType = UDESC_ENDPOINT,
.bEndpointAddress = UE_DIR_IN | UMOUSE_INTR_ENDPT,
.bmAttributes = UE_INTERRUPT,
.wMaxPacketSize[0] = 8,
.bInterval = 0xA,
},
.sscompd = {
.bLength = sizeof(umouse_confd.sscompd),
.bDescriptorType = UDESC_ENDPOINT_SS_COMP,
.bMaxBurst = 0,
.bmAttributes = 0,
MSETW(.wBytesPerInterval, 0),
},
};
struct umouse_bos_desc {
struct usb_bos_descriptor bosd;
struct usb_devcap_ss_descriptor usbssd;
} __packed;
struct umouse_bos_desc umouse_bosd = {
.bosd = {
.bLength = sizeof(umouse_bosd.bosd),
.bDescriptorType = UDESC_BOS,
HSETW(.wTotalLength, sizeof(umouse_bosd)),
.bNumDeviceCaps = 1,
},
.usbssd = {
.bLength = sizeof(umouse_bosd.usbssd),
.bDescriptorType = UDESC_DEVICE_CAPABILITY,
.bDevCapabilityType = 3,
.bmAttributes = 0,
HSETW(.wSpeedsSupported, 0x08),
.bFunctionalitySupport = 3,
.bU1DevExitLat = 0xa, /* dummy - not used */
.wU2DevExitLat = { 0x20, 0x00 },
}
};
struct umouse_softc {
struct usb_hci *hci;
char *opt;
struct umouse_report um_report;
int newdata;
struct {
uint8_t idle;
uint8_t protocol;
uint8_t feature;
} hid;
pthread_mutex_t mtx;
pthread_mutex_t ev_mtx;
int polling;
struct timeval prev_evt;
};
static void
umouse_event(uint8_t button, int x, int y, void *arg)
{
struct umouse_softc *sc;
struct bhyvegc_image *gc;
gc = console_get_image();
if (gc == NULL) {
/* not ready */
return;
}
sc = arg;
pthread_mutex_lock(&sc->mtx);
sc->um_report.buttons = 0;
sc->um_report.z = 0;
if (button & 0x01)
sc->um_report.buttons |= 0x01; /* left */
if (button & 0x02)
sc->um_report.buttons |= 0x04; /* middle */
if (button & 0x04)
sc->um_report.buttons |= 0x02; /* right */
if (button & 0x8)
sc->um_report.z = 1;
if (button & 0x10)
sc->um_report.z = -1;
/* scale coords to mouse resolution */
sc->um_report.x = MOUSE_MAX_X * x / gc->width;
sc->um_report.y = MOUSE_MAX_X * y / gc->height;
sc->newdata = 1;
pthread_mutex_unlock(&sc->mtx);
pthread_mutex_lock(&sc->ev_mtx);
sc->hci->hci_intr(sc->hci, UE_DIR_IN | UMOUSE_INTR_ENDPT);
pthread_mutex_unlock(&sc->ev_mtx);
}
static void *
umouse_init(struct usb_hci *hci, char *opt)
{
struct umouse_softc *sc;
char *mopt;
mopt = opt;
sc = calloc(1, sizeof(struct umouse_softc));
sc->hci = hci;
sc->hid.protocol = 1; /* REPORT protocol */
sc->opt = strdup(opt);
pthread_mutex_init(&sc->mtx, NULL);
pthread_mutex_init(&sc->ev_mtx, NULL);
console_ptr_register(umouse_event, sc, 10);
return (sc);
}
#define UREQ(x,y) ((x) | ((y) << 8))
static int
umouse_request(void *scarg, struct usb_data_xfer *xfer)
{
struct umouse_softc *sc;
struct usb_data_xfer_block *data;
const char *str;
uint16_t value;
uint16_t index;
uint16_t len;
uint16_t slen;
uint8_t *udata;
int err;
int i, idx;
int eshort;
sc = scarg;
data = NULL;
udata = NULL;
idx = xfer->head;
for (i = 0; i < xfer->ndata; i++) {
xfer->data[idx].bdone = 0;
if (data == NULL && USB_DATA_OK(xfer,i)) {
data = &xfer->data[idx];
udata = data->buf;
}
xfer->data[idx].processed = 1;
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
}
err = USB_ERR_NORMAL_COMPLETION;
eshort = 0;
if (!xfer->ureq) {
DPRINTF(("umouse_request: port %d\r\n", sc->hci->hci_port));
goto done;
}
value = UGETW(xfer->ureq->wValue);
index = UGETW(xfer->ureq->wIndex);
len = UGETW(xfer->ureq->wLength);
DPRINTF(("umouse_request: port %d, type 0x%x, req 0x%x, val 0x%x, "
"idx 0x%x, len %u\r\n",
sc->hci->hci_port, xfer->ureq->bmRequestType,
xfer->ureq->bRequest, value, index, len));
switch (UREQ(xfer->ureq->bRequest, xfer->ureq->bmRequestType)) {
case UREQ(UR_GET_CONFIG, UT_READ_DEVICE):
DPRINTF(("umouse: (UR_GET_CONFIG, UT_READ_DEVICE)\r\n"));
if (!data)
break;
*udata = umouse_confd.confd.bConfigurationValue;
data->blen = len > 0 ? len - 1 : 0;
eshort = data->blen > 0;
data->bdone += 1;
break;
case UREQ(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
DPRINTF(("umouse: (UR_GET_DESCRIPTOR, UT_READ_DEVICE) val %x\r\n",
value >> 8));
if (!data)
break;
switch (value >> 8) {
case UDESC_DEVICE:
DPRINTF(("umouse: (->UDESC_DEVICE) len %u ?= "
"sizeof(umouse_dev_desc) %lu\r\n",
len, sizeof(umouse_dev_desc)));
if ((value & 0xFF) != 0) {
err = USB_ERR_IOERROR;
goto done;
}
if (len > sizeof(umouse_dev_desc)) {
data->blen = len - sizeof(umouse_dev_desc);
len = sizeof(umouse_dev_desc);
} else
data->blen = 0;
memcpy(data->buf, &umouse_dev_desc, len);
data->bdone += len;
break;
case UDESC_CONFIG:
DPRINTF(("umouse: (->UDESC_CONFIG)\r\n"));
if ((value & 0xFF) != 0) {
err = USB_ERR_IOERROR;
goto done;
}
if (len > sizeof(umouse_confd)) {
data->blen = len - sizeof(umouse_confd);
len = sizeof(umouse_confd);
} else
data->blen = 0;
memcpy(data->buf, &umouse_confd, len);
data->bdone += len;
break;
case UDESC_STRING:
DPRINTF(("umouse: (->UDESC_STRING)\r\n"));
str = NULL;
if ((value & 0xFF) < UMSTR_MAX)
str = umouse_desc_strings[value & 0xFF];
else
goto done;
if ((value & 0xFF) == UMSTR_LANG) {
udata[0] = 4;
udata[1] = UDESC_STRING;
data->blen = len - 2;
len -= 2;
data->bdone += 2;
if (len >= 2) {
udata[2] = str[0];
udata[3] = str[1];
data->blen -= 2;
data->bdone += 2;
} else
data->blen = 0;
goto done;
}
slen = 2 + strlen(str) * 2;
udata[0] = slen;
udata[1] = UDESC_STRING;
if (len > slen) {
data->blen = len - slen;
len = slen;
} else
data->blen = 0;
for (i = 2; i < len; i += 2) {
udata[i] = *str++;
udata[i+1] = '\0';
}
data->bdone += slen;
break;
case UDESC_BOS:
DPRINTF(("umouse: USB3 BOS\r\n"));
if (len > sizeof(umouse_bosd)) {
data->blen = len - sizeof(umouse_bosd);
len = sizeof(umouse_bosd);
} else
data->blen = 0;
memcpy(udata, &umouse_bosd, len);
data->bdone += len;
break;
default:
DPRINTF(("umouse: unknown(%d)->ERROR\r\n", value >> 8));
err = USB_ERR_IOERROR;
goto done;
}
eshort = data->blen > 0;
break;
case UREQ(UR_GET_DESCRIPTOR, UT_READ_INTERFACE):
DPRINTF(("umouse: (UR_GET_DESCRIPTOR, UT_READ_INTERFACE) "
"0x%x\r\n", (value >> 8)));
if (!data)
break;
switch (value >> 8) {
case UMOUSE_REPORT_DESC_TYPE:
if (len > sizeof(umouse_report_desc)) {
data->blen = len - sizeof(umouse_report_desc);
len = sizeof(umouse_report_desc);
} else
data->blen = 0;
memcpy(data->buf, umouse_report_desc, len);
data->bdone += len;
break;
default:
DPRINTF(("umouse: IO ERROR\r\n"));
err = USB_ERR_IOERROR;
goto done;
}
eshort = data->blen > 0;
break;
case UREQ(UR_GET_INTERFACE, UT_READ_INTERFACE):
DPRINTF(("umouse: (UR_GET_INTERFACE, UT_READ_INTERFACE)\r\n"));
if (index != 0) {
DPRINTF(("umouse get_interface, invalid index %d\r\n",
index));
err = USB_ERR_IOERROR;
goto done;
}
if (!data)
break;
if (len > 0) {
*udata = 0;
data->blen = len - 1;
}
eshort = data->blen > 0;
data->bdone += 1;
break;
case UREQ(UR_GET_STATUS, UT_READ_DEVICE):
DPRINTF(("umouse: (UR_GET_STATUS, UT_READ_DEVICE)\r\n"));
if (data != NULL && len > 1) {
if (sc->hid.feature == UF_DEVICE_REMOTE_WAKEUP)
USETW(udata, UDS_REMOTE_WAKEUP);
else
USETW(udata, 0);
data->blen = len - 2;
data->bdone += 2;
}
eshort = data->blen > 0;
break;
case UREQ(UR_GET_STATUS, UT_READ_INTERFACE):
case UREQ(UR_GET_STATUS, UT_READ_ENDPOINT):
DPRINTF(("umouse: (UR_GET_STATUS, UT_READ_INTERFACE)\r\n"));
if (data != NULL && len > 1) {
USETW(udata, 0);
data->blen = len - 2;
data->bdone += 2;
}
eshort = data->blen > 0;
break;
case UREQ(UR_SET_ADDRESS, UT_WRITE_DEVICE):
/* XXX Controller should've handled this */
DPRINTF(("umouse set address %u\r\n", value));
break;
case UREQ(UR_SET_CONFIG, UT_WRITE_DEVICE):
DPRINTF(("umouse set config %u\r\n", value));
break;
case UREQ(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
DPRINTF(("umouse set descriptor %u\r\n", value));
break;
case UREQ(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
DPRINTF(("umouse: (UR_SET_FEATURE, UT_WRITE_DEVICE) %x\r\n", value));
if (value == UF_DEVICE_REMOTE_WAKEUP)
sc->hid.feature = 0;
break;
case UREQ(UR_SET_FEATURE, UT_WRITE_DEVICE):
DPRINTF(("umouse: (UR_SET_FEATURE, UT_WRITE_DEVICE) %x\r\n", value));
if (value == UF_DEVICE_REMOTE_WAKEUP)
sc->hid.feature = UF_DEVICE_REMOTE_WAKEUP;
break;
case UREQ(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
case UREQ(UR_SET_FEATURE, UT_WRITE_INTERFACE):
case UREQ(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
DPRINTF(("umouse: (UR_CLEAR_FEATURE, UT_WRITE_INTERFACE)\r\n"));
err = USB_ERR_IOERROR;
goto done;
case UREQ(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
DPRINTF(("umouse set interface %u\r\n", value));
break;
case UREQ(UR_ISOCH_DELAY, UT_WRITE_DEVICE):
DPRINTF(("umouse set isoch delay %u\r\n", value));
break;
case UREQ(UR_SET_SEL, 0):
DPRINTF(("umouse set sel\r\n"));
break;
case UREQ(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
DPRINTF(("umouse synch frame\r\n"));
break;
/* HID device requests */
case UREQ(UMOUSE_GET_REPORT, UT_READ_CLASS_INTERFACE):
DPRINTF(("umouse: (UMOUSE_GET_REPORT, UT_READ_CLASS_INTERFACE) "
"0x%x\r\n", (value >> 8)));
if (!data)
break;
if ((value >> 8) == 0x01 && len >= sizeof(sc->um_report)) {
/* TODO read from backend */
if (len > sizeof(sc->um_report)) {
data->blen = len - sizeof(sc->um_report);
len = sizeof(sc->um_report);
} else
data->blen = 0;
memcpy(data->buf, &sc->um_report, len);
data->bdone += len;
} else {
err = USB_ERR_IOERROR;
goto done;
}
eshort = data->blen > 0;
break;
case UREQ(UMOUSE_GET_IDLE, UT_READ_CLASS_INTERFACE):
if (data != NULL && len > 0) {
*udata = sc->hid.idle;
data->blen = len - 1;
data->bdone += 1;
}
eshort = data->blen > 0;
break;
case UREQ(UMOUSE_GET_PROTOCOL, UT_READ_CLASS_INTERFACE):
if (data != NULL && len > 0) {
*udata = sc->hid.protocol;
data->blen = len - 1;
data->bdone += 1;
}
eshort = data->blen > 0;
break;
case UREQ(UMOUSE_SET_REPORT, UT_WRITE_CLASS_INTERFACE):
DPRINTF(("umouse: (UMOUSE_SET_REPORT, UT_WRITE_CLASS_INTERFACE) ignored\r\n"));
break;
case UREQ(UMOUSE_SET_IDLE, UT_WRITE_CLASS_INTERFACE):
sc->hid.idle = UGETW(xfer->ureq->wValue) >> 8;
DPRINTF(("umouse: (UMOUSE_SET_IDLE, UT_WRITE_CLASS_INTERFACE) %x\r\n",
sc->hid.idle));
break;
case UREQ(UMOUSE_SET_PROTOCOL, UT_WRITE_CLASS_INTERFACE):
sc->hid.protocol = UGETW(xfer->ureq->wValue) >> 8;
DPRINTF(("umouse: (UR_CLEAR_FEATURE, UT_WRITE_CLASS_INTERFACE) %x\r\n",
sc->hid.protocol));
break;
default:
DPRINTF(("**** umouse request unhandled\r\n"));
err = USB_ERR_IOERROR;
break;
}
done:
if (xfer->ureq && (xfer->ureq->bmRequestType & UT_WRITE) &&
(err == USB_ERR_NORMAL_COMPLETION) && (data != NULL))
data->blen = 0;
else if (eshort)
err = USB_ERR_SHORT_XFER;
DPRINTF(("umouse request error code %d (0=ok), blen %u txlen %u\r\n",
err, (data ? data->blen : 0), (data ? data->bdone : 0)));
return (err);
}
static int
umouse_data_handler(void *scarg, struct usb_data_xfer *xfer, int dir,
int epctx)
{
struct umouse_softc *sc;
struct usb_data_xfer_block *data;
uint8_t *udata;
int len, i, idx;
int err;
DPRINTF(("umouse handle data - DIR=%s|EP=%d, blen %d\r\n",
dir ? "IN" : "OUT", epctx, xfer->data[0].blen));
/* find buffer to add data */
udata = NULL;
err = USB_ERR_NORMAL_COMPLETION;
/* handle xfer at first unprocessed item with buffer */
data = NULL;
idx = xfer->head;
for (i = 0; i < xfer->ndata; i++) {
data = &xfer->data[idx];
if (data->buf != NULL && data->blen != 0) {
break;
} else {
data->processed = 1;
data = NULL;
}
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
}
if (!data)
goto done;
udata = data->buf;
len = data->blen;
if (udata == NULL) {
DPRINTF(("umouse no buffer provided for input\r\n"));
err = USB_ERR_NOMEM;
goto done;
}
sc = scarg;
if (dir) {
pthread_mutex_lock(&sc->mtx);
if (!sc->newdata) {
err = USB_ERR_CANCELLED;
USB_DATA_SET_ERRCODE(&xfer->data[xfer->head], USB_NAK);
pthread_mutex_unlock(&sc->mtx);
goto done;
}
if (sc->polling) {
err = USB_ERR_STALLED;
USB_DATA_SET_ERRCODE(data, USB_STALL);
pthread_mutex_unlock(&sc->mtx);
goto done;
}
sc->polling = 1;
if (len > 0) {
sc->newdata = 0;
data->processed = 1;
data->bdone += 6;
memcpy(udata, &sc->um_report, 6);
data->blen = len - 6;
if (data->blen > 0)
err = USB_ERR_SHORT_XFER;
}
sc->polling = 0;
pthread_mutex_unlock(&sc->mtx);
} else {
USB_DATA_SET_ERRCODE(data, USB_STALL);
err = USB_ERR_STALLED;
}
done:
return (err);
}
static int
umouse_reset(void *scarg)
{
struct umouse_softc *sc;
sc = scarg;
sc->newdata = 0;
return (0);
}
static int
umouse_remove(void *scarg)
{
return (0);
}
static int
umouse_stop(void *scarg)
{
return (0);
}
struct usb_devemu ue_mouse = {
.ue_emu = "tablet",
.ue_usbver = 3,
.ue_usbspeed = USB_SPEED_HIGH,
.ue_init = umouse_init,
.ue_request = umouse_request,
.ue_data = umouse_data_handler,
.ue_reset = umouse_reset,
.ue_remove = umouse_remove,
.ue_stop = umouse_stop
};
USB_EMUL_SET(ue_mouse);

1331
usr.sbin/bhyve/vga.c Normal file

File diff suppressed because it is too large Load Diff

160
usr.sbin/bhyve/vga.h Normal file
View File

@ -0,0 +1,160 @@
/*-
* Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
* All rights reserved.
*
* 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, 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 OR CONTRIBUTORS 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.
*
* $FreeBSD$
*/
#ifndef _VGA_H_
#define _VGA_H_
#define VGA_IOPORT_START 0x3c0
#define VGA_IOPORT_END 0x3df
/* General registers */
#define GEN_INPUT_STS0_PORT 0x3c2
#define GEN_FEATURE_CTRL_PORT 0x3ca
#define GEN_MISC_OUTPUT_PORT 0x3cc
#define GEN_INPUT_STS1_MONO_PORT 0x3ba
#define GEN_INPUT_STS1_COLOR_PORT 0x3da
#define GEN_IS1_VR 0x08 /* Vertical retrace */
#define GEN_IS1_DE 0x01 /* Display enable not */
/* Attribute controller registers. */
#define ATC_IDX_PORT 0x3c0
#define ATC_DATA_PORT 0x3c1
#define ATC_IDX_MASK 0x1f
#define ATC_PALETTE0 0
#define ATC_PALETTE15 15
#define ATC_MODE_CONTROL 16
#define ATC_MC_IPS 0x80 /* Internal palette size */
#define ATC_MC_GA 0x01 /* Graphics/alphanumeric */
#define ATC_OVERSCAN_COLOR 17
#define ATC_COLOR_PLANE_ENABLE 18
#define ATC_HORIZ_PIXEL_PANNING 19
#define ATC_COLOR_SELECT 20
#define ATC_CS_C67 0x0c /* Color select bits 6+7 */
#define ATC_CS_C45 0x03 /* Color select bits 4+5 */
/* Sequencer registers. */
#define SEQ_IDX_PORT 0x3c4
#define SEQ_DATA_PORT 0x3c5
#define SEQ_RESET 0
#define SEQ_RESET_ASYNC 0x1
#define SEQ_RESET_SYNC 0x2
#define SEQ_CLOCKING_MODE 1
#define SEQ_CM_SO 0x20 /* Screen off */
#define SEQ_CM_89 0x01 /* 8/9 dot clock */
#define SEQ_MAP_MASK 2
#define SEQ_CHAR_MAP_SELECT 3
#define SEQ_CMS_SAH 0x20 /* Char map A bit 2 */
#define SEQ_CMS_SAH_SHIFT 5
#define SEQ_CMS_SA 0x0c /* Char map A bits 0+1 */
#define SEQ_CMS_SA_SHIFT 2
#define SEQ_CMS_SBH 0x10 /* Char map B bit 2 */
#define SEQ_CMS_SBH_SHIFT 4
#define SEQ_CMS_SB 0x03 /* Char map B bits 0+1 */
#define SEQ_CMS_SB_SHIFT 0
#define SEQ_MEMORY_MODE 4
#define SEQ_MM_C4 0x08 /* Chain 4 */
#define SEQ_MM_OE 0x04 /* Odd/even */
#define SEQ_MM_EM 0x02 /* Extended memory */
/* Graphics controller registers. */
#define GC_IDX_PORT 0x3ce
#define GC_DATA_PORT 0x3cf
#define GC_SET_RESET 0
#define GC_ENABLE_SET_RESET 1
#define GC_COLOR_COMPARE 2
#define GC_DATA_ROTATE 3
#define GC_READ_MAP_SELECT 4
#define GC_MODE 5
#define GC_MODE_OE 0x10 /* Odd/even */
#define GC_MODE_C4 0x04 /* Chain 4 */
#define GC_MISCELLANEOUS 6
#define GC_MISC_GM 0x01 /* Graphics/alphanumeric */
#define GC_MISC_MM 0x0c /* memory map */
#define GC_MISC_MM_SHIFT 2
#define GC_COLOR_DONT_CARE 7
#define GC_BIT_MASK 8
/* CRT controller registers. */
#define CRTC_IDX_MONO_PORT 0x3b4
#define CRTC_DATA_MONO_PORT 0x3b5
#define CRTC_IDX_COLOR_PORT 0x3d4
#define CRTC_DATA_COLOR_PORT 0x3d5
#define CRTC_HORIZ_TOTAL 0
#define CRTC_HORIZ_DISP_END 1
#define CRTC_START_HORIZ_BLANK 2
#define CRTC_END_HORIZ_BLANK 3
#define CRTC_START_HORIZ_RETRACE 4
#define CRTC_END_HORIZ_RETRACE 5
#define CRTC_VERT_TOTAL 6
#define CRTC_OVERFLOW 7
#define CRTC_OF_VRS9 0x80 /* VRS bit 9 */
#define CRTC_OF_VRS9_SHIFT 7
#define CRTC_OF_VDE9 0x40 /* VDE bit 9 */
#define CRTC_OF_VDE9_SHIFT 6
#define CRTC_OF_VRS8 0x04 /* VRS bit 8 */
#define CRTC_OF_VRS8_SHIFT 2
#define CRTC_OF_VDE8 0x02 /* VDE bit 8 */
#define CRTC_OF_VDE8_SHIFT 1
#define CRTC_PRESET_ROW_SCAN 8
#define CRTC_MAX_SCAN_LINE 9
#define CRTC_MSL_MSL 0x1f
#define CRTC_CURSOR_START 10
#define CRTC_CS_CO 0x20 /* Cursor off */
#define CRTC_CS_CS 0x1f /* Cursor start */
#define CRTC_CURSOR_END 11
#define CRTC_CE_CE 0x1f /* Cursor end */
#define CRTC_START_ADDR_HIGH 12
#define CRTC_START_ADDR_LOW 13
#define CRTC_CURSOR_LOC_HIGH 14
#define CRTC_CURSOR_LOC_LOW 15
#define CRTC_VERT_RETRACE_START 16
#define CRTC_VERT_RETRACE_END 17
#define CRTC_VRE_MASK 0xf
#define CRTC_VERT_DISP_END 18
#define CRTC_OFFSET 19
#define CRTC_UNDERLINE_LOC 20
#define CRTC_START_VERT_BLANK 21
#define CRTC_END_VERT_BLANK 22
#define CRTC_MODE_CONTROL 23
#define CRTC_MC_TE 0x80 /* Timing enable */
#define CRTC_LINE_COMPARE 24
/* DAC registers */
#define DAC_MASK 0x3c6
#define DAC_IDX_RD_PORT 0x3c7
#define DAC_IDX_WR_PORT 0x3c8
#define DAC_DATA_PORT 0x3c9
void *vga_init(int io_only);
#endif /* _VGA_H_ */