xboxfb.c revision 152219
1/*- 2 * Copyright (c) 2005 Rink Springer 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/i386/xbox/xboxfb.c 152219 2005-11-09 03:55:40Z imp $ 28 */ 29 30/* 31 * This will handles video output using the XBOX' frame buffer. It assumes 32 * the graphics have been set up by Cromwell. This driver uses all video memory 33 * to avoid expensive memcpy()'s. 34 * 35 * It is usuable as console (to see the initial boot) as well as for interactive 36 * use. The latter is handeled using kbd_*() functionality. Keyboard hotplug is 37 * fully supported, the console will periodically rescan if no keyboard was 38 * found. 39 * 40 */ 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <vm/vm_param.h> 44#include <sys/kernel.h> 45#include <sys/cons.h> 46#include <sys/conf.h> 47#include <sys/consio.h> 48#include <sys/tty.h> 49#include <sys/kbio.h> 50#include <sys/fbio.h> 51#include <dev/kbd/kbdreg.h> 52#include <vm/vm.h> 53#include <vm/pmap.h> 54#include <machine/bus.h> 55#include <machine/xbox.h> 56#include <dev/fb/fbreg.h> 57#include <dev/fb/gfb.h> 58 59#define SCREEN_WIDTH 640 60#define SCREEN_HEIGHT 480 61#define SCREEN_BPP 4 62#define SCREEN_SIZE (SCREEN_WIDTH*SCREEN_HEIGHT*SCREEN_BPP) 63 64/* FONT_xxx declares the dimensions of the charachter structure, CHAR_xxx is how 65 * they appear on-screen. Having slightly more spacing improves readability. */ 66#define FONT_HEIGHT 16 67#define FONT_WIDTH 8 68 69#define CHAR_HEIGHT 16 70#define CHAR_WIDTH 10 71 72#define RAM_SIZE (arch_i386_xbox_memsize * 1024 * 1024) 73#define FB_SIZE (0x400000) 74#define FB_START (0xf0000000 | (RAM_SIZE - FB_SIZE)) 75#define FB_START_PTR (0xFD600800) 76 77/* colours */ 78#define CONSOLE_COL 0xFF88FF88 /* greenish */ 79#define NORM_COL 0xFFAAAAAA /* grayish */ 80#define BLACK_COL 0x00000000 /* black */ 81 82static int xcon_x = 0; 83static int xcon_y = 0; 84static int xcon_yoffs = 0; 85 86extern struct gfb_font bold8x16; 87 88static char* xcon_map; 89static int* xcon_memstartptr; 90 91static struct tty* xboxfb_tp = NULL; 92static struct keyboard* xbfb_kbd = NULL; 93static int xbfb_keyboard = -1; 94static d_open_t xboxfb_dev_open; 95static d_close_t xboxfb_dev_close; 96static int xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg); 97 98static struct cdevsw xboxfb_cdevsw = { 99 .d_version = D_VERSION, 100 .d_open = xboxfb_dev_open, 101 .d_close = xboxfb_dev_close, 102 .d_name = "xboxfb", 103 .d_flags = D_TTY | D_NEEDGIANT, 104}; 105 106static void 107xcon_probe(struct consdev* cp) 108{ 109 if (arch_i386_is_xbox) 110 cp->cn_pri = CN_REMOTE; 111 else 112 cp->cn_pri = CN_DEAD; 113} 114 115static int 116xcon_getc(struct consdev* cp) 117{ 118 return 0; 119} 120 121static int 122xcon_checkc(struct consdev* cp) 123{ 124 return 0; 125} 126 127static void 128xcon_real_putc(int basecol, int c) 129{ 130 int i, j, ch = c, col; 131 char mask; 132 int* ptri = (int*)xcon_map; 133 134 /* special control chars */ 135 switch (ch) { 136 case '\r': /* carriage return */ 137 xcon_x = 0; 138 return; 139 case '\n': /* newline */ 140 xcon_y += CHAR_HEIGHT; 141 goto scroll; 142 case 7: /* beep */ 143 return; 144 case 8: /* backspace */ 145 if (xcon_x > 0) { 146 xcon_x -= CHAR_WIDTH; 147 } else { 148 if (xcon_y > CHAR_HEIGHT) { 149 xcon_y -= CHAR_HEIGHT; 150 xcon_x = (SCREEN_WIDTH - CHAR_WIDTH); 151 } 152 } 153 return; 154 case 9: /* tab */ 155 xcon_real_putc (basecol, ' '); 156 while ((xcon_x % (8 * CHAR_WIDTH)) != 0) { 157 xcon_real_putc (basecol, ' '); 158 } 159 return; 160 } 161 ptri += (xcon_y * SCREEN_WIDTH) + xcon_x; 162 163 /* we plot the font pixel-by-pixel. bit 7 is skipped as it renders the 164 * console unreadable ... */ 165 for (i = 0; i < FONT_HEIGHT; i++) { 166 mask = 0x40; 167 for (j = 0; j < FONT_WIDTH; j++) { 168 col = (bold8x16.data[(ch * FONT_HEIGHT) + i] & mask) ? basecol : BLACK_COL; 169 *ptri++ = col; 170 mask >>= 1; 171 } 172 ptri += (SCREEN_WIDTH - FONT_WIDTH); 173 } 174 175 xcon_x += CHAR_WIDTH; 176 if (xcon_x >= SCREEN_WIDTH) { 177 xcon_x = 0; 178 xcon_y += CHAR_HEIGHT; 179 } 180 181scroll: 182 if (((xcon_yoffs + CHAR_HEIGHT) * SCREEN_WIDTH * SCREEN_BPP) > (FB_SIZE - SCREEN_SIZE)) { 183 /* we are about to run out of video memory, so move everything 184 * back to the beginning of the video memory */ 185 memcpy ((char*)xcon_map, 186 (char*)(xcon_map + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP)), 187 SCREEN_SIZE); 188 xcon_y -= xcon_yoffs; xcon_yoffs = 0; 189 *xcon_memstartptr = FB_START; 190 } 191 192 /* we achieve much faster scrolling by just altering the video memory 193 * address base. once all memory is used, we return to the beginning 194 * again */ 195 while ((xcon_y - xcon_yoffs) >= SCREEN_HEIGHT) { 196 xcon_yoffs += CHAR_HEIGHT; 197 memset ((char*)(xcon_map + (xcon_y * SCREEN_WIDTH * SCREEN_BPP)), 0, CHAR_HEIGHT * SCREEN_WIDTH * SCREEN_BPP); 198 *xcon_memstartptr = FB_START + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP); 199 } 200} 201 202static void 203xcon_putc(struct consdev* cp, int c) 204{ 205 xcon_real_putc (CONSOLE_COL, c); 206} 207 208static void 209xcon_init(struct consdev* cp) 210{ 211 int i; 212 int* iptr; 213 214 /* Don't init the framebuffer on non-XBOX-es */ 215 if (!arch_i386_is_xbox) 216 return; 217 218 /* 219 * We must make a mapping from video framebuffer memory to real. This is 220 * very crude: we map the entire videomemory to PAGE_SIZE! Since our 221 * kernel lives at it's relocated address range (0xc0xxxxxx), it won't 222 * care. 223 * 224 * We use address PAGE_SIZE and up so we can still trap NULL pointers. 225 * Once xboxfb_drvinit() is called, the mapping will be done via the OS 226 * and stored in a more sensible location ... but since we're not fully 227 * initialized, this is our only way to go :-( 228 */ 229 for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) { 230 pmap_kenter (((i + 1) * PAGE_SIZE), FB_START + (i * PAGE_SIZE)); 231 } 232 pmap_kenter ((i + 1) * PAGE_SIZE, FB_START_PTR - FB_START_PTR % PAGE_SIZE); 233 xcon_map = (char*)PAGE_SIZE; 234 xcon_memstartptr = (int*)((i + 1) * PAGE_SIZE + FB_START_PTR % PAGE_SIZE); 235 236 /* clear the screen */ 237 iptr = (int*)xcon_map; 238 for (i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++) 239 *iptr++ = BLACK_COL; 240 241 sprintf(cp->cn_name, "xboxfb"); 242 cp->cn_tp = xboxfb_tp; 243} 244 245static void 246xboxfb_timer(void* arg) 247{ 248 int i; 249 250 if (xbfb_kbd != NULL) 251 return; 252 253 i = kbd_allocate ("*", 0, (void*)&xbfb_keyboard, xboxfb_kbdevent, NULL); 254 if (i != -1) { 255 /* allocation was successfull; xboxfb_kbdevent() is called to 256 * feed the keystrokes to the tty driver */ 257 xbfb_kbd = kbd_get_keyboard (i); 258 xbfb_keyboard = i; 259 return; 260 } 261 262 /* probe again in a few */ 263 timeout (xboxfb_timer, NULL, hz / 10); 264} 265 266static int 267xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg) 268{ 269 int c; 270 271 if (event == KBDIO_UNLOADING) { 272 /* keyboard was unplugged; clean up and enable probing */ 273 xbfb_kbd = NULL; 274 xbfb_keyboard = -1; 275 kbd_release (thiskbd, (void*)&xbfb_keyboard); 276 timeout (xboxfb_timer, NULL, hz / 10); 277 return 0; 278 } 279 280 for (;;) { 281 c = (kbdsw[xbfb_kbd->kb_index])->read_char (xbfb_kbd, 0); 282 if (c == NOKEY) 283 return 0; 284 285 /* only feed non-special keys to an open console */ 286 if (c != ERRKEY) { 287 if ((KEYFLAGS(c)) == 0x0) 288 if (xboxfb_tp->t_state & TS_ISOPEN) 289 ttyld_rint (xboxfb_tp, KEYCHAR(c)); 290 } 291 } 292 293 return 0; 294} 295 296static void 297xboxfb_drvinit (void* unused) 298{ 299 struct cdev* dev; 300 int i; 301 302 /* Don't init the framebuffer on non-XBOX-es */ 303 if (!arch_i386_is_xbox) 304 return; 305 306 /* 307 * When this function is called, the OS is capable of doing 308 * device-memory mappings using pmap_mapdev(). Therefore, we ditch the 309 * ugly PAGE_SIZE-based mapping and ask the OS to create a decent 310 * mapping for us. 311 */ 312 dev = make_dev (&xboxfb_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", "xboxfb"); 313 xcon_map = pmap_mapdev (FB_START, FB_SIZE); 314 xcon_memstartptr = (int*)pmap_mapdev (FB_START_PTR, PAGE_SIZE); 315 *xcon_memstartptr = FB_START; 316 317 /* ditch all ugly previous mappings */ 318 for (i = 0; i < (FB_SIZE / PAGE_SIZE); i++) { 319 pmap_kremove (((i + 1) * PAGE_SIZE)); 320 } 321 pmap_kremove (PAGE_SIZE + FB_SIZE); 322 323 /* probe for a keyboard */ 324 xboxfb_timer (NULL); 325} 326 327static void 328xboxfb_tty_start(struct tty* tp) 329{ 330 struct clist* cl; 331 int len, i; 332 u_char buf[128]; 333 334 if (tp->t_state & TS_BUSY) 335 return; 336 337 /* simply feed all outstanding tty data to real_putc() */ 338 tp->t_state |= TS_BUSY; 339 cl = &tp->t_outq; 340 len = q_to_b(cl, buf, 128); 341 for (i = 0; i < len; i++) 342 xcon_real_putc(NORM_COL, buf[i]); 343 tp->t_state &= ~TS_BUSY; 344} 345 346static void 347xboxfb_tty_stop(struct tty* tp, int flag) { 348 if (tp->t_state & TS_BUSY) 349 if ((tp->t_state & TS_TTSTOP) == 0) 350 tp->t_state |= TS_FLUSH; 351} 352 353static int 354xboxfb_tty_param(struct tty* tp, struct termios* t) 355{ 356 return 0; 357} 358 359static int 360xboxfb_dev_open(struct cdev* dev, int flag, int mode, struct thread* td) 361{ 362 struct tty* tp; 363 364 tp = xboxfb_tp = dev->si_tty = ttymalloc (xboxfb_tp); 365 366 tp->t_oproc = xboxfb_tty_start; 367 tp->t_param = xboxfb_tty_param; 368 tp->t_stop = xboxfb_tty_stop; 369 tp->t_dev = dev; 370 371 if ((tp->t_state & TS_ISOPEN) == 0) { 372 tp->t_state |= TS_CARR_ON; 373 ttychars(tp); 374 tp->t_iflag = TTYDEF_IFLAG; 375 tp->t_oflag = TTYDEF_OFLAG; 376 tp->t_cflag = TTYDEF_CFLAG | CLOCAL; 377 tp->t_lflag = TTYDEF_LFLAG; 378 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 379 ttsetwater(tp); 380 } 381 382 return ttyld_open (tp, dev); 383} 384 385static int 386xboxfb_dev_close(struct cdev* dev, int flag, int mode, struct thread* td) 387{ 388 struct tty* tp; 389 390 tp = xboxfb_tp; 391 ttyld_close (tp, flag); 392 tty_close(tp); 393 return 0; 394} 395 396SYSINIT(xboxfbdev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, xboxfb_drvinit, NULL) 397 398CONS_DRIVER(xcon, xcon_probe, xcon_init, NULL, xcon_getc, xcon_checkc, xcon_putc, NULL); 399