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.
This commit is contained in:
parent
361da73864
commit
205b961b15
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/bhyve_graphics/; revision=300829
15
Makefile
15
Makefile
@ -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
|
||||
|
||||
|
525
atkbdc.c
525
atkbdc.c
@ -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
atkbdc.h
Normal file
38
atkbdc.h
Normal 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
bhyvegc.c
Normal file
72
bhyvegc.c
Normal 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
bhyvegc.h
Normal file
46
bhyvegc.h
Normal 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_ */
|
@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "bhyverun.h"
|
||||
#include "acpi.h"
|
||||
#include "atkbdc.h"
|
||||
#include "inout.h"
|
||||
#include "dbgport.h"
|
||||
#include "fwctl.h"
|
||||
@ -901,6 +902,7 @@ main(int argc, char *argv[])
|
||||
|
||||
init_mem();
|
||||
init_inout();
|
||||
atkbdc_init(ctx);
|
||||
pci_irq_init(ctx);
|
||||
ioapic_init(ctx);
|
||||
|
||||
|
118
console.c
Normal file
118
console.c
Normal 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
console.h
Normal file
53
console.h
Normal 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
pci_fbuf.c
Normal file
406
pci_fbuf.c
Normal 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
pci_xhci.c
Normal file
2828
pci_xhci.c
Normal file
File diff suppressed because it is too large
Load Diff
353
pci_xhci.h
Normal file
353
pci_xhci.h
Normal 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
ps2kbd.c
Normal file
481
ps2kbd.c
Normal 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
ps2kbd.h
Normal file
39
ps2kbd.h
Normal 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
ps2mouse.c
Normal file
405
ps2mouse.c
Normal 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
ps2mouse.h
Normal file
41
ps2mouse.h
Normal 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
rfb.c
Normal file
949
rfb.c
Normal 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
rfb.h
Normal file
36
rfb.h
Normal 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_ */
|
86
sockstream.c
Normal file
86
sockstream.c
Normal 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);
|
||||
}
|
||||
|
||||
|
33
sockstream.h
Normal file
33
sockstream.h
Normal 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
usb_emul.c
Normal file
76
usb_emul.c
Normal 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
usb_emul.h
Normal file
156
usb_emul.h
Normal 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
usb_mouse.c
Normal file
803
usb_mouse.c
Normal 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);
|
160
vga.h
Normal file
160
vga.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user