xboxfb.c revision 155510
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 155510 2006-02-10 18:48:22Z rink $ 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/* colours */ 73#define CONSOLE_COL 0xFF88FF88 /* greenish */ 74#define NORM_COL 0xFFAAAAAA /* grayish */ 75#define BLACK_COL 0x00000000 /* black */ 76 77static int xcon_x = 0; 78static int xcon_y = 0; 79static int xcon_yoffs = 0; 80 81extern struct gfb_font bold8x16; 82 83static char* xcon_map; 84static int* xcon_memstartptr; 85 86static struct tty* xboxfb_tp = NULL; 87static struct keyboard* xbfb_kbd = NULL; 88static int xbfb_keyboard = -1; 89static d_open_t xboxfb_dev_open; 90static d_close_t xboxfb_dev_close; 91static int xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg); 92 93static struct cdevsw xboxfb_cdevsw = { 94 .d_version = D_VERSION, 95 .d_open = xboxfb_dev_open, 96 .d_close = xboxfb_dev_close, 97 .d_name = "xboxfb", 98 .d_flags = D_TTY | D_NEEDGIANT, 99}; 100 101static void 102xcon_probe(struct consdev* cp) 103{ 104 if (arch_i386_is_xbox) 105 cp->cn_pri = CN_REMOTE; 106 else 107 cp->cn_pri = CN_DEAD; 108} 109 110static int 111xcon_getc(struct consdev* cp) 112{ 113 return 0; 114} 115 116static int 117xcon_checkc(struct consdev* cp) 118{ 119 return 0; 120} 121 122static void 123xcon_real_putc(int basecol, int c) 124{ 125 int i, j, ch = c, col; 126 char mask; 127 int* ptri = (int*)xcon_map; 128 129 /* special control chars */ 130 switch (ch) { 131 case '\r': /* carriage return */ 132 xcon_x = 0; 133 return; 134 case '\n': /* newline */ 135 xcon_y += CHAR_HEIGHT; 136 goto scroll; 137 case 7: /* beep */ 138 return; 139 case 8: /* backspace */ 140 if (xcon_x > 0) { 141 xcon_x -= CHAR_WIDTH; 142 } else { 143 if (xcon_y > CHAR_HEIGHT) { 144 xcon_y -= CHAR_HEIGHT; 145 xcon_x = (SCREEN_WIDTH - CHAR_WIDTH); 146 } 147 } 148 return; 149 case 9: /* tab */ 150 xcon_real_putc (basecol, ' '); 151 while ((xcon_x % (8 * CHAR_WIDTH)) != 0) { 152 xcon_real_putc (basecol, ' '); 153 } 154 return; 155 } 156 ptri += (xcon_y * SCREEN_WIDTH) + xcon_x; 157 158 /* we plot the font pixel-by-pixel. bit 7 is skipped as it renders the 159 * console unreadable ... */ 160 for (i = 0; i < FONT_HEIGHT; i++) { 161 mask = 0x40; 162 for (j = 0; j < FONT_WIDTH; j++) { 163 col = (bold8x16.data[(ch * FONT_HEIGHT) + i] & mask) ? basecol : BLACK_COL; 164 *ptri++ = col; 165 mask >>= 1; 166 } 167 ptri += (SCREEN_WIDTH - FONT_WIDTH); 168 } 169 170 xcon_x += CHAR_WIDTH; 171 if (xcon_x >= SCREEN_WIDTH) { 172 xcon_x = 0; 173 xcon_y += CHAR_HEIGHT; 174 } 175 176scroll: 177 if (((xcon_yoffs + CHAR_HEIGHT) * SCREEN_WIDTH * SCREEN_BPP) > (XBOX_FB_SIZE - SCREEN_SIZE)) { 178 /* we are about to run out of video memory, so move everything 179 * back to the beginning of the video memory */ 180 memcpy ((char*)xcon_map, 181 (char*)(xcon_map + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP)), 182 SCREEN_SIZE); 183 xcon_y -= xcon_yoffs; xcon_yoffs = 0; 184 *xcon_memstartptr = XBOX_FB_START; 185 } 186 187 /* we achieve much faster scrolling by just altering the video memory 188 * address base. once all memory is used, we return to the beginning 189 * again */ 190 while ((xcon_y - xcon_yoffs) >= SCREEN_HEIGHT) { 191 xcon_yoffs += CHAR_HEIGHT; 192 memset ((char*)(xcon_map + (xcon_y * SCREEN_WIDTH * SCREEN_BPP)), 0, CHAR_HEIGHT * SCREEN_WIDTH * SCREEN_BPP); 193 *xcon_memstartptr = XBOX_FB_START + (xcon_yoffs * SCREEN_WIDTH * SCREEN_BPP); 194 } 195} 196 197static void 198xcon_putc(struct consdev* cp, int c) 199{ 200 xcon_real_putc (CONSOLE_COL, c); 201} 202 203static void 204xcon_init(struct consdev* cp) 205{ 206 int i; 207 int* iptr; 208 209 /* Don't init the framebuffer on non-XBOX-es */ 210 if (!arch_i386_is_xbox) 211 return; 212 213 /* 214 * We must make a mapping from video framebuffer memory to real. This is 215 * very crude: we map the entire videomemory to PAGE_SIZE! Since our 216 * kernel lives at it's relocated address range (0xc0xxxxxx), it won't 217 * care. 218 * 219 * We use address PAGE_SIZE and up so we can still trap NULL pointers. 220 * Once xboxfb_drvinit() is called, the mapping will be done via the OS 221 * and stored in a more sensible location ... but since we're not fully 222 * initialized, this is our only way to go :-( 223 */ 224 for (i = 0; i < (XBOX_FB_SIZE / PAGE_SIZE); i++) { 225 pmap_kenter (((i + 1) * PAGE_SIZE), XBOX_FB_START + (i * PAGE_SIZE)); 226 } 227 pmap_kenter ((i + 1) * PAGE_SIZE, XBOX_FB_START_PTR - XBOX_FB_START_PTR % PAGE_SIZE); 228 xcon_map = (char*)PAGE_SIZE; 229 xcon_memstartptr = (int*)((i + 1) * PAGE_SIZE + XBOX_FB_START_PTR % PAGE_SIZE); 230 231 /* clear the screen */ 232 iptr = (int*)xcon_map; 233 for (i = 0; i < SCREEN_HEIGHT * SCREEN_WIDTH; i++) 234 *iptr++ = BLACK_COL; 235 236 sprintf(cp->cn_name, "xboxfb"); 237 cp->cn_tp = xboxfb_tp; 238} 239 240static void 241xboxfb_timer(void* arg) 242{ 243 int i; 244 245 if (xbfb_kbd != NULL) 246 return; 247 248 i = kbd_allocate ("*", 0, (void*)&xbfb_keyboard, xboxfb_kbdevent, NULL); 249 if (i != -1) { 250 /* allocation was successfull; xboxfb_kbdevent() is called to 251 * feed the keystrokes to the tty driver */ 252 xbfb_kbd = kbd_get_keyboard (i); 253 xbfb_keyboard = i; 254 return; 255 } 256 257 /* probe again in a few */ 258 timeout (xboxfb_timer, NULL, hz / 10); 259} 260 261static int 262xboxfb_kbdevent(keyboard_t* thiskbd, int event, void* arg) 263{ 264 int c; 265 266 if (event == KBDIO_UNLOADING) { 267 /* keyboard was unplugged; clean up and enable probing */ 268 xbfb_kbd = NULL; 269 xbfb_keyboard = -1; 270 kbd_release (thiskbd, (void*)&xbfb_keyboard); 271 timeout (xboxfb_timer, NULL, hz / 10); 272 return 0; 273 } 274 275 for (;;) { 276 c = (kbdsw[xbfb_kbd->kb_index])->read_char (xbfb_kbd, 0); 277 if (c == NOKEY) 278 return 0; 279 280 /* only feed non-special keys to an open console */ 281 if (c != ERRKEY) { 282 if ((KEYFLAGS(c)) == 0x0) 283 if (xboxfb_tp->t_state & TS_ISOPEN) 284 ttyld_rint (xboxfb_tp, KEYCHAR(c)); 285 } 286 } 287 288 return 0; 289} 290 291static void 292xboxfb_drvinit (void* unused) 293{ 294 struct cdev* dev; 295 int i; 296 297 /* Don't init the framebuffer on non-XBOX-es */ 298 if (!arch_i386_is_xbox) 299 return; 300 301 /* 302 * When this function is called, the OS is capable of doing 303 * device-memory mappings using pmap_mapdev(). Therefore, we ditch the 304 * ugly PAGE_SIZE-based mapping and ask the OS to create a decent 305 * mapping for us. 306 */ 307 dev = make_dev (&xboxfb_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", "xboxfb"); 308 xcon_map = pmap_mapdev (XBOX_FB_START, XBOX_FB_SIZE); 309 xcon_memstartptr = (int*)pmap_mapdev (XBOX_FB_START_PTR, PAGE_SIZE); 310 *xcon_memstartptr = XBOX_FB_START; 311 312 /* ditch all ugly previous mappings */ 313 for (i = 0; i < (XBOX_FB_SIZE / PAGE_SIZE); i++) { 314 pmap_kremove (((i + 1) * PAGE_SIZE)); 315 } 316 pmap_kremove (PAGE_SIZE + XBOX_FB_SIZE); 317 318 /* probe for a keyboard */ 319 xboxfb_timer (NULL); 320 xboxfb_tp = ttyalloc(); 321} 322 323static void 324xboxfb_tty_start(struct tty* tp) 325{ 326 struct clist* cl; 327 int len, i; 328 u_char buf[128]; 329 330 if (tp->t_state & TS_BUSY) 331 return; 332 333 /* simply feed all outstanding tty data to real_putc() */ 334 tp->t_state |= TS_BUSY; 335 cl = &tp->t_outq; 336 len = q_to_b(cl, buf, 128); 337 for (i = 0; i < len; i++) 338 xcon_real_putc(NORM_COL, buf[i]); 339 tp->t_state &= ~TS_BUSY; 340} 341 342static void 343xboxfb_tty_stop(struct tty* tp, int flag) { 344 if (tp->t_state & TS_BUSY) 345 if ((tp->t_state & TS_TTSTOP) == 0) 346 tp->t_state |= TS_FLUSH; 347} 348 349static int 350xboxfb_tty_param(struct tty* tp, struct termios* t) 351{ 352 return 0; 353} 354 355static int 356xboxfb_dev_open(struct cdev* dev, int flag, int mode, struct thread* td) 357{ 358 struct tty* tp; 359 360 tp = dev->si_tty = xboxfb_tp; 361 362 tp->t_oproc = xboxfb_tty_start; 363 tp->t_param = xboxfb_tty_param; 364 tp->t_stop = xboxfb_tty_stop; 365 tp->t_dev = dev; 366 367 if ((tp->t_state & TS_ISOPEN) == 0) { 368 tp->t_state |= TS_CARR_ON; 369 ttychars(tp); 370 tp->t_iflag = TTYDEF_IFLAG; 371 tp->t_oflag = TTYDEF_OFLAG; 372 tp->t_cflag = TTYDEF_CFLAG | CLOCAL; 373 tp->t_lflag = TTYDEF_LFLAG; 374 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 375 ttsetwater(tp); 376 } 377 378 return ttyld_open (tp, dev); 379} 380 381static int 382xboxfb_dev_close(struct cdev* dev, int flag, int mode, struct thread* td) 383{ 384 struct tty* tp; 385 386 tp = xboxfb_tp; 387 ttyld_close (tp, flag); 388 tty_close(tp); 389 return 0; 390} 391 392SYSINIT(xboxfbdev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, xboxfb_drvinit, NULL) 393 394CONS_DRIVER(xcon, xcon_probe, xcon_init, NULL, xcon_getc, xcon_checkc, xcon_putc, NULL); 395