freebsd-dev/sys/i386/xbox/xboxfb.c
2006-01-04 09:46:20 +00:00

400 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);
xboxfb_tp = ttyalloc();
}
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 = dev->si_tty = 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);