mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2026-06-07 09:41:30 +00:00
initial import
internal commit: 0ab1ea615e5cfbb0687a9d593a86a7b774386076 Signed-off-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
572
devicemodel/hw/platform/atkbdc.c
Normal file
572
devicemodel/hw/platform/atkbdc.c
Normal 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);
|
||||
936
devicemodel/hw/platform/block_if.c
Normal file
936
devicemodel/hw/platform/block_if.c
Normal 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;
|
||||
}
|
||||
107
devicemodel/hw/platform/cmos_io.c
Normal file
107
devicemodel/hw/platform/cmos_io.c
Normal 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);
|
||||
74
devicemodel/hw/platform/ioapic.c
Normal file
74
devicemodel/hw/platform/ioapic.c
Normal 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));
|
||||
}
|
||||
305
devicemodel/hw/platform/pm.c
Normal file
305
devicemodel/hw/platform/pm.c
Normal 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);
|
||||
}
|
||||
477
devicemodel/hw/platform/ps2kbd.c
Normal file
477
devicemodel/hw/platform/ps2kbd.c
Normal 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;
|
||||
}
|
||||
412
devicemodel/hw/platform/ps2mouse.c
Normal file
412
devicemodel/hw/platform/ps2mouse.c
Normal 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;
|
||||
}
|
||||
1163
devicemodel/hw/platform/rtc.c
Normal file
1163
devicemodel/hw/platform/rtc.c
Normal file
File diff suppressed because it is too large
Load Diff
687
devicemodel/hw/platform/uart_core.c
Normal file
687
devicemodel/hw/platform/uart_core.c
Normal 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;
|
||||
}
|
||||
73
devicemodel/hw/platform/usb_core.c
Normal file
73
devicemodel/hw/platform/usb_core.c
Normal 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;
|
||||
}
|
||||
816
devicemodel/hw/platform/usb_mouse.c
Normal file
816
devicemodel/hw/platform/usb_mouse.c
Normal 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);
|
||||
Reference in New Issue
Block a user