c8edc59b40
nearly identical to wintel/ia32, with a couple of tweaks. Since it is so similar to ia32, it is optionally added to a i386 kernel. This port is preliminary, but seems to work well. Further improvements will improve the interaction with syscons(4), port Linux nforce driver and future versions of the xbox. This supports the 64MB and 128MB boxes. You'll need the most recent CVS version of Cromwell (the Linux BIOS for the XBOX) to boot. Rink will be maintaining this port, and is interested in feedback. He's setup a website http://xbox-bsd.nl to report the latest developments. Any silly mistakes are my fault. Submitted by: Rink P.W. Springer rink at stack dot nl and Ed Schouten ed at fxq dot nl
399 lines
10 KiB
C
399 lines
10 KiB
C
/*-
|
|
* Copyright (c) 2005 Rink Springer
|
|
* 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.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* This will handles video output using the XBOX' frame buffer. It assumes
|
|
* the graphics have been set up by Cromwell. This driver uses all video memory
|
|
* to avoid expensive memcpy()'s.
|
|
*
|
|
* It is usuable as console (to see the initial boot) as well as for interactive
|
|
* use. The latter is handeled using kbd_*() functionality. Keyboard hotplug is
|
|
* fully supported, the console will periodically rescan if no keyboard was
|
|
* found.
|
|
*
|
|
*/
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <vm/vm_param.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/cons.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/consio.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/kbio.h>
|
|
#include <sys/fbio.h>
|
|
#include <dev/kbd/kbdreg.h>
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <machine/bus.h>
|
|
#include <machine/xbox.h>
|
|
#include <dev/fb/fbreg.h>
|
|
#include <dev/fb/gfb.h>
|
|
|
|
#define SCREEN_WIDTH 640
|
|
#define SCREEN_HEIGHT 480
|
|
#define SCREEN_BPP 4
|
|
#define SCREEN_SIZE (SCREEN_WIDTH*SCREEN_HEIGHT*SCREEN_BPP)
|
|
|
|
/* FONT_xxx declares the dimensions of the charachter structure, CHAR_xxx is how
|
|
* they appear on-screen. Having slightly more spacing improves readability. */
|
|
#define FONT_HEIGHT 16
|
|
#define FONT_WIDTH 8
|
|
|
|
#define CHAR_HEIGHT 16
|
|
#define CHAR_WIDTH 10
|
|
|
|
#define RAM_SIZE (arch_i386_xbox_memsize * 1024 * 1024)
|
|
#define FB_SIZE (0x400000)
|
|
#define FB_START (0xf0000000 | (RAM_SIZE - FB_SIZE))
|
|
#define FB_START_PTR (0xFD600800)
|
|
|
|
/* colours */
|
|
#define CONSOLE_COL 0xFF88FF88 /* greenish */
|
|
#define NORM_COL 0xFFAAAAAA /* grayish */
|
|
#define BLACK_COL 0x00000000 /* black */
|
|
|
|
static int xcon_x = 0;
|
|
static int xcon_y = 0;
|
|
static int xcon_yoffs = 0;
|
|
|
|
extern struct gfb_font bold8x16;
|
|
|
|
static char* xcon_map;
|
|
static int* xcon_memstartptr;
|
|
|
|
static struct tty* xboxfb_tp = NULL;
|
|
static struct keyboard* xbfb_kbd = NULL;
|
|
static int xbfb_keyboard = -1;
|
|
static d_open_t xboxfb_dev_open;
|
|
static d_close_t xboxfb_dev_close;
|
|
static int xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg);
|
|
|
|
static struct cdevsw xboxfb_cdevsw = {
|
|
.d_version = D_VERSION,
|
|
.d_open = xboxfb_dev_open,
|
|
.d_close = xboxfb_dev_close,
|
|
.d_name = "xboxfb",
|
|
.d_flags = D_TTY | D_NEEDGIANT,
|
|
};
|
|
|
|
static void
|
|
xcon_probe(struct consdev* cp)
|
|
{
|
|
if (arch_i386_is_xbox)
|
|
cp->cn_pri = CN_REMOTE;
|
|
else
|
|
cp->cn_pri = CN_DEAD;
|
|
}
|
|
|
|
static int
|
|
xcon_getc(struct consdev* cp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xcon_checkc(struct consdev* cp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
xcon_real_putc(int basecol, int c)
|
|
{
|
|
int i, j, ch = c, col;
|
|
char mask;
|
|
int* ptri = (int*)xcon_map;
|
|
|
|
/* special control chars */
|
|
switch (ch) {
|
|
case '\r': /* carriage return */
|
|
xcon_x = 0;
|
|
return;
|
|
case '\n': /* newline */
|
|
xcon_y += CHAR_HEIGHT;
|
|
goto scroll;
|
|
case 7: /* beep */
|
|
return;
|
|
case 8: /* backspace */
|
|
if (xcon_x > 0) {
|
|
xcon_x -= CHAR_WIDTH;
|
|
} else {
|
|
if (xcon_y > CHAR_HEIGHT) {
|
|
xcon_y -= CHAR_HEIGHT;
|
|
xcon_x = (SCREEN_WIDTH - CHAR_WIDTH);
|
|
}
|
|
}
|
|
return;
|
|
case 9: /* tab */
|
|
xcon_real_putc (basecol, ' ');
|
|
while ((xcon_x % (8 * CHAR_WIDTH)) != 0) {
|
|
xcon_real_putc (basecol, ' ');
|
|
}
|
|
return;
|
|
}
|
|
ptri += (xcon_y * SCREEN_WIDTH) + xcon_x;
|
|
|
|
/* we plot the font pixel-by-pixel. bit 7 is skipped as it renders the
|
|
* console unreadable ... */
|
|
for (i = 0; i < FONT_HEIGHT; i++) {
|
|
mask = 0x40;
|
|
for (j = 0; j < FONT_WIDTH; j++) {
|
|
col = (bold8x16.data[(ch * FONT_HEIGHT) + i] & mask) ? basecol : BLACK_COL;
|
|
*ptri++ = col;
|
|
mask >>= 1;
|
|
}
|
|
ptri += (SCREEN_WIDTH - FONT_WIDTH);
|
|
}
|
|
|
|
xcon_x += CHAR_WIDTH;
|
|
if (xcon_x >= SCREEN_WIDTH) {
|
|
xcon_x = 0;
|
|
xcon_y += CHAR_HEIGHT;
|
|
}
|
|
|
|
scroll:
|
|
if (((xcon_yoffs + CHAR_HEIGHT) * SCREEN_WIDTH * SCREEN_BPP) > (FB_SIZE - SCREEN_SIZE)) {
|
|
/* we are about to run out of video memory, so move everything
|
|
* back to the beginning of the video memory */
|
|
memcpy ((char*)xcon_map,
|
|
(char*)(xcon_map + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP)),
|
|
SCREEN_SIZE);
|
|
xcon_y -= xcon_yoffs; xcon_yoffs = 0;
|
|
*xcon_memstartptr = FB_START;
|
|
}
|
|
|
|
/* we achieve much faster scrolling by just altering the video memory
|
|
* address base. once all memory is used, we return to the beginning
|
|
* again */
|
|
while ((xcon_y - xcon_yoffs) >= SCREEN_HEIGHT) {
|
|
xcon_yoffs += CHAR_HEIGHT;
|
|
memset ((char*)(xcon_map + (xcon_y * SCREEN_WIDTH * SCREEN_BPP)), 0, CHAR_HEIGHT * SCREEN_WIDTH * SCREEN_BPP);
|
|
*xcon_memstartptr = FB_START + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP);
|
|
}
|
|
}
|
|
|
|
static void
|
|
xcon_putc(struct consdev* cp, int c)
|
|
{
|
|
xcon_real_putc (CONSOLE_COL, c);
|
|
}
|
|
|
|
static void
|
|
xcon_init(struct consdev* cp)
|
|
{
|
|
int i;
|
|
int* iptr;
|
|
|
|
/* Don't init the framebuffer on non-XBOX-es */
|
|
if (!arch_i386_is_xbox)
|
|
return;
|
|
|
|
/*
|
|
* We must make a mapping from video framebuffer memory to real. This is
|
|
* very crude: we map the entire videomemory to PAGE_SIZE! Since our
|
|
* kernel lives at it's relocated address range (0xc0xxxxxx), it won't
|
|
* care.
|
|
*
|
|
* We use address PAGE_SIZE and up so we can still trap NULL pointers.
|
|
* Once xboxfb_drvinit() is called, the mapping will be done via the OS
|
|
* and stored in a more sensible location ... but since we're not fully
|
|
* initialized, this is our only way to go :-(
|
|
*/
|
|
for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) {
|
|
pmap_kenter (((i + 1) * PAGE_SIZE), FB_START + (i * PAGE_SIZE));
|
|
}
|
|
pmap_kenter ((i + 1) * PAGE_SIZE, FB_START_PTR - FB_START_PTR % PAGE_SIZE);
|
|
xcon_map = (char*)PAGE_SIZE;
|
|
xcon_memstartptr = (int*)((i + 1) * PAGE_SIZE + FB_START_PTR % PAGE_SIZE);
|
|
|
|
/* clear the screen */
|
|
iptr = (int*)xcon_map;
|
|
for (i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++)
|
|
*iptr++ = BLACK_COL;
|
|
|
|
sprintf(cp->cn_name, "xboxfb");
|
|
cp->cn_tp = xboxfb_tp;
|
|
}
|
|
|
|
static void
|
|
xboxfb_timer(void* arg)
|
|
{
|
|
int i;
|
|
|
|
if (xbfb_kbd != NULL)
|
|
return;
|
|
|
|
i = kbd_allocate ("*", 0, (void*)&xbfb_keyboard, xboxfb_kbdevent, NULL);
|
|
if (i != -1) {
|
|
/* allocation was successfull; xboxfb_kbdevent() is called to
|
|
* feed the keystrokes to the tty driver */
|
|
xbfb_kbd = kbd_get_keyboard (i);
|
|
xbfb_keyboard = i;
|
|
return;
|
|
}
|
|
|
|
/* probe again in a few */
|
|
timeout (xboxfb_timer, NULL, hz / 10);
|
|
}
|
|
|
|
static int
|
|
xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg)
|
|
{
|
|
int c;
|
|
|
|
if (event == KBDIO_UNLOADING) {
|
|
/* keyboard was unplugged; clean up and enable probing */
|
|
xbfb_kbd = NULL;
|
|
xbfb_keyboard = -1;
|
|
kbd_release (thiskbd, (void*)&xbfb_keyboard);
|
|
timeout (xboxfb_timer, NULL, hz / 10);
|
|
return 0;
|
|
}
|
|
|
|
for (;;) {
|
|
c = (kbdsw[xbfb_kbd->kb_index])->read_char (xbfb_kbd, 0);
|
|
if (c == NOKEY)
|
|
return 0;
|
|
|
|
/* only feed non-special keys to an open console */
|
|
if (c != ERRKEY) {
|
|
if ((KEYFLAGS(c)) == 0x0)
|
|
if (xboxfb_tp->t_state & TS_ISOPEN)
|
|
ttyld_rint (xboxfb_tp, KEYCHAR(c));
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
xboxfb_drvinit (void* unused)
|
|
{
|
|
struct cdev* dev;
|
|
int i;
|
|
|
|
/* Don't init the framebuffer on non-XBOX-es */
|
|
if (!arch_i386_is_xbox)
|
|
return;
|
|
|
|
/*
|
|
* When this function is called, the OS is capable of doing
|
|
* device-memory mappings using pmap_mapdev(). Therefore, we ditch the
|
|
* ugly PAGE_SIZE-based mapping and ask the OS to create a decent
|
|
* mapping for us.
|
|
*/
|
|
dev = make_dev (&xboxfb_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", "xboxfb");
|
|
xcon_map = pmap_mapdev (FB_START, FB_SIZE);
|
|
xcon_memstartptr = (int*)pmap_mapdev (FB_START_PTR, PAGE_SIZE);
|
|
*xcon_memstartptr = FB_START;
|
|
|
|
/* ditch all ugly previous mappings */
|
|
for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) {
|
|
pmap_kremove (((i + 1) * PAGE_SIZE));
|
|
}
|
|
pmap_kremove (PAGE_SIZE + FB_SIZE);
|
|
|
|
/* probe for a keyboard */
|
|
xboxfb_timer (NULL);
|
|
}
|
|
|
|
static void
|
|
xboxfb_tty_start(struct tty* tp)
|
|
{
|
|
struct clist* cl;
|
|
int len, i;
|
|
u_char buf[128];
|
|
|
|
if (tp->t_state & TS_BUSY)
|
|
return;
|
|
|
|
/* simply feed all outstanding tty data to real_putc() */
|
|
tp->t_state |= TS_BUSY;
|
|
cl = &tp->t_outq;
|
|
len = q_to_b(cl, buf, 128);
|
|
for (i = 0; i < len; i++)
|
|
xcon_real_putc(NORM_COL, buf[i]);
|
|
tp->t_state &= ~TS_BUSY;
|
|
}
|
|
|
|
static void
|
|
xboxfb_tty_stop(struct tty* tp, int flag) {
|
|
if (tp->t_state & TS_BUSY)
|
|
if ((tp->t_state & TS_TTSTOP) == 0)
|
|
tp->t_state |= TS_FLUSH;
|
|
}
|
|
|
|
static int
|
|
xboxfb_tty_param(struct tty* tp, struct termios* t)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xboxfb_dev_open(struct cdev* dev, int flag, int mode, struct thread* td)
|
|
{
|
|
struct tty* tp;
|
|
|
|
tp = xboxfb_tp = dev->si_tty = ttymalloc (xboxfb_tp);
|
|
|
|
tp->t_oproc = xboxfb_tty_start;
|
|
tp->t_param = xboxfb_tty_param;
|
|
tp->t_stop = xboxfb_tty_stop;
|
|
tp->t_dev = dev;
|
|
|
|
if ((tp->t_state & TS_ISOPEN) == 0) {
|
|
tp->t_state |= TS_CARR_ON;
|
|
ttychars(tp);
|
|
tp->t_iflag = TTYDEF_IFLAG;
|
|
tp->t_oflag = TTYDEF_OFLAG;
|
|
tp->t_cflag = TTYDEF_CFLAG | CLOCAL;
|
|
tp->t_lflag = TTYDEF_LFLAG;
|
|
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
|
|
ttsetwater(tp);
|
|
}
|
|
|
|
return ttyld_open (tp, dev);
|
|
}
|
|
|
|
static int
|
|
xboxfb_dev_close(struct cdev* dev, int flag, int mode, struct thread* td)
|
|
{
|
|
struct tty* tp;
|
|
|
|
tp = xboxfb_tp;
|
|
ttyld_close (tp, flag);
|
|
tty_close(tp);
|
|
return 0;
|
|
}
|
|
|
|
SYSINIT(xboxfbdev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, xboxfb_drvinit, NULL)
|
|
|
|
CONS_DRIVER(xcon, xcon_probe, xcon_init, NULL, xcon_getc, xcon_checkc, xcon_putc, NULL);
|