initial import

internal commit: 0ab1ea615e5cfbb0687a9d593a86a7b774386076

Signed-off-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
Anthony Xu
2018-03-07 21:01:19 +08:00
committed by lijinxia
parent b966397914
commit bd31b1c53e
93 changed files with 37861 additions and 0 deletions

View File

@@ -0,0 +1,572 @@
/*-
* 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
* 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>
#include <sys/types.h>
#include <stdint.h>
#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 "acpi.h"
#include "inout.h"
#include "pci_core.h"
#include "irq.h"
#include "lpc.h"
#include "ps2kbd.h"
#include "ps2mouse.h"
#include "vmm.h"
#include "vmmapi.h"
#define KBD_DATA_PORT 0x60
#define KBD_STS_CTL_PORT 0x64
#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_base {
struct vmctx *ctx;
pthread_mutex_t mtx;
struct ps2kbd_info *ps2kbd;
struct ps2mouse_info *ps2mouse;
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_base *base)
{
if ((base->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
base->kbd.irq_active = true;
vm_isa_pulse_irq(base->ctx, base->kbd.irq, base->kbd.irq);
}
}
static void
atkbdc_assert_aux_intr(struct atkbdc_base *base)
{
if ((base->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
base->aux.irq_active = true;
vm_isa_pulse_irq(base->ctx, base->aux.irq, base->aux.irq);
}
}
static int
atkbdc_kbd_queue_data(struct atkbdc_base *base, uint8_t val)
{
if (base->kbd.bcnt < FIFOSZ) {
base->kbd.buffer[base->kbd.bwr] = val;
base->kbd.bwr = (base->kbd.bwr + 1) % FIFOSZ;
base->kbd.bcnt++;
base->status |= KBDS_KBD_BUFFER_FULL;
base->outport |= KBDO_KBD_OUTFULL;
} else {
printf("atkbd data buffer full\n");
}
return (base->kbd.bcnt < FIFOSZ);
}
static void
atkbdc_kbd_read(struct atkbdc_base *base)
{
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;
if (base->ram[0] & KBD_TRANSLATION) {
while (ps2kbd_read(base->ps2kbd, &val) != -1) {
if (val == 0xf0) {
release = 0x80;
continue;
} else {
val = translation[val] | release;
}
atkbdc_kbd_queue_data(base, val);
break;
}
} else {
while (base->kbd.bcnt < FIFOSZ) {
if (ps2kbd_read(base->ps2kbd, &val) != -1)
atkbdc_kbd_queue_data(base, val);
else
break;
}
}
if (((base->ram[0] & KBD_DISABLE_AUX_PORT) ||
ps2mouse_fifocnt(base->ps2mouse) == 0) && base->kbd.bcnt > 0)
atkbdc_assert_kbd_intr(base);
}
static void
atkbdc_aux_poll(struct atkbdc_base *base)
{
if (ps2mouse_fifocnt(base->ps2mouse) > 0) {
base->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
base->outport |= KBDO_AUX_OUTFULL;
atkbdc_assert_aux_intr(base);
}
}
static void
atkbdc_kbd_poll(struct atkbdc_base *base)
{
atkbdc_kbd_read(base);
}
static void
atkbdc_poll(struct atkbdc_base *base)
{
atkbdc_aux_poll(base);
atkbdc_kbd_poll(base);
}
static void
atkbdc_dequeue_data(struct atkbdc_base *base, uint8_t *buf)
{
if (ps2mouse_read(base->ps2mouse, buf) == 0) {
if (ps2mouse_fifocnt(base->ps2mouse) == 0) {
if (base->kbd.bcnt == 0)
base->status &= ~(KBDS_AUX_BUFFER_FULL |
KBDS_KBD_BUFFER_FULL);
else
base->status &= ~(KBDS_AUX_BUFFER_FULL);
base->outport &= ~KBDO_AUX_OUTFULL;
}
atkbdc_poll(base);
return;
}
if (base->kbd.bcnt > 0) {
*buf = base->kbd.buffer[base->kbd.brd];
base->kbd.brd = (base->kbd.brd + 1) % FIFOSZ;
base->kbd.bcnt--;
if (base->kbd.bcnt == 0) {
base->status &= ~KBDS_KBD_BUFFER_FULL;
base->outport &= ~KBDO_KBD_OUTFULL;
}
atkbdc_poll(base);
}
if (ps2mouse_fifocnt(base->ps2mouse) == 0 && base->kbd.bcnt == 0)
base->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_base *base;
uint8_t buf;
int retval;
if (bytes != 1)
return -1;
base = arg;
retval = 0;
pthread_mutex_lock(&base->mtx);
if (in) {
base->curcmd = 0;
if (base->ctrlbyte != 0) {
*eax = base->ctrlbyte & 0xff;
base->ctrlbyte = 0;
} else {
/* read device buffer; includes kbd cmd responses */
atkbdc_dequeue_data(base, &buf);
*eax = buf;
}
base->status &= ~KBDS_CTRL_FLAG;
pthread_mutex_unlock(&base->mtx);
return retval;
}
if (base->status & KBDS_CTRL_FLAG) {
/*
* Command byte for the controller.
*/
switch (base->curcmd) {
case KBDC_SET_COMMAND_BYTE:
base->ram[0] = *eax;
if (base->ram[0] & KBD_SYS_FLAG_BIT)
base->status |= KBDS_SYS_FLAG;
else
base->status &= ~KBDS_SYS_FLAG;
break;
case KBDC_WRITE_OUTPORT:
base->outport = *eax;
break;
case KBDC_WRITE_TO_AUX:
ps2mouse_write(base->ps2mouse, *eax, 0);
atkbdc_poll(base);
break;
case KBDC_WRITE_KBD_OUTBUF:
atkbdc_kbd_queue_data(base, *eax);
break;
case KBDC_WRITE_AUX_OUTBUF:
ps2mouse_write(base->ps2mouse, *eax, 1);
base->status |= (KBDS_AUX_BUFFER_FULL |
KBDS_KBD_BUFFER_FULL);
atkbdc_aux_poll(base);
break;
default:
/* write to particular RAM byte */
if (base->curcmd >= 0x61 && base->curcmd <= 0x7f) {
int byten;
byten = (base->curcmd - 0x60) & 0x1f;
base->ram[byten] = *eax & 0xff;
}
break;
}
base->curcmd = 0;
base->status &= ~KBDS_CTRL_FLAG;
pthread_mutex_unlock(&base->mtx);
return retval;
}
/*
* Data byte for the device.
*/
ps2kbd_write(base->ps2kbd, *eax);
atkbdc_poll(base);
pthread_mutex_unlock(&base->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)
{
struct atkbdc_base *base;
int error, retval;
if (bytes != 1)
return -1;
base = arg;
retval = 0;
pthread_mutex_lock(&base->mtx);
if (in) {
/* read status register */
*eax = base->status;
pthread_mutex_unlock(&base->mtx);
return retval;
}
base->curcmd = 0;
base->status |= KBDS_CTRL_FLAG;
base->ctrlbyte = 0;
switch (*eax) {
case KBDC_GET_COMMAND_BYTE:
base->ctrlbyte = CTRL_CMD_FLAG | base->ram[0];
break;
case KBDC_TEST_CTRL:
base->ctrlbyte = CTRL_CMD_FLAG | 0x55;
break;
case KBDC_TEST_AUX_PORT:
case KBDC_TEST_KBD_PORT:
base->ctrlbyte = CTRL_CMD_FLAG | 0;
break;
case KBDC_READ_INPORT:
base->ctrlbyte = CTRL_CMD_FLAG | 0;
break;
case KBDC_READ_OUTPORT:
base->ctrlbyte = CTRL_CMD_FLAG | base->outport;
break;
case KBDC_SET_COMMAND_BYTE:
case KBDC_WRITE_OUTPORT:
case KBDC_WRITE_KBD_OUTBUF:
case KBDC_WRITE_AUX_OUTBUF:
base->curcmd = *eax;
break;
case KBDC_DISABLE_KBD_PORT:
base->ram[0] |= KBD_DISABLE_KBD_PORT;
break;
case KBDC_ENABLE_KBD_PORT:
base->ram[0] &= ~KBD_DISABLE_KBD_PORT;
if (base->kbd.bcnt > 0)
base->status |= KBDS_KBD_BUFFER_FULL;
atkbdc_poll(base);
break;
case KBDC_WRITE_TO_AUX:
base->curcmd = *eax;
break;
case KBDC_DISABLE_AUX_PORT:
base->ram[0] |= KBD_DISABLE_AUX_PORT;
ps2mouse_toggle(base->ps2mouse, 0);
base->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
base->outport &= ~KBDS_AUX_BUFFER_FULL;
break;
case KBDC_ENABLE_AUX_PORT:
base->ram[0] &= ~KBD_DISABLE_AUX_PORT;
ps2mouse_toggle(base->ps2mouse, 1);
if (ps2mouse_fifocnt(base->ps2mouse) > 0)
base->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;
base->ctrlbyte = CTRL_CMD_FLAG | base->ram[byten];
}
break;
}
pthread_mutex_unlock(&base->mtx);
if (base->ctrlbyte != 0) {
base->status |= KBDS_KBD_BUFFER_FULL;
base->status &= ~KBDS_AUX_BUFFER_FULL;
atkbdc_assert_kbd_intr(base);
} else if (ps2mouse_fifocnt(base->ps2mouse) > 0 &&
(base->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
base->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
atkbdc_assert_aux_intr(base);
} else if (base->kbd.bcnt > 0 && (base->ram[0] &
KBD_DISABLE_KBD_PORT) == 0) {
base->status |= KBDS_KBD_BUFFER_FULL;
atkbdc_assert_kbd_intr(base);
}
return retval;
}
void
atkbdc_event(struct atkbdc_base *base, int iskbd)
{
pthread_mutex_lock(&base->mtx);
if (iskbd)
atkbdc_kbd_poll(base);
else
atkbdc_aux_poll(base);
pthread_mutex_unlock(&base->mtx);
}
void
atkbdc_init(struct vmctx *ctx)
{
struct inout_port iop;
struct atkbdc_base *base;
int error;
base = calloc(1, sizeof(struct atkbdc_base));
assert(base != NULL);
base->ctx = ctx;
pthread_mutex_init(&base->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 = base;
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 = base;
error = register_inout(&iop);
assert(error == 0);
pci_irq_reserve(KBD_DEV_IRQ);
base->kbd.irq = KBD_DEV_IRQ;
pci_irq_reserve(AUX_DEV_IRQ);
base->aux.irq = AUX_DEV_IRQ;
base->ps2kbd = ps2kbd_init(base);
base->ps2mouse = ps2mouse_init(base);
}
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);

View File

@@ -0,0 +1,936 @@
/*-
* Copyright (c) 2013 Peter Grehan <grehan@freebsd.org>
* 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>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <errno.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sysexits.h>
#include <unistd.h>
#include "dm.h"
#include "mevent.h"
#include "block_if.h"
#include "ahci.h"
/*
* Notes:
* The F_OFD_SETLK support is introduced in glibc 2.20.
* The glibc version on target board is above 2.20.
* The following code temporarily fixes up building issues on Ubuntu 14.04,
* where the glibc version is 2.19 by default.
* Theoretically we should use cross-compiling tool to compile applications.
*/
#ifndef F_OFD_SETLK
#define F_OFD_SETLK 37
#endif
#define BLOCKIF_SIG 0xb109b109
#define BLOCKIF_NUMTHR 8
#define BLOCKIF_MAXREQ (64 + BLOCKIF_NUMTHR)
/*
* Debug printf
*/
static int block_if_debug;
#define DPRINTF(params) do { if (block_if_debug) printf params; } while (0)
#define WPRINTF(params) (printf params)
enum blockop {
BOP_READ,
BOP_WRITE,
BOP_FLUSH,
BOP_DELETE
};
enum blockstat {
BST_FREE,
BST_BLOCK,
BST_PEND,
BST_BUSY,
BST_DONE
};
struct blockif_elem {
TAILQ_ENTRY(blockif_elem) link;
struct blockif_req *req;
enum blockop op;
enum blockstat status;
pthread_t tid;
off_t block;
};
struct blockif_ctxt {
int magic;
int fd;
int isblk;
int isgeom;
int candelete;
int rdonly;
off_t size;
int sub_file_assign;
off_t sub_file_start_lba;
struct flock fl;
int sectsz;
int psectsz;
int psectoff;
int closing;
pthread_t btid[BLOCKIF_NUMTHR];
pthread_mutex_t mtx;
pthread_cond_t cond;
/* Request elements and free/pending/busy queues */
TAILQ_HEAD(, blockif_elem) freeq;
TAILQ_HEAD(, blockif_elem) pendq;
TAILQ_HEAD(, blockif_elem) busyq;
struct blockif_elem reqs[BLOCKIF_MAXREQ];
};
static pthread_once_t blockif_once = PTHREAD_ONCE_INIT;
struct blockif_sig_elem {
pthread_mutex_t mtx;
pthread_cond_t cond;
int pending;
struct blockif_sig_elem *next;
};
static struct blockif_sig_elem *blockif_bse_head;
static int
blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq,
enum blockop op)
{
struct blockif_elem *be, *tbe;
off_t off;
int i;
be = TAILQ_FIRST(&bc->freeq);
assert(be != NULL);
assert(be->status == BST_FREE);
TAILQ_REMOVE(&bc->freeq, be, link);
be->req = breq;
be->op = op;
switch (op) {
case BOP_READ:
case BOP_WRITE:
case BOP_DELETE:
off = breq->offset;
for (i = 0; i < breq->iovcnt; i++)
off += breq->iov[i].iov_len;
break;
default:
/* off = OFF_MAX; */
off = 1 << (sizeof(off_t) - 1);
}
be->block = off;
TAILQ_FOREACH(tbe, &bc->pendq, link) {
if (tbe->block == breq->offset)
break;
}
if (tbe == NULL) {
TAILQ_FOREACH(tbe, &bc->busyq, link) {
if (tbe->block == breq->offset)
break;
}
}
if (tbe == NULL)
be->status = BST_PEND;
else
be->status = BST_BLOCK;
TAILQ_INSERT_TAIL(&bc->pendq, be, link);
return (be->status == BST_PEND);
}
static int
blockif_dequeue(struct blockif_ctxt *bc, pthread_t t, struct blockif_elem **bep)
{
struct blockif_elem *be;
TAILQ_FOREACH(be, &bc->pendq, link) {
if (be->status == BST_PEND)
break;
assert(be->status == BST_BLOCK);
}
if (be == NULL)
return 0;
TAILQ_REMOVE(&bc->pendq, be, link);
be->status = BST_BUSY;
be->tid = t;
TAILQ_INSERT_TAIL(&bc->busyq, be, link);
*bep = be;
return 1;
}
static void
blockif_complete(struct blockif_ctxt *bc, struct blockif_elem *be)
{
struct blockif_elem *tbe;
if (be->status == BST_DONE || be->status == BST_BUSY)
TAILQ_REMOVE(&bc->busyq, be, link);
else
TAILQ_REMOVE(&bc->pendq, be, link);
TAILQ_FOREACH(tbe, &bc->pendq, link) {
if (tbe->req->offset == be->block)
tbe->status = BST_PEND;
}
be->tid = 0;
be->status = BST_FREE;
be->req = NULL;
TAILQ_INSERT_TAIL(&bc->freeq, be, link);
}
static void
blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be, uint8_t *buf)
{
struct blockif_req *br;
off_t arg[2];
ssize_t clen, len, off, boff, voff;
int i, err;
br = be->req;
if (br->iovcnt <= 1)
buf = NULL;
err = 0;
switch (be->op) {
case BOP_READ:
if (buf == NULL) {
len = preadv(bc->fd, br->iov, br->iovcnt,
br->offset + bc->sub_file_start_lba);
if (len < 0)
err = errno;
else
br->resid -= len;
break;
}
i = 0;
off = voff = 0;
while (br->resid > 0) {
len = MIN(br->resid, MAXPHYS);
if (pread(bc->fd, buf, len, br->offset +
off + bc->sub_file_start_lba) < 0) {
err = errno;
break;
}
boff = 0;
do {
clen = MIN(len - boff, br->iov[i].iov_len -
voff);
memcpy(br->iov[i].iov_base + voff,
buf + boff, clen);
if (clen < br->iov[i].iov_len - voff)
voff += clen;
else {
i++;
voff = 0;
}
boff += clen;
} while (boff < len);
off += len;
br->resid -= len;
}
break;
case BOP_WRITE:
if (bc->rdonly) {
err = EROFS;
break;
}
if (buf == NULL) {
len = pwritev(bc->fd, br->iov, br->iovcnt,
br->offset + bc->sub_file_start_lba);
if (len < 0)
err = errno;
else
br->resid -= len;
break;
}
i = 0;
off = voff = 0;
while (br->resid > 0) {
len = MIN(br->resid, MAXPHYS);
boff = 0;
do {
clen = MIN(len - boff, br->iov[i].iov_len -
voff);
memcpy(buf + boff,
br->iov[i].iov_base + voff, clen);
if (clen < br->iov[i].iov_len - voff)
voff += clen;
else {
i++;
voff = 0;
}
boff += clen;
} while (boff < len);
if (pwrite(bc->fd, buf, len, br->offset +
off + bc->sub_file_start_lba) < 0) {
err = errno;
break;
}
off += len;
br->resid -= len;
}
break;
case BOP_FLUSH:
if (fsync(bc->fd))
err = errno;
break;
case BOP_DELETE:
/* only used by AHCI */
if (!bc->candelete)
err = EOPNOTSUPP;
else if (bc->rdonly)
err = EROFS;
else if (bc->isblk) {
arg[0] = br->offset;
arg[1] = br->resid;
if (ioctl(bc->fd, BLKDISCARD, arg))
err = errno;
else
br->resid = 0;
}
else
err = EOPNOTSUPP;
break;
default:
err = EINVAL;
break;
}
be->status = BST_DONE;
(*br->callback)(br, err);
}
static void *
blockif_thr(void *arg)
{
struct blockif_ctxt *bc;
struct blockif_elem *be;
pthread_t t;
uint8_t *buf;
bc = arg;
if (bc->isgeom)
buf = malloc(MAXPHYS);
else
buf = NULL;
t = pthread_self();
pthread_mutex_lock(&bc->mtx);
for (;;) {
while (blockif_dequeue(bc, t, &be)) {
pthread_mutex_unlock(&bc->mtx);
blockif_proc(bc, be, buf);
pthread_mutex_lock(&bc->mtx);
blockif_complete(bc, be);
}
/* Check ctxt status here to see if exit requested */
if (bc->closing)
break;
pthread_cond_wait(&bc->cond, &bc->mtx);
}
pthread_mutex_unlock(&bc->mtx);
if (buf)
free(buf);
pthread_exit(NULL);
return NULL;
}
static void
blockif_sigcont_handler(int signal)
{
struct blockif_sig_elem *bse;
WPRINTF(("block_if sigcont handler!\n"));
for (;;) {
/*
* Process the entire list even if not intended for
* this thread.
*/
do {
bse = blockif_bse_head;
if (bse == NULL)
return;
} while (!__sync_bool_compare_and_swap(
(uintptr_t *)&blockif_bse_head,
(uintptr_t)bse,
(uintptr_t)bse->next));
pthread_mutex_lock(&bse->mtx);
bse->pending = 0;
pthread_cond_signal(&bse->cond);
pthread_mutex_unlock(&bse->mtx);
}
}
static void
blockif_init(void)
{
signal(SIGCONT, blockif_sigcont_handler);
}
/*
* This function checks if the sub file range, specified by sub_start and
* sub_size, has any overlap with other sub file ranges with write access.
*/
static int
sub_file_validate(struct blockif_ctxt *bc, int fd, int read_only,
off_t sub_start, off_t sub_size)
{
struct flock *fl = &bc->fl;
memset(fl, 0, sizeof(struct flock));
fl->l_whence = SEEK_SET; /* offset base is start of file */
if (read_only)
fl->l_type = F_RDLCK;
else
fl->l_type = F_WRLCK;
fl->l_start = sub_start;
fl->l_len = sub_size;
/* use "open file description locks" to validate */
if (fcntl(fd, F_OFD_SETLK, fl) == -1) {
DPRINTF(("failed to lock subfile!\n"));
return -1;
}
/* Keep file lock on to prevent other sub files, until DM exits */
return 0;
}
void
sub_file_unlock(struct blockif_ctxt *bc)
{
struct flock *fl;
if (bc->sub_file_assign) {
fl = &bc->fl;
DPRINTF(("blockif: release file lock...\n"));
fl->l_type = F_UNLCK;
if (fcntl(bc->fd, F_OFD_SETLK, fl) == -1) {
fprintf(stderr, "blockif: failed to unlock subfile!\n");
exit(1);
}
DPRINTF(("blockif: release done\n"));
}
}
struct blockif_ctxt *
blockif_open(const char *optstr, const char *ident)
{
char tname[MAXCOMLEN + 1];
/* char name[MAXPATHLEN]; */
char *nopt, *xopts, *cp;
struct blockif_ctxt *bc;
struct stat sbuf;
/* struct diocgattr_arg arg; */
off_t size, psectsz, psectoff;
int extra, fd, i, sectsz;
int nocache, sync, ro, candelete, geom, ssopt, pssopt;
long sz;
long long b;
int err_code = -1;
off_t sub_file_start_lba, sub_file_size;
int sub_file_assign;
pthread_once(&blockif_once, blockif_init);
fd = -1;
ssopt = 0;
nocache = 0;
sync = 0;
ro = 0;
sub_file_assign = 0;
/*
* The first element in the optstring is always a pathname.
* Optional elements follow
*/
nopt = xopts = strdup(optstr);
if (!nopt) {
WPRINTF(("block_if.c: strdup retruns NULL\n"));
return NULL;
}
while (xopts != NULL) {
cp = strsep(&xopts, ",");
if (cp == nopt) /* file or device pathname */
continue;
else if (!strcmp(cp, "nocache"))
nocache = 1;
else if (!strcmp(cp, "sync") || !strcmp(cp, "direct"))
sync = 1;
else if (!strcmp(cp, "ro"))
ro = 1;
else if (sscanf(cp, "sectorsize=%d/%d", &ssopt, &pssopt) == 2)
;
else if (sscanf(cp, "sectorsize=%d", &ssopt) == 1)
pssopt = ssopt;
else if (sscanf(cp, "range=%ld/%ld", &sub_file_start_lba,
&sub_file_size) == 2)
sub_file_assign = 1;
else {
fprintf(stderr, "Invalid device option \"%s\"\n", cp);
goto err;
}
}
/* enforce a write-through policy by default */
nocache = 1;
sync = 1;
extra = 0;
if (nocache)
extra |= O_DIRECT;
if (sync)
extra |= O_SYNC;
fd = open(nopt, (ro ? O_RDONLY : O_RDWR) | extra);
if (fd < 0 && !ro) {
/* Attempt a r/w fail with a r/o open */
fd = open(nopt, O_RDONLY | extra);
ro = 1;
}
if (fd < 0) {
warn("Could not open backing file: %s", nopt);
goto err;
}
if (fstat(fd, &sbuf) < 0) {
warn("Could not stat backing file %s", nopt);
goto err;
}
/*
* Deal with raw devices
*/
size = sbuf.st_size;
sectsz = DEV_BSIZE;
psectsz = psectoff = 0;
candelete = geom = 0;
if (S_ISBLK(sbuf.st_mode)) {
/* get size */
err_code = ioctl(fd, BLKGETSIZE, &sz);
if (err_code) {
fprintf(stderr, "error %d getting block size!\n",
err_code);
size = sbuf.st_size; /* set default value */
} else {
size = sz * DEV_BSIZE; /* DEV_BSIZE is 512 on Linux */
}
if (!err_code || err_code == EFBIG) {
err_code = ioctl(fd, BLKGETSIZE64, &b);
if (err_code || b == 0 || b == sz)
size = b * DEV_BSIZE;
else
size = b;
}
DPRINTF(("block partition size is 0x%lx\n", size));
/* get sector size, 512 on Linux */
sectsz = DEV_BSIZE;
DPRINTF(("block partition sector size is 0x%x\n", sectsz));
/* get physical sector size */
err_code = ioctl(fd, BLKPBSZGET, &psectsz);
if (err_code) {
fprintf(stderr, "error %d getting physical sectsz!\n",
err_code);
psectsz = DEV_BSIZE; /* set default physical size */
}
DPRINTF(("block partition physical sector size is 0x%lx\n",
psectsz));
} else
psectsz = sbuf.st_blksize;
if (ssopt != 0) {
if (!powerof2(ssopt) || !powerof2(pssopt) || ssopt < 512 ||
ssopt > pssopt) {
fprintf(stderr, "Invalid sector size %d/%d\n",
ssopt, pssopt);
goto err;
}
/*
* Some backend drivers (e.g. cd0, ada0) require that the I/O
* size be a multiple of the device's sector size.
*
* Validate that the emulated sector size complies with this
* requirement.
*/
if (S_ISCHR(sbuf.st_mode)) {
if (ssopt < sectsz || (ssopt % sectsz) != 0) {
fprintf(stderr,
"Sector size %d incompatible with underlying device sector size %d\n",
ssopt, sectsz);
goto err;
}
}
sectsz = ssopt;
psectsz = pssopt;
psectoff = 0;
}
bc = calloc(1, sizeof(struct blockif_ctxt));
if (bc == NULL) {
perror("calloc");
goto err;
}
if (sub_file_assign) {
DPRINTF(("sector size is %d\n", sectsz));
bc->sub_file_assign = 1;
bc->sub_file_start_lba = sub_file_start_lba * sectsz;
size = sub_file_size * sectsz;
DPRINTF(("Validating sub file...\n"));
err_code = sub_file_validate(bc, fd, ro, bc->sub_file_start_lba,
size);
if (err_code < 0) {
fprintf(stderr, "subfile range specified not valid!\n");
exit(1);
}
DPRINTF(("Validated done!\n"));
} else {
/* normal case */
bc->sub_file_assign = 0;
bc->sub_file_start_lba = 0;
}
bc->magic = BLOCKIF_SIG;
bc->fd = fd;
bc->isblk = S_ISBLK(sbuf.st_mode);
bc->isgeom = geom;
bc->candelete = candelete;
bc->rdonly = ro;
bc->size = size;
bc->sectsz = sectsz;
bc->psectsz = psectsz;
bc->psectoff = psectoff;
pthread_mutex_init(&bc->mtx, NULL);
pthread_cond_init(&bc->cond, NULL);
TAILQ_INIT(&bc->freeq);
TAILQ_INIT(&bc->pendq);
TAILQ_INIT(&bc->busyq);
for (i = 0; i < BLOCKIF_MAXREQ; i++) {
bc->reqs[i].status = BST_FREE;
TAILQ_INSERT_HEAD(&bc->freeq, &bc->reqs[i], link);
}
for (i = 0; i < BLOCKIF_NUMTHR; i++) {
pthread_create(&bc->btid[i], NULL, blockif_thr, bc);
snprintf(tname, sizeof(tname), "blk-%s-%d", ident, i);
pthread_setname_np(bc->btid[i], tname);
}
return bc;
err:
if (fd >= 0)
close(fd);
return NULL;
}
static int
blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq,
enum blockop op)
{
int err;
err = 0;
pthread_mutex_lock(&bc->mtx);
if (!TAILQ_EMPTY(&bc->freeq)) {
/*
* Enqueue and inform the block i/o thread
* that there is work available
*/
if (blockif_enqueue(bc, breq, op))
pthread_cond_signal(&bc->cond);
} else {
/*
* Callers are not allowed to enqueue more than
* the specified blockif queue limit. Return an
* error to indicate that the queue length has been
* exceeded.
*/
err = E2BIG;
}
pthread_mutex_unlock(&bc->mtx);
return err;
}
int
blockif_read(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_READ);
}
int
blockif_write(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_WRITE);
}
int
blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_FLUSH);
}
int
blockif_delete(struct blockif_ctxt *bc, struct blockif_req *breq)
{
assert(bc->magic == BLOCKIF_SIG);
return blockif_request(bc, breq, BOP_DELETE);
}
int
blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
{
struct blockif_elem *be;
assert(bc->magic == BLOCKIF_SIG);
pthread_mutex_lock(&bc->mtx);
/*
* Check pending requests.
*/
TAILQ_FOREACH(be, &bc->pendq, link) {
if (be->req == breq)
break;
}
if (be != NULL) {
/*
* Found it.
*/
blockif_complete(bc, be);
pthread_mutex_unlock(&bc->mtx);
return 0;
}
/*
* Check in-flight requests.
*/
TAILQ_FOREACH(be, &bc->busyq, link) {
if (be->req == breq)
break;
}
if (be == NULL) {
/*
* Didn't find it.
*/
pthread_mutex_unlock(&bc->mtx);
return -1;
}
/*
* Interrupt the processing thread to force it return
* prematurely via it's normal callback path.
*/
while (be->status == BST_BUSY) {
struct blockif_sig_elem bse, *old_head;
pthread_mutex_init(&bse.mtx, NULL);
pthread_cond_init(&bse.cond, NULL);
bse.pending = 1;
do {
old_head = blockif_bse_head;
bse.next = old_head;
} while (!__sync_bool_compare_and_swap((uintptr_t *)&
blockif_bse_head,
(uintptr_t)old_head,
(uintptr_t)&bse));
pthread_kill(be->tid, SIGCONT);
pthread_mutex_lock(&bse.mtx);
while (bse.pending)
pthread_cond_wait(&bse.cond, &bse.mtx);
pthread_mutex_unlock(&bse.mtx);
}
pthread_mutex_unlock(&bc->mtx);
/*
* The processing thread has been interrupted. Since it's not
* clear if the callback has been invoked yet, return EBUSY.
*/
return -EBUSY;
}
int
blockif_close(struct blockif_ctxt *bc)
{
void *jval;
int i;
assert(bc->magic == BLOCKIF_SIG);
sub_file_unlock(bc);
/*
* Stop the block i/o thread
*/
pthread_mutex_lock(&bc->mtx);
bc->closing = 1;
pthread_mutex_unlock(&bc->mtx);
pthread_cond_broadcast(&bc->cond);
for (i = 0; i < BLOCKIF_NUMTHR; i++)
pthread_join(bc->btid[i], &jval);
/* XXX Cancel queued i/o's ??? */
/*
* Release resources
*/
bc->magic = 0;
close(bc->fd);
free(bc);
return 0;
}
/*
* Return virtual C/H/S values for a given block. Use the algorithm
* outlined in the VHD specification to calculate values.
*/
void
blockif_chs(struct blockif_ctxt *bc, uint16_t *c, uint8_t *h, uint8_t *s)
{
off_t sectors; /* total sectors of the block dev */
off_t hcyl; /* cylinders times heads */
uint16_t secpt; /* sectors per track */
uint8_t heads;
assert(bc->magic == BLOCKIF_SIG);
sectors = bc->size / bc->sectsz;
/* Clamp the size to the largest possible with CHS */
if (sectors > 65535UL*16*255)
sectors = 65535UL*16*255;
if (sectors >= 65536UL*16*63) {
secpt = 255;
heads = 16;
hcyl = sectors / secpt;
} else {
secpt = 17;
hcyl = sectors / secpt;
heads = (hcyl + 1023) / 1024;
if (heads < 4)
heads = 4;
if (hcyl >= (heads * 1024) || heads > 16) {
secpt = 31;
heads = 16;
hcyl = sectors / secpt;
}
if (hcyl >= (heads * 1024)) {
secpt = 63;
heads = 16;
hcyl = sectors / secpt;
}
}
*c = hcyl / heads;
*h = heads;
*s = secpt;
}
/*
* Accessors
*/
off_t
blockif_size(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->size;
}
int
blockif_sectsz(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->sectsz;
}
void
blockif_psectsz(struct blockif_ctxt *bc, int *size, int *off)
{
assert(bc->magic == BLOCKIF_SIG);
*size = bc->psectsz;
*off = bc->psectoff;
}
int
blockif_queuesz(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return (BLOCKIF_MAXREQ - 1);
}
int
blockif_is_ro(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->rdonly;
}
int
blockif_candelete(struct blockif_ctxt *bc)
{
assert(bc->magic == BLOCKIF_SIG);
return bc->candelete;
}

View File

@@ -0,0 +1,107 @@
/*************************************************************************
* INTEL CONFIDENTIAL
* Copyright 2018 Intel Corporation
*
* The source code contained or described herein and all documents related to
* the source code ("Material") are owned by Intel Corporation or its
* suppliers or licensors. Title to the Material remains with Intel
* Corporation or its suppliers and licensors. The Material contains trade
* secrets and proprietary and confidential information of Intel or its
* suppliers and licensors. The Material is protected by worldwide copyright
* and trade secret laws and treaty provisions. No part of the Material may
* be used, copied, reproduced, modified, published, uploaded, posted,
* transmitted, distributed, or disclosed in any way without Intel's prior
* express written permission.
*
* No license under any patent, copyright, trade secret or other
* intellectual property right is granted to or conferred upon you by
* disclosure or delivery of the Materials, either expressly, by
* implication, inducement, estoppel or otherwise. Any license under such
* intellectual property rights must be express and approved by Intel in
* writing.
*************************************************************************/
/* cmos io device is used for android device reboot to bootloader or
* recovery or normal boot usage
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <assert.h>
#include "inout.h"
#define CMOS_ADDR 0x74
#define CMOS_DATA 0x75
#define CMOS_BUF_SIZE 256
#define CMOS_NAME "cmos_io"
/* #define CMOS_DEBUG */
#ifdef CMOS_DEBUG
static FILE * dbg_file;
#define DPRINTF(format, args...) \
do { fprintf(dbg_file, format, args); fflush(dbg_file); } while (0)
#else
#define DPRINTF(format, arg...)
#endif
/* cmos buffer used to store write/read contents,
* and it should not be cleared when reboot
*/
static uint8_t cmos_buffer[CMOS_BUF_SIZE];
static int
cmos_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
static int buf_offset;
static int next_ops; /* 0 for addr, 1 for data, in pair (addr,data)*/
assert(port == CMOS_ADDR || port == CMOS_DATA);
assert(bytes == 1);
#ifdef CMOS_DEBUG
if (!dbg_file)
dbg_file = fopen("/tmp/cmos_log", "a+");
#endif
DPRINTF("%s port =0x%x, in=%d, size=%d, val=0x%x, ops=%d\n",
__func__, port, in, bytes, (uint8_t)*eax, next_ops);
if (port == CMOS_ADDR) {
/* if port is addr, ops should be 0 */
assert(next_ops == 0 && !in);
if (next_ops != 0) {
next_ops = 0;
return -1;
}
buf_offset = (uint8_t)(*eax);
next_ops = 1;
} else if (port == CMOS_DATA) {
assert(next_ops == 1);
if (next_ops != 1) {
next_ops = 0;
return -1;
}
if (in)
*eax = cmos_buffer[buf_offset];
else
cmos_buffer[buf_offset] = (uint8_t)*eax;
next_ops = 0;
}
return 0;
}
INOUT_PORT(cmos_io, CMOS_ADDR, IOPORT_F_INOUT, cmos_io_handler);
INOUT_PORT(cmos_io, CMOS_DATA, IOPORT_F_INOUT, cmos_io_handler);

View File

@@ -0,0 +1,74 @@
/*-
* Copyright (c) 2014 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* 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>
#include <sys/types.h>
#include <stdio.h>
#include <stdbool.h>
#include "vmm.h"
#include "vmmapi.h"
#include "ioapic.h"
#include "pci_core.h"
#include "lpc.h"
/*
* Assign PCI INTx interrupts to I/O APIC pins in a round-robin
* fashion. Note that we have no idea what the HPET is using, but the
* HPET is also programmable whereas this is intended for hardwired
* PCI interrupts.
*
* This assumes a single I/O APIC where pins >= 16 are permitted for
* PCI devices.
*/
static int pci_pins;
void
ioapic_init(struct vmctx *ctx)
{
if (vm_ioapic_pincount(ctx, &pci_pins) < 0) {
pci_pins = 0;
return;
}
/* Ignore the first 16 pins. */
if (pci_pins <= 16) {
pci_pins = 0;
return;
}
pci_pins -= 16;
}
int
ioapic_pci_alloc_irq(struct pci_vdev *dev)
{
static int last_pin;
if (pci_pins == 0)
return -1;
return (16 + (last_pin++ % pci_pins));
}

View File

@@ -0,0 +1,305 @@
/*-
* Copyright (c) 2013 Hudson River Trading LLC
* Written by: John H. Baldwin <jhb@FreeBSD.org>
* 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>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include "vmmapi.h"
#include "vmm.h"
#include "acpi.h"
#include "inout.h"
#include "mevent.h"
#include "irq.h"
#include "lpc.h"
static pthread_mutex_t pm_lock = PTHREAD_MUTEX_INITIALIZER;
static struct mevent *power_button;
static sig_t old_power_handler;
/*
* Reset Control register at I/O port 0xcf9. Bit 2 forces a system
* reset when it transitions from 0 to 1. Bit 1 selects the type of
* reset to attempt: 0 selects a "soft" reset, and 1 selects a "hard"
* reset.
*/
static int
reset_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
int error;
static uint8_t reset_control;
if (bytes != 1)
return -1;
if (in)
*eax = reset_control;
else {
reset_control = *eax;
/* Treat hard and soft resets the same. */
if (reset_control & 0x4) {
error = vm_suspend(ctx, VM_SUSPEND_RESET);
assert(error == 0 || errno == EALREADY);
}
}
return 0;
}
INOUT_PORT(reset_reg, 0xCF9, IOPORT_F_INOUT, reset_handler);
/*
* ACPI's SCI is a level-triggered interrupt.
*/
static int sci_active;
static void
sci_assert(struct vmctx *ctx)
{
if (sci_active)
return;
vm_isa_assert_irq(ctx, SCI_INT, SCI_INT);
sci_active = 1;
}
static void
sci_deassert(struct vmctx *ctx)
{
if (!sci_active)
return;
vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT);
sci_active = 0;
}
/*
* Power Management 1 Event Registers
*
* The only power management event supported is a power button upon
* receiving SIGTERM.
*/
static uint16_t pm1_enable, pm1_status;
#define PM1_TMR_STS 0x0001
#define PM1_BM_STS 0x0010
#define PM1_GBL_STS 0x0020
#define PM1_PWRBTN_STS 0x0100
#define PM1_SLPBTN_STS 0x0200
#define PM1_RTC_STS 0x0400
#define PM1_WAK_STS 0x8000
#define PM1_TMR_EN 0x0001
#define PM1_GBL_EN 0x0020
#define PM1_PWRBTN_EN 0x0100
#define PM1_SLPBTN_EN 0x0200
#define PM1_RTC_EN 0x0400
static void
sci_update(struct vmctx *ctx)
{
int need_sci;
/* See if the SCI should be active or not. */
need_sci = 0;
if ((pm1_enable & PM1_TMR_EN) && (pm1_status & PM1_TMR_STS))
need_sci = 1;
if ((pm1_enable & PM1_GBL_EN) && (pm1_status & PM1_GBL_STS))
need_sci = 1;
if ((pm1_enable & PM1_PWRBTN_EN) && (pm1_status & PM1_PWRBTN_STS))
need_sci = 1;
if ((pm1_enable & PM1_SLPBTN_EN) && (pm1_status & PM1_SLPBTN_STS))
need_sci = 1;
if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS))
need_sci = 1;
if (need_sci)
sci_assert(ctx);
else
sci_deassert(ctx);
}
static int
pm1_status_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
if (bytes != 2)
return -1;
pthread_mutex_lock(&pm_lock);
if (in)
*eax = pm1_status;
else {
/*
* Writes are only permitted to clear certain bits by
* writing 1 to those flags.
*/
pm1_status &= ~(*eax & (PM1_WAK_STS | PM1_RTC_STS |
PM1_SLPBTN_STS | PM1_PWRBTN_STS | PM1_BM_STS));
sci_update(ctx);
}
pthread_mutex_unlock(&pm_lock);
return 0;
}
static int
pm1_enable_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
if (bytes != 2)
return -1;
pthread_mutex_lock(&pm_lock);
if (in)
*eax = pm1_enable;
else {
/*
* Only permit certain bits to be set. We never use
* the global lock, but ACPI-CA whines profusely if it
* can't set GBL_EN.
*/
pm1_enable = *eax & (PM1_PWRBTN_EN | PM1_GBL_EN);
sci_update(ctx);
}
pthread_mutex_unlock(&pm_lock);
return 0;
}
INOUT_PORT(pm1_status, PM1A_EVT_ADDR, IOPORT_F_INOUT, pm1_status_handler);
INOUT_PORT(pm1_enable, PM1A_EVT_ADDR + 2, IOPORT_F_INOUT, pm1_enable_handler);
static void
power_button_handler(int signal, enum ev_type type, void *arg)
{
struct vmctx *ctx;
ctx = arg;
pthread_mutex_lock(&pm_lock);
if (!(pm1_status & PM1_PWRBTN_STS)) {
pm1_status |= PM1_PWRBTN_STS;
sci_update(ctx);
}
pthread_mutex_unlock(&pm_lock);
}
/*
* Power Management 1 Control Register
*
* This is mostly unimplemented except that we wish to handle writes that
* set SPL_EN to handle S5 (soft power off).
*/
static uint16_t pm1_control;
#define PM1_SCI_EN 0x0001
#define PM1_SLP_TYP 0x1c00
#define PM1_SLP_EN 0x2000
#define PM1_ALWAYS_ZERO 0xc003
static int
pm1_control_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
int error;
if (bytes != 2)
return -1;
if (in)
*eax = pm1_control;
else {
/*
* Various bits are write-only or reserved, so force them
* to zero in pm1_control. Always preserve SCI_EN as OSPM
* can never change it.
*/
pm1_control = (pm1_control & PM1_SCI_EN) |
(*eax & ~(PM1_SLP_EN | PM1_ALWAYS_ZERO));
/*
* If SLP_EN is set, check for S5. ACRN-DM's _S5_ method
* says that '5' should be stored in SLP_TYP for S5.
*/
if (*eax & PM1_SLP_EN) {
if ((pm1_control & PM1_SLP_TYP) >> 10 == 5) {
error = vm_suspend(ctx, VM_SUSPEND_POWEROFF);
assert(error == 0 || errno == EALREADY);
}
}
}
return 0;
}
INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler);
SYSRES_IO(PM1A_EVT_ADDR, 8);
/*
* ACPI SMI Command Register
*
* This write-only register is used to enable and disable ACPI.
*/
static int
smi_cmd_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
uint32_t *eax, void *arg)
{
assert(!in);
if (bytes != 1)
return -1;
pthread_mutex_lock(&pm_lock);
switch (*eax) {
case ACPI_ENABLE:
pm1_control |= PM1_SCI_EN;
if (power_button == NULL) {
power_button = mevent_add(SIGTERM, EVF_SIGNAL,
power_button_handler, ctx);
old_power_handler = signal(SIGTERM, SIG_IGN);
}
break;
case ACPI_DISABLE:
pm1_control &= ~PM1_SCI_EN;
if (power_button != NULL) {
mevent_delete(power_button);
power_button = NULL;
signal(SIGTERM, old_power_handler);
}
break;
}
pthread_mutex_unlock(&pm_lock);
return 0;
}
INOUT_PORT(smi_cmd, SMI_CMD, IOPORT_F_OUT, smi_cmd_handler);
SYSRES_IO(SMI_CMD, 1);
void
sci_init(struct vmctx *ctx)
{
/*
* Mark ACPI's SCI as level trigger and bump its use count
* in the PIRQ router.
*/
pci_irq_use(SCI_INT);
}

View File

@@ -0,0 +1,477 @@
/*-
* 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>
#include <sys/types.h>
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include "types.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_info {
struct atkbdc_base *base;
pthread_mutex_t mtx;
bool enabled;
struct fifo fifo;
uint8_t curcmd; /* current command for next byte */
};
static void
fifo_init(struct ps2kbd_info *kbd)
{
struct fifo *fifo;
fifo = &kbd->fifo;
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static void
fifo_reset(struct ps2kbd_info *kbd)
{
struct fifo *fifo;
fifo = &kbd->fifo;
bzero(fifo, sizeof(struct fifo));
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static void
fifo_put(struct ps2kbd_info *kbd, uint8_t val)
{
struct fifo *fifo;
fifo = &kbd->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_info *kbd, uint8_t *val)
{
struct fifo *fifo;
fifo = &kbd->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_info *kbd, uint8_t *val)
{
int retval;
pthread_mutex_lock(&kbd->mtx);
retval = fifo_get(kbd, val);
pthread_mutex_unlock(&kbd->mtx);
return retval;
}
void
ps2kbd_write(struct ps2kbd_info *kbd, uint8_t val)
{
pthread_mutex_lock(&kbd->mtx);
if (kbd->curcmd) {
switch (kbd->curcmd) {
case PS2KC_SET_TYPEMATIC:
fifo_put(kbd, PS2KC_ACK);
break;
case PS2KC_SET_SCANCODE_SET:
fifo_put(kbd, PS2KC_ACK);
break;
case PS2KC_SET_LEDS:
fifo_put(kbd, PS2KC_ACK);
break;
default:
fprintf(stderr, "Unhandled ps2 keyboard current "
"command byte 0x%02x\n", val);
break;
}
kbd->curcmd = 0;
} else {
switch (val) {
case 0x00:
fifo_put(kbd, PS2KC_ACK);
break;
case PS2KC_RESET_DEV:
fifo_reset(kbd);
fifo_put(kbd, PS2KC_ACK);
fifo_put(kbd, PS2KC_BAT_SUCCESS);
break;
case PS2KC_DISABLE:
kbd->enabled = false;
fifo_put(kbd, PS2KC_ACK);
break;
case PS2KC_ENABLE:
kbd->enabled = true;
fifo_reset(kbd);
fifo_put(kbd, PS2KC_ACK);
break;
case PS2KC_SET_TYPEMATIC:
kbd->curcmd = val;
fifo_put(kbd, PS2KC_ACK);
break;
case PS2KC_SEND_DEV_ID:
fifo_put(kbd, PS2KC_ACK);
fifo_put(kbd, 0xab);
fifo_put(kbd, 0x83);
break;
case PS2KC_SET_SCANCODE_SET:
kbd->curcmd = val;
fifo_put(kbd, PS2KC_ACK);
break;
case PS2KC_ECHO:
fifo_put(kbd, PS2KC_ECHO);
break;
case PS2KC_SET_LEDS:
kbd->curcmd = val;
fifo_put(kbd, PS2KC_ACK);
break;
default:
fprintf(stderr, "Unhandled ps2 keyboard command "
"0x%02x\n", val);
break;
}
}
pthread_mutex_unlock(&kbd->mtx);
}
/*
* Translate keysym to type 2 scancode and insert into keyboard buffer.
*/
static void
ps2kbd_keysym_queue(struct ps2kbd_info *kbd,
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(&kbd->mtx)); */
switch (keysym) {
case 0x0 ... 0x7f:
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, translation[keysym]);
break;
case 0xff08: /* Back space */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x66);
break;
case 0xff09: /* Tab */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x0d);
break;
case 0xff0d: /* Return */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x5a);
break;
case 0xff1b: /* Escape */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x76);
break;
case 0xff50: /* Home */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x6c);
break;
case 0xff51: /* Left arrow */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x6b);
break;
case 0xff52: /* Up arrow */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x75);
break;
case 0xff53: /* Right arrow */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x74);
break;
case 0xff54: /* Down arrow */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x72);
break;
case 0xff55: /* PgUp */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x7d);
break;
case 0xff56: /* PgDwn */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x7a);
break;
case 0xff57: /* End */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x69);
break;
case 0xff63: /* Ins */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x70);
break;
case 0xff8d: /* Keypad Enter */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x5a);
break;
case 0xffe1: /* Left shift */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x12);
break;
case 0xffe2: /* Right shift */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x59);
break;
case 0xffe3: /* Left control */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x14);
break;
case 0xffe4: /* Right control */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x14);
break;
case 0xffe7: /* Left meta */
/* XXX */
break;
case 0xffe8: /* Right meta */
/* XXX */
break;
case 0xffe9: /* Left alt */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x11);
break;
case 0xfe03: /* AltGr */
case 0xffea: /* Right alt */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x11);
break;
case 0xffeb: /* Left Windows */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x1f);
break;
case 0xffec: /* Right Windows */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x27);
break;
case 0xffbe: /* F1 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x05);
break;
case 0xffbf: /* F2 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x06);
break;
case 0xffc0: /* F3 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x04);
break;
case 0xffc1: /* F4 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x0C);
break;
case 0xffc2: /* F5 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x03);
break;
case 0xffc3: /* F6 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x0B);
break;
case 0xffc4: /* F7 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x83);
break;
case 0xffc5: /* F8 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x0A);
break;
case 0xffc6: /* F9 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x01);
break;
case 0xffc7: /* F10 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x09);
break;
case 0xffc8: /* F11 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x78);
break;
case 0xffc9: /* F12 */
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 0x07);
break;
case 0xffff: /* Del */
fifo_put(kbd, 0xe0);
if (!down)
fifo_put(kbd, 0xf0);
fifo_put(kbd, 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_info *kbd = arg;
int fifo_full;
pthread_mutex_lock(&kbd->mtx);
if (!kbd->enabled) {
pthread_mutex_unlock(&kbd->mtx);
return;
}
fifo_full = kbd->fifo.num == PS2KBD_FIFOSZ;
ps2kbd_keysym_queue(kbd, down, keysym);
pthread_mutex_unlock(&kbd->mtx);
if (!fifo_full)
atkbdc_event(kbd->base, 1);
}
struct ps2kbd_info *
ps2kbd_init(struct atkbdc_base *base)
{
struct ps2kbd_info *kbd;
kbd = calloc(1, sizeof(struct ps2kbd_info));
assert(kbd != NULL);
pthread_mutex_init(&kbd->mtx, NULL);
fifo_init(kbd);
kbd->base = base;
console_kbd_register(ps2kbd_event, kbd, 1);
return kbd;
}

View File

@@ -0,0 +1,412 @@
/*-
* 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 <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include "types.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 data bits */
#define PS2M_DATA_Y_OFLOW 0x80
#define PS2M_DATA_X_OFLOW 0x40
#define PS2M_DATA_Y_SIGN 0x20
#define PS2M_DATA_X_SIGN 0x10
#define PS2M_DATA_AONE 0x08
#define PS2M_DATA_MID_BUTTON 0x04
#define PS2M_DATA_RIGHT_BUTTON 0x02
#define PS2M_DATA_LEFT_BUTTON 0x01
/* 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_info {
struct atkbdc_base *base;
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_info *mouse)
{
struct fifo *fifo;
fifo = &mouse->fifo;
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static void
fifo_reset(struct ps2mouse_info *mouse)
{
struct fifo *fifo;
fifo = &mouse->fifo;
bzero(fifo, sizeof(struct fifo));
fifo->size = sizeof(((struct fifo *)0)->buf);
}
static void
fifo_put(struct ps2mouse_info *mouse, uint8_t val)
{
struct fifo *fifo;
fifo = &mouse->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_info *mouse, uint8_t *val)
{
struct fifo *fifo;
fifo = &mouse->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_info *mouse)
{
/* assert(pthread_mutex_isowned_np(&mouse->mtx)); */
mouse->delta_x = 0;
mouse->delta_y = 0;
}
static void
movement_update(struct ps2mouse_info *mouse, int x, int y)
{
mouse->delta_x += x - mouse->cur_x;
mouse->delta_y += mouse->cur_y - y;
mouse->cur_x = x;
mouse->cur_y = y;
}
static void
movement_get(struct ps2mouse_info *mouse)
{
uint8_t val0, val1, val2;
/* assert(pthread_mutex_isowned_np(&mouse->mtx)); */
val0 = PS2M_DATA_AONE;
val0 |= mouse->status & (PS2M_DATA_LEFT_BUTTON |
PS2M_DATA_RIGHT_BUTTON | PS2M_DATA_MID_BUTTON);
if (mouse->delta_x >= 0) {
if (mouse->delta_x > 255) {
val0 |= PS2M_DATA_X_OFLOW;
val1 = 255;
} else
val1 = mouse->delta_x;
} else {
val0 |= PS2M_DATA_X_SIGN;
if (mouse->delta_x < -255) {
val0 |= PS2M_DATA_X_OFLOW;
val1 = 255;
} else
val1 = mouse->delta_x;
}
mouse->delta_x = 0;
if (mouse->delta_y >= 0) {
if (mouse->delta_y > 255) {
val0 |= PS2M_DATA_Y_OFLOW;
val2 = 255;
} else
val2 = mouse->delta_y;
} else {
val0 |= PS2M_DATA_Y_SIGN;
if (mouse->delta_y < -255) {
val0 |= PS2M_DATA_Y_OFLOW;
val2 = 255;
} else
val2 = mouse->delta_y;
}
mouse->delta_y = 0;
if (mouse->fifo.num < (mouse->fifo.size - 3)) {
fifo_put(mouse, val0);
fifo_put(mouse, val1);
fifo_put(mouse, val2);
}
}
static void
ps2mouse_reset(struct ps2mouse_info *mouse)
{
/* assert(pthread_mutex_isowned_np(&mouse->mtx)); */
fifo_reset(mouse);
movement_reset(mouse);
mouse->status = PS2M_STS_ENABLE_DEV;
mouse->resolution = 4;
mouse->sampling_rate = 100;
mouse->cur_x = 0;
mouse->cur_y = 0;
mouse->delta_x = 0;
mouse->delta_y = 0;
}
int
ps2mouse_read(struct ps2mouse_info *mouse, uint8_t *val)
{
int retval;
pthread_mutex_lock(&mouse->mtx);
retval = fifo_get(mouse, val);
pthread_mutex_unlock(&mouse->mtx);
return retval;
}
int
ps2mouse_fifocnt(struct ps2mouse_info *mouse)
{
return mouse->fifo.num;
}
void
ps2mouse_toggle(struct ps2mouse_info *mouse, int enable)
{
pthread_mutex_lock(&mouse->mtx);
if (enable)
mouse->ctrlenable = 1;
else {
mouse->ctrlenable = 0;
mouse->fifo.rindex = 0;
mouse->fifo.windex = 0;
mouse->fifo.num = 0;
}
pthread_mutex_unlock(&mouse->mtx);
}
void
ps2mouse_write(struct ps2mouse_info *mouse, uint8_t val, int insert)
{
pthread_mutex_lock(&mouse->mtx);
fifo_reset(mouse);
if (mouse->curcmd) {
switch (mouse->curcmd) {
case PS2MC_SET_SAMPLING_RATE:
mouse->sampling_rate = val;
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_SET_RESOLUTION:
mouse->resolution = val;
fifo_put(mouse, PS2MC_ACK);
break;
default:
fprintf(stderr, "Unhandled ps2 mouse current "
"command byte 0x%02x\n", val);
break;
}
mouse->curcmd = 0;
} else if (insert) {
fifo_put(mouse, val);
} else {
switch (val) {
case 0x00:
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_RESET_DEV:
ps2mouse_reset(mouse);
fifo_put(mouse, PS2MC_ACK);
fifo_put(mouse, PS2MC_BAT_SUCCESS);
fifo_put(mouse, PS2MOUSE_DEV_ID);
break;
case PS2MC_SET_DEFAULTS:
ps2mouse_reset(mouse);
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_DISABLE:
fifo_reset(mouse);
mouse->status &= ~PS2M_STS_ENABLE_DEV;
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_ENABLE:
fifo_reset(mouse);
mouse->status |= PS2M_STS_ENABLE_DEV;
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_SET_SAMPLING_RATE:
mouse->curcmd = val;
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_SEND_DEV_ID:
fifo_put(mouse, PS2MC_ACK);
fifo_put(mouse, PS2MOUSE_DEV_ID);
break;
case PS2MC_SET_REMOTE_MODE:
mouse->status |= PS2M_STS_REMOTE_MODE;
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_SEND_DEV_DATA:
fifo_put(mouse, PS2MC_ACK);
movement_get(mouse);
break;
case PS2MC_SET_STREAM_MODE:
mouse->status &= ~PS2M_STS_REMOTE_MODE;
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_SEND_DEV_STATUS:
fifo_put(mouse, PS2MC_ACK);
fifo_put(mouse, mouse->status);
fifo_put(mouse, mouse->resolution);
fifo_put(mouse, mouse->sampling_rate);
break;
case PS2MC_SET_RESOLUTION:
mouse->curcmd = val;
fifo_put(mouse, PS2MC_ACK);
break;
case PS2MC_SET_SCALING1:
case PS2MC_SET_SCALING2:
fifo_put(mouse, PS2MC_ACK);
break;
default:
fifo_put(mouse, PS2MC_ACK);
fprintf(stderr, "Unhandled ps2 mouse command "
"0x%02x\n", val);
break;
}
}
pthread_mutex_unlock(&mouse->mtx);
}
static void
ps2mouse_event(uint8_t button, int x, int y, void *arg)
{
struct ps2mouse_info *mouse = arg;
pthread_mutex_lock(&mouse->mtx);
movement_update(mouse, x, y);
mouse->status &= ~(PS2M_STS_LEFT_BUTTON |
PS2M_STS_RIGHT_BUTTON | PS2M_STS_MID_BUTTON);
if (button & (1 << 0))
mouse->status |= PS2M_STS_LEFT_BUTTON;
if (button & (1 << 1))
mouse->status |= PS2M_STS_MID_BUTTON;
if (button & (1 << 2))
mouse->status |= PS2M_STS_RIGHT_BUTTON;
if ((mouse->status & PS2M_STS_ENABLE_DEV) == 0 || !mouse->ctrlenable) {
/* no data reporting */
pthread_mutex_unlock(&mouse->mtx);
return;
}
movement_get(mouse);
pthread_mutex_unlock(&mouse->mtx);
if (mouse->fifo.num > 0)
atkbdc_event(mouse->base, 0);
}
struct ps2mouse_info *
ps2mouse_init(struct atkbdc_base *base)
{
struct ps2mouse_info *mouse;
mouse = calloc(1, sizeof(struct ps2mouse_info));
assert(mouse != NULL);
pthread_mutex_init(&mouse->mtx, NULL);
fifo_init(mouse);
mouse->base = base;
pthread_mutex_lock(&mouse->mtx);
ps2mouse_reset(mouse);
pthread_mutex_unlock(&mouse->mtx);
console_ptr_register(ps2mouse_event, mouse, 1);
return mouse;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,687 @@
/*-
* Copyright (c) 2012 NetApp, Inc.
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* 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 NETAPP, INC ``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 NETAPP, INC 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>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include <sysexits.h>
#include "types.h"
#include "mevent.h"
#include "uart_core.h"
#include "ns16550.h"
#include "dm.h"
#define COM1_BASE 0x3F8
#define COM1_IRQ 4
#define COM2_BASE 0x2F8
#define COM2_IRQ 3
#define DEFAULT_RCLK 1843200
#define DEFAULT_BAUD 9600
#define FCR_RX_MASK 0xC0
#define MCR_OUT1 0x04
#define MCR_OUT2 0x08
#define MSR_DELTA_MASK 0x0f
#ifndef REG_SCR
#define REG_SCR com_scr
#endif
#define FIFOSZ 256
static struct termios tio_stdio_orig;
static struct {
int baseaddr;
int irq;
bool inuse;
} uart_lres[] = {
{ COM1_BASE, COM1_IRQ, false},
{ COM2_BASE, COM2_IRQ, false},
};
#define UART_NLDEVS (ARRAY_SIZE(uart_lres))
struct fifo {
uint8_t buf[FIFOSZ];
int rindex; /* index to read from */
int windex; /* index to write to */
int num; /* number of characters in the fifo */
int size; /* size of the fifo */
};
struct ttyfd {
bool opened;
int fd; /* tty device file descriptor */
struct termios tio_orig, tio_new; /* I/O Terminals */
};
struct uart_vdev {
pthread_mutex_t mtx; /* protects all elements */
uint8_t data; /* Data register (R/W) */
uint8_t ier; /* Interrupt enable register (R/W) */
uint8_t lcr; /* Line control register (R/W) */
uint8_t mcr; /* Modem control register (R/W) */
uint8_t lsr; /* Line status register (R/W) */
uint8_t msr; /* Modem status register (R/W) */
uint8_t fcr; /* FIFO control register (W) */
uint8_t scr; /* Scratch register (R/W) */
uint8_t dll; /* Baudrate divisor latch LSB */
uint8_t dlh; /* Baudrate divisor latch MSB */
struct fifo rxfifo;
struct mevent *mev;
struct ttyfd tty;
bool thre_int_pending; /* THRE interrupt pending */
void *arg;
uart_intr_func_t intr_assert;
uart_intr_func_t intr_deassert;
};
static void uart_drain(int fd, enum ev_type ev, void *arg);
static void
ttyclose(void)
{
tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig);
}
static void
ttyopen(struct ttyfd *tf)
{
tcgetattr(tf->fd, &tf->tio_orig);
tf->tio_new = tf->tio_orig;
cfmakeraw(&tf->tio_new);
tf->tio_new.c_cflag |= CLOCAL;
tcsetattr(tf->fd, TCSANOW, &tf->tio_new);
if (tf->fd == STDIN_FILENO) {
tio_stdio_orig = tf->tio_orig;
atexit(ttyclose);
}
}
static int
ttyread(struct ttyfd *tf)
{
unsigned char rb;
if (read(tf->fd, &rb, 1) == 1)
return rb;
else
return -1;
}
static void
ttywrite(struct ttyfd *tf, unsigned char wb)
{
(void)write(tf->fd, &wb, 1);
}
static void
rxfifo_reset(struct uart_vdev *uart, int size)
{
char flushbuf[32];
struct fifo *fifo;
ssize_t nread;
int error;
fifo = &uart->rxfifo;
bzero(fifo, sizeof(struct fifo));
fifo->size = size;
if (uart->tty.opened) {
/*
* Flush any unread input from the tty buffer.
*/
while (1) {
nread = read(uart->tty.fd, flushbuf, sizeof(flushbuf));
if (nread != sizeof(flushbuf))
break;
}
/*
* Enable mevent to trigger when new characters are available
* on the tty fd.
*/
error = mevent_enable(uart->mev);
assert(error == 0);
}
}
static int
rxfifo_available(struct uart_vdev *uart)
{
struct fifo *fifo;
fifo = &uart->rxfifo;
return (fifo->num < fifo->size);
}
static int
rxfifo_putchar(struct uart_vdev *uart, uint8_t ch)
{
struct fifo *fifo;
int error;
fifo = &uart->rxfifo;
if (fifo->num < fifo->size) {
fifo->buf[fifo->windex] = ch;
fifo->windex = (fifo->windex + 1) % fifo->size;
fifo->num++;
if (!rxfifo_available(uart)) {
if (uart->tty.opened) {
/*
* Disable mevent callback if the FIFO is full.
*/
error = mevent_disable(uart->mev);
assert(error == 0);
}
}
return 0;
} else
return -1;
}
static int
rxfifo_getchar(struct uart_vdev *uart)
{
struct fifo *fifo;
int c, error, wasfull;
wasfull = 0;
fifo = &uart->rxfifo;
if (fifo->num > 0) {
if (!rxfifo_available(uart))
wasfull = 1;
c = fifo->buf[fifo->rindex];
fifo->rindex = (fifo->rindex + 1) % fifo->size;
fifo->num--;
if (wasfull) {
if (uart->tty.opened) {
error = mevent_enable(uart->mev);
assert(error == 0);
}
}
return c;
} else
return -1;
}
static int
rxfifo_numchars(struct uart_vdev *uart)
{
struct fifo *fifo = &uart->rxfifo;
return fifo->num;
}
static void
uart_opentty(struct uart_vdev *uart)
{
ttyopen(&uart->tty);
uart->mev = mevent_add(uart->tty.fd, EVF_READ, uart_drain, uart);
assert(uart->mev != NULL);
}
static uint8_t
modem_status(uint8_t mcr)
{
uint8_t msr;
if (mcr & MCR_LOOPBACK) {
/*
* In the loopback mode certain bits from the MCR are
* reflected back into MSR.
*/
msr = 0;
if (mcr & MCR_RTS)
msr |= MSR_CTS;
if (mcr & MCR_DTR)
msr |= MSR_DSR;
if (mcr & MCR_OUT1)
msr |= MSR_RI;
if (mcr & MCR_OUT2)
msr |= MSR_DCD;
} else {
/*
* Always assert DCD and DSR so tty open doesn't block
* even if CLOCAL is turned off.
*/
msr = MSR_DCD | MSR_DSR;
}
assert((msr & MSR_DELTA_MASK) == 0);
return msr;
}
/*
* The IIR returns a prioritized interrupt reason:
* - receive data available
* - transmit holding register empty
* - modem status change
*
* Return an interrupt reason if one is available.
*/
static int
uart_intr_reason(struct uart_vdev *uart)
{
if ((uart->lsr & LSR_OE) != 0 && (uart->ier & IER_ERLS) != 0)
return IIR_RLS;
else if (rxfifo_numchars(uart) > 0 && (uart->ier & IER_ERXRDY) != 0)
return IIR_RXTOUT;
else if (uart->thre_int_pending && (uart->ier & IER_ETXRDY) != 0)
return IIR_TXRDY;
else if ((uart->msr & MSR_DELTA_MASK) != 0 &&
(uart->ier & IER_EMSC) != 0)
return IIR_MLSC;
else
return IIR_NOPEND;
}
static void
uart_reset(struct uart_vdev *uart)
{
uint16_t divisor;
divisor = DEFAULT_RCLK / DEFAULT_BAUD / 16;
uart->dll = divisor;
uart->dlh = divisor >> 16;
uart->msr = modem_status(uart->mcr);
rxfifo_reset(uart, 1); /* no fifo until enabled by software */
}
/*
* Toggle the COM port's intr pin depending on whether or not we have an
* interrupt condition to report to the processor.
*/
static void
uart_toggle_intr(struct uart_vdev *uart)
{
uint8_t intr_reason;
intr_reason = uart_intr_reason(uart);
if (intr_reason == IIR_NOPEND)
(*uart->intr_deassert)(uart->arg);
else
(*uart->intr_assert)(uart->arg);
}
static void
uart_drain(int fd, enum ev_type ev, void *arg)
{
struct uart_vdev *uart;
int ch;
uart = arg;
assert(fd == uart->tty.fd);
assert(ev == EVF_READ);
/*
* This routine is called in the context of the mevent thread
* to take out the uart lock to protect against concurrent
* access from a vCPU i/o exit
*/
pthread_mutex_lock(&uart->mtx);
if ((uart->mcr & MCR_LOOPBACK) != 0) {
(void) ttyread(&uart->tty);
} else {
while ((ch = ttyread(&uart->tty)) != -1)
rxfifo_putchar(uart, ch);
uart_toggle_intr(uart);
}
pthread_mutex_unlock(&uart->mtx);
}
void
uart_write(struct uart_vdev *uart, int offset, uint8_t value)
{
int fifosz;
uint8_t msr;
pthread_mutex_lock(&uart->mtx);
/*
* Take care of the special case DLAB accesses first
*/
if ((uart->lcr & LCR_DLAB) != 0) {
if (offset == REG_DLL) {
uart->dll = value;
goto done;
}
if (offset == REG_DLH) {
uart->dlh = value;
goto done;
}
}
switch (offset) {
case REG_DATA:
if (uart->mcr & MCR_LOOPBACK) {
if (rxfifo_putchar(uart, value) != 0)
uart->lsr |= LSR_OE;
} else if (uart->tty.opened) {
ttywrite(&uart->tty, value);
} /* else drop on floor */
uart->thre_int_pending = true;
break;
case REG_IER:
/*
* Apply mask so that bits 4-7 are 0
* Also enables bits 0-3 only if they're 1
*/
uart->ier = value & 0x0F;
break;
case REG_FCR:
/*
* When moving from FIFO and 16450 mode and vice versa,
* the FIFO contents are reset.
*/
if ((uart->fcr & FCR_ENABLE) ^ (value & FCR_ENABLE)) {
fifosz = (value & FCR_ENABLE) ? FIFOSZ : 1;
rxfifo_reset(uart, fifosz);
}
/*
* The FCR_ENABLE bit must be '1' for the programming
* of other FCR bits to be effective.
*/
if ((value & FCR_ENABLE) == 0) {
uart->fcr = 0;
} else {
if ((value & FCR_RCV_RST) != 0)
rxfifo_reset(uart, FIFOSZ);
uart->fcr = value &
(FCR_ENABLE | FCR_DMA | FCR_RX_MASK);
}
break;
case REG_LCR:
uart->lcr = value;
break;
case REG_MCR:
/* Apply mask so that bits 5-7 are 0 */
uart->mcr = value & 0x1F;
msr = modem_status(uart->mcr);
/*
* Detect if there has been any change between the
* previous and the new value of MSR. If there is
* then assert the appropriate MSR delta bit.
*/
if ((msr & MSR_CTS) ^ (uart->msr & MSR_CTS))
uart->msr |= MSR_DCTS;
if ((msr & MSR_DSR) ^ (uart->msr & MSR_DSR))
uart->msr |= MSR_DDSR;
if ((msr & MSR_DCD) ^ (uart->msr & MSR_DCD))
uart->msr |= MSR_DDCD;
if ((uart->msr & MSR_RI) != 0 && (msr & MSR_RI) == 0)
uart->msr |= MSR_TERI;
/*
* Update the value of MSR while retaining the delta
* bits.
*/
uart->msr &= MSR_DELTA_MASK;
uart->msr |= msr;
break;
case REG_LSR:
/*
* Line status register is not meant to be written to
* during normal operation.
*/
break;
case REG_MSR:
/*
* As far as I can tell MSR is a read-only register.
*/
break;
case REG_SCR:
uart->scr = value;
break;
default:
break;
}
done:
uart_toggle_intr(uart);
pthread_mutex_unlock(&uart->mtx);
}
uint8_t
uart_read(struct uart_vdev *uart, int offset)
{
uint8_t iir, intr_reason, reg;
pthread_mutex_lock(&uart->mtx);
/*
* Take care of the special case DLAB accesses first
*/
if ((uart->lcr & LCR_DLAB) != 0) {
if (offset == REG_DLL) {
reg = uart->dll;
goto done;
}
if (offset == REG_DLH) {
reg = uart->dlh;
goto done;
}
}
switch (offset) {
case REG_DATA:
reg = rxfifo_getchar(uart);
break;
case REG_IER:
reg = uart->ier;
break;
case REG_IIR:
iir = (uart->fcr & FCR_ENABLE) ? IIR_FIFO_MASK : 0;
intr_reason = uart_intr_reason(uart);
/*
* Deal with side effects of reading the IIR register
*/
if (intr_reason == IIR_TXRDY)
uart->thre_int_pending = false;
iir |= intr_reason;
reg = iir;
break;
case REG_LCR:
reg = uart->lcr;
break;
case REG_MCR:
reg = uart->mcr;
break;
case REG_LSR:
/* Transmitter is always ready for more data */
uart->lsr |= LSR_TEMT | LSR_THRE;
/* Check for new receive data */
if (rxfifo_numchars(uart) > 0)
uart->lsr |= LSR_RXRDY;
else
uart->lsr &= ~LSR_RXRDY;
reg = uart->lsr;
/* The LSR_OE bit is cleared on LSR read */
uart->lsr &= ~LSR_OE;
break;
case REG_MSR:
/*
* MSR delta bits are cleared on read
*/
reg = uart->msr;
uart->msr &= ~MSR_DELTA_MASK;
break;
case REG_SCR:
reg = uart->scr;
break;
default:
reg = 0xFF;
break;
}
done:
uart_toggle_intr(uart);
pthread_mutex_unlock(&uart->mtx);
return reg;
}
int
uart_legacy_alloc(int which, int *baseaddr, int *irq)
{
if (which < 0 || which >= UART_NLDEVS || uart_lres[which].inuse)
return -1;
uart_lres[which].inuse = true;
*baseaddr = uart_lres[which].baseaddr;
*irq = uart_lres[which].irq;
return 0;
}
void
uart_legacy_dealloc(int which)
{
uart_lres[which].inuse = false;
}
struct uart_vdev *
uart_init(uart_intr_func_t intr_assert, uart_intr_func_t intr_deassert,
void *arg)
{
struct uart_vdev *uart;
uart = calloc(1, sizeof(struct uart_vdev));
assert(uart != NULL);
uart->arg = arg;
uart->intr_assert = intr_assert;
uart->intr_deassert = intr_deassert;
pthread_mutex_init(&uart->mtx, NULL);
uart_reset(uart);
return uart;
}
void
uart_deinit(struct uart_vdev *uart)
{
if (uart) {
if (uart->tty.opened && uart->tty.fd == STDIN_FILENO) {
ttyclose();
stdio_in_use = false;
}
free(uart);
}
}
static int
uart_tty_backend(struct uart_vdev *uart, const char *opts)
{
int fd;
int retval;
retval = -1;
fd = open(opts, O_RDWR | O_NONBLOCK);
if (fd > 0 && isatty(fd)) {
uart->tty.fd = fd;
uart->tty.opened = true;
retval = 0;
}
return retval;
}
int
uart_set_backend(struct uart_vdev *uart, const char *opts)
{
int retval;
retval = -1;
if (opts == NULL)
return 0;
if (strcmp("stdio", opts) == 0) {
if (!stdio_in_use) {
uart->tty.fd = STDIN_FILENO;
uart->tty.opened = true;
stdio_in_use = true;
retval = 0;
}
} else if (uart_tty_backend(uart, opts) == 0) {
retval = 0;
}
/* Make the backend file descriptor non-blocking */
if (retval == 0)
retval = fcntl(uart->tty.fd, F_SETFL, O_NONBLOCK);
if (retval == 0)
uart_opentty(uart);
return retval;
}

View File

@@ -0,0 +1,73 @@
/*-
* 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>
#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_core.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;
}

View File

@@ -0,0 +1,816 @@
/*-
* 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>
#include <sys/time.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "types.h"
#include "usb.h"
#include "usbdi.h"
#include "usb_core.h"
#include "console.h"
#include "gc.h"
static int umouse_debug;
#define DPRINTF(params) do { if (umouse_debug) printf params; } while (0)
#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
enum {
UMSTR_LANG,
UMSTR_MANUFACTURER,
UMSTR_PRODUCT,
UMSTR_SERIAL,
UMSTR_CONFIG,
UMSTR_MAX
};
static const char *const umouse_desc_strings[] = {
"\x04\x09",
"ACRN-DM",
"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];
} __attribute__((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;
} __attribute__((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 */
} __attribute__((packed));
static struct usb_device_descriptor umouse_dev_desc = {
.bLength = sizeof(umouse_dev_desc),
.bDescriptorType = UDESC_DEVICE,
.bcdUSB = UD_USB_3_0,
.bMaxPacketSize = 8, /* max packet size */
.idVendor = 0xFB5D, /* vendor */
.idProduct = 0x0001,/* product */
.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 = 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 = 8,
.bInterval = 0xA,
},
.sscompd = {
.bLength = sizeof(umouse_confd.sscompd),
.bDescriptorType = UDESC_ENDPOINT_SS_COMP,
.bMaxBurst = 0,
.bmAttributes = 0,
.wBytesPerInterval = 0,
},
};
struct umouse_bos_desc {
struct usb_bos_descriptor bosd;
struct usb_devcap_ss_descriptor usbssd;
} __attribute__((packed));
struct umouse_bos_desc umouse_bosd = {
.bosd = {
.bLength = sizeof(umouse_bosd.bosd),
.bDescriptorType = UDESC_BOS,
.wTotalLength = sizeof(umouse_bosd),
.bNumDeviceCaps = 1,
},
.usbssd = {
.bLength = sizeof(umouse_bosd.usbssd),
.bDescriptorType = UDESC_DEVICE_CAPABILITY,
.bDevCapabilityType = 3,
.bmAttributes = 0,
.wSpeedsSupported = 0x08,
.bFunctionalitySupport = 3,
.bU1DevExitLat = 0xa, /* dummy - not used */
.wU2DevExitLat = 0x20,
}
};
struct umouse_vdev {
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_vdev *dev;
struct gfx_ctx_image *gc;
gc = console_get_image();
if (gc == NULL) {
/* not ready */
return;
}
dev = arg;
pthread_mutex_lock(&dev->mtx);
dev->um_report.buttons = 0;
dev->um_report.z = 0;
if (button & 0x01)
dev->um_report.buttons |= 0x01; /* left */
if (button & 0x02)
dev->um_report.buttons |= 0x04; /* middle */
if (button & 0x04)
dev->um_report.buttons |= 0x02; /* right */
if (button & 0x8)
dev->um_report.z = 1;
if (button & 0x10)
dev->um_report.z = -1;
/* scale coords to mouse resolution */
dev->um_report.x = MOUSE_MAX_X * x / gc->width;
dev->um_report.y = MOUSE_MAX_Y * y / gc->height;
dev->newdata = 1;
pthread_mutex_unlock(&dev->mtx);
pthread_mutex_lock(&dev->ev_mtx);
dev->hci->hci_intr(dev->hci, UE_DIR_IN | UMOUSE_INTR_ENDPT);
pthread_mutex_unlock(&dev->ev_mtx);
}
static void *
umouse_init(struct usb_hci *hci, char *opt)
{
struct umouse_vdev *dev;
dev = calloc(1, sizeof(struct umouse_vdev));
if (!dev) {
WPRINTF(("umouse: calloc returns NULL\n"));
return NULL;
}
dev->hci = hci;
dev->hid.protocol = 1; /* REPORT protocol */
dev->opt = strdup(opt);
pthread_mutex_init(&dev->mtx, NULL);
pthread_mutex_init(&dev->ev_mtx, NULL);
console_ptr_register(umouse_event, dev, 10);
return dev;
}
#define UREQ(x, y) ((x) | ((y) << 8))
static int
umouse_request(void *scarg, struct usb_data_xfer *xfer)
{
struct umouse_vdev *dev;
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;
dev = scarg;
data = NULL;
udata = NULL;
assert(xfer != NULL && xfer->head >= 0);
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(("%s: port %d\r\n", __func__, dev->hci->hci_port));
goto done;
}
value = xfer->ureq->wValue;
index = xfer->ureq->wIndex;
len = xfer->ureq->wLength;
DPRINTF(("%s: port %d, type 0x%x, req 0x%x,"
"val 0x%x, idx 0x%x, len %u\r\n", __func__,
dev->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)
break;
if (data != NULL && len > 1) {
if (dev->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)
break;
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)
dev->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)
dev->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(dev->um_report)) {
/* TODO read from backend */
if (len > sizeof(dev->um_report)) {
data->blen = len - sizeof(dev->um_report);
len = sizeof(dev->um_report);
} else
data->blen = 0;
memcpy(data->buf, &dev->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)
break;
if (data != NULL && len > 0) {
*udata = dev->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)
break;
if (data != NULL && len > 0) {
*udata = dev->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):
dev->hid.idle = xfer->ureq->wValue >> 8;
DPRINTF(("umouse: (UMOUSE_SET_IDLE,"
"UT_WRITE_CLASS_INTERFACE) %x\r\n",
dev->hid.idle));
break;
case UREQ(UMOUSE_SET_PROTOCOL, UT_WRITE_CLASS_INTERFACE):
dev->hid.protocol = xfer->ureq->wValue >> 8;
DPRINTF(("umouse: (UR_CLEAR_FEATURE,"
"UT_WRITE_CLASS_INTERFACE) %x\r\n",
dev->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_vdev *dev;
struct usb_data_xfer_block *data;
uint8_t *udata;
int len, i, idx;
int err;
assert(xfer != NULL && xfer->head >= 0);
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;
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;
}
dev = scarg;
if (dir) {
pthread_mutex_lock(&dev->mtx);
if (!dev->newdata) {
err = USB_ERR_CANCELLED;
USB_DATA_SET_ERRCODE(&xfer->data[xfer->head], USB_NAK);
pthread_mutex_unlock(&dev->mtx);
goto done;
}
if (dev->polling) {
err = USB_ERR_STALLED;
USB_DATA_SET_ERRCODE(data, USB_STALL);
pthread_mutex_unlock(&dev->mtx);
goto done;
}
dev->polling = 1;
if (len > 0) {
dev->newdata = 0;
data->processed = 1;
data->bdone += 6;
memcpy(udata, &dev->um_report, 6);
data->blen = len - 6;
if (data->blen > 0)
err = USB_ERR_SHORT_XFER;
}
dev->polling = 0;
pthread_mutex_unlock(&dev->mtx);
} else {
USB_DATA_SET_ERRCODE(data, USB_STALL);
err = USB_ERR_STALLED;
}
done:
return err;
}
static int
umouse_reset(void *scarg)
{
struct umouse_vdev *dev;
dev = scarg;
dev->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);