1 /* 2 * Copyright 2005-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <KernelExport.h> 8 #include <kernel.h> 9 #include <lock.h> 10 #include <boot_item.h> 11 #include <vm.h> 12 #include <fs/devfs.h> 13 #include <boot/kernel_args.h> 14 15 #include <frame_buffer_console.h> 16 #include "font.h" 17 18 #include <stdlib.h> 19 #include <string.h> 20 #include <stdio.h> 21 #include <unistd.h> 22 23 24 //#define TRACE_FB_CONSOLE 25 #ifdef TRACE_FB_CONSOLE 26 # define TRACE(x) dprintf x 27 #else 28 # define TRACE(x) ; 29 #endif 30 31 32 struct console_info { 33 mutex lock; 34 area_id area; 35 36 addr_t frame_buffer; 37 int32 width; 38 int32 height; 39 int32 depth; 40 int32 bytes_per_pixel; 41 int32 bytes_per_row; 42 43 int32 columns; 44 int32 rows; 45 int32 cursor_x; 46 int32 cursor_y; 47 }; 48 49 // Palette is (white and black are exchanged): 50 // 0 - white, 1 - blue, 2 - green, 3 - cyan, 4 - red, 5 - magenta, 6 - yellow, 7 - black 51 // 8-15 - same but bright (we're ignoring those) 52 53 static uint8 sPalette8[] = { 54 63, 32, 52, 70, 42, 88, 67, 0, 55 }; 56 static uint16 sPalette15[] = { 57 // 0bbbbbgggggrrrrr (5-5-5) 58 0x7fff, 0x001f, 0x03e0, 0x03ff, 0x7c00, 0x7c1f, 0x7fe0, 0x0000, 59 }; 60 static uint16 sPalette16[] = { 61 // bbbbbggggggrrrrr (5-6-5) 62 0xffff, 0x001f, 0x07e0, 0x07ff, 0xf800, 0xf81f, 0xffe0, 0x0000, 63 }; 64 static uint32 sPalette32[] = { 65 // is also used by 24 bit modes 66 0xffffff, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00, 0x000000, 67 }; 68 69 static struct console_info sConsole; 70 static struct frame_buffer_boot_info sBootInfo; 71 72 73 static inline uint8 74 foreground_color(uint8 attr) 75 { 76 return attr & 0x7; 77 } 78 79 80 static inline uint8 81 background_color(uint8 attr) 82 { 83 return (attr >> 4) & 0x7; 84 } 85 86 87 static uint8 * 88 get_palette_entry(uint8 index) 89 { 90 switch (sConsole.depth) { 91 case 8: 92 return &sPalette8[index]; 93 case 15: 94 return (uint8 *)&sPalette15[index]; 95 case 16: 96 return (uint8 *)&sPalette16[index]; 97 default: 98 return (uint8 *)&sPalette32[index]; 99 } 100 } 101 102 103 static void 104 render_glyph(int32 x, int32 y, uint8 glyph, uint8 attr) 105 { 106 // we're ASCII only 107 if (glyph > 127) 108 glyph = 127; 109 110 if (sConsole.depth >= 8) { 111 uint8 *base = (uint8 *)(sConsole.frame_buffer + sConsole.bytes_per_row * y * CHAR_HEIGHT 112 + x * CHAR_WIDTH * sConsole.bytes_per_pixel); 113 uint8 *color = get_palette_entry(foreground_color(attr)); 114 uint8 *backgroundColor = get_palette_entry(background_color(attr)); 115 116 for (y = 0; y < CHAR_HEIGHT; y++) { 117 uint8 bits = FONT[CHAR_HEIGHT * glyph + y]; 118 for (x = 0; x < CHAR_WIDTH; x++) { 119 for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) { 120 if (bits & 1) 121 base[x * sConsole.bytes_per_pixel + i] = color[i]; 122 else 123 base[x * sConsole.bytes_per_pixel + i] = backgroundColor[i]; 124 } 125 bits >>= 1; 126 } 127 128 base += sConsole.bytes_per_row; 129 } 130 } else { 131 // monochrome mode 132 133 uint8 *base = (uint8 *)(sConsole.frame_buffer + sConsole.bytes_per_row * y * CHAR_HEIGHT 134 + x * CHAR_WIDTH / 8); 135 uint8 baseOffset = (x * CHAR_WIDTH) & 0x7; 136 137 for (y = 0; y < CHAR_HEIGHT; y++) { 138 uint8 bits = FONT[CHAR_HEIGHT * glyph + y]; 139 uint8 offset = baseOffset; 140 uint8 mask = 1 << (7 - baseOffset); 141 142 for (x = 0; x < CHAR_WIDTH; x++) { 143 if (mask == 0) 144 mask = 128; 145 146 // black on white 147 if (bits & 1) 148 base[offset / 8] &= ~mask; 149 else 150 base[offset / 8] |= mask; 151 152 bits >>= 1; 153 mask >>= 1; 154 offset += 1; 155 } 156 157 base += sConsole.bytes_per_row; 158 } 159 } 160 } 161 162 163 static void 164 draw_cursor(int32 x, int32 y) 165 { 166 if (x < 0 || y < 0) 167 return; 168 169 x *= CHAR_WIDTH * sConsole.bytes_per_pixel; 170 y *= CHAR_HEIGHT; 171 int32 endX = x + CHAR_WIDTH * sConsole.bytes_per_pixel; 172 int32 endY = y + CHAR_HEIGHT; 173 uint8 *base = (uint8 *)(sConsole.frame_buffer + y * sConsole.bytes_per_row); 174 175 if (sConsole.depth < 8) { 176 x /= 8; 177 endY /= 8; 178 } 179 180 for (; y < endY; y++) { 181 for (int32 x2 = x; x2 < endX; x2++) 182 base[x2] = ~base[x2]; 183 184 base += sConsole.bytes_per_row; 185 } 186 } 187 188 189 static status_t 190 console_get_size(int32 *_width, int32 *_height) 191 { 192 *_width = sConsole.columns; 193 *_height = sConsole.rows; 194 195 return B_OK; 196 } 197 198 199 static void 200 console_move_cursor(int32 x, int32 y) 201 { 202 if (!frame_buffer_console_available()) 203 return; 204 205 draw_cursor(sConsole.cursor_x, sConsole.cursor_y); 206 draw_cursor(x, y); 207 208 sConsole.cursor_x = x; 209 sConsole.cursor_y = y; 210 } 211 212 213 static void 214 console_put_glyph(int32 x, int32 y, uint8 glyph, uint8 attr) 215 { 216 if (x >= sConsole.columns || y >= sConsole.rows 217 || !frame_buffer_console_available()) 218 return; 219 220 render_glyph(x, y, glyph, attr); 221 } 222 223 224 static void 225 console_fill_glyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph, uint8 attr) 226 { 227 if (x >= sConsole.columns || y >= sConsole.rows 228 || !frame_buffer_console_available()) 229 return; 230 231 int32 left = x + width; 232 if (left > sConsole.columns) 233 left = sConsole.columns; 234 235 int32 bottom = y + height; 236 if (bottom > sConsole.rows) 237 bottom = sConsole.rows; 238 239 for (; y < bottom; y++) { 240 for (int32 x2 = x; x2 < left; x2++) { 241 render_glyph(x2, y, glyph, attr); 242 } 243 } 244 } 245 246 247 static void 248 console_blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx, int32 desty) 249 { 250 if (!frame_buffer_console_available()) 251 return; 252 253 height *= CHAR_HEIGHT; 254 srcy *= CHAR_HEIGHT; 255 desty *= CHAR_HEIGHT; 256 257 if (sConsole.depth >= 8) { 258 width *= CHAR_WIDTH * sConsole.bytes_per_pixel; 259 srcx *= CHAR_WIDTH * sConsole.bytes_per_pixel; 260 destx *= CHAR_WIDTH * sConsole.bytes_per_pixel; 261 } else { 262 // monochrome mode 263 width = width * CHAR_WIDTH / 8; 264 srcx = srcx * CHAR_WIDTH / 8; 265 destx = destx * CHAR_WIDTH / 8; 266 } 267 268 for (int32 y = 0; y < height; y++) { 269 memmove((void *)(sConsole.frame_buffer + (desty + y) * sConsole.bytes_per_row + destx), 270 (void *)(sConsole.frame_buffer + (srcy + y) * sConsole.bytes_per_row + srcx), width); 271 } 272 } 273 274 275 static void 276 console_clear(uint8 attr) 277 { 278 if (!frame_buffer_console_available()) 279 return; 280 281 switch (sConsole.bytes_per_pixel) { 282 case 1: 283 if (sConsole.depth >= 8) { 284 memset((void *)sConsole.frame_buffer, sPalette8[background_color(attr)], 285 sConsole.height * sConsole.bytes_per_row); 286 } else { 287 // special case for VGA mode 288 memset((void *)sConsole.frame_buffer, 0xff, 289 sConsole.height * sConsole.bytes_per_row); 290 } 291 break; 292 default: 293 { 294 uint8 *base = (uint8 *)sConsole.frame_buffer; 295 uint8 *color = get_palette_entry(background_color(attr)); 296 297 for (int32 y = 0; y < sConsole.height; y++) { 298 for (int32 x = 0; x < sConsole.width; x++) { 299 for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) { 300 base[x * sConsole.bytes_per_pixel + i] = color[i]; 301 } 302 } 303 base += sConsole.bytes_per_row; 304 } 305 break; 306 } 307 } 308 309 sConsole.cursor_x = -1; 310 sConsole.cursor_y = -1; 311 } 312 313 314 static status_t 315 console_std_ops(int32 op, ...) 316 { 317 switch (op) { 318 case B_MODULE_INIT: 319 return frame_buffer_console_available() ? B_OK : B_ERROR; 320 case B_MODULE_UNINIT: 321 return B_OK; 322 323 default: 324 return B_ERROR; 325 } 326 } 327 328 329 console_module_info gFrameBufferConsoleModule = { 330 { 331 FRAME_BUFFER_CONSOLE_MODULE_NAME, 332 0, 333 console_std_ops 334 }, 335 &console_get_size, 336 &console_move_cursor, 337 &console_put_glyph, 338 &console_fill_glyph, 339 &console_blit, 340 &console_clear, 341 }; 342 343 344 static status_t 345 frame_buffer_update(addr_t baseAddress, int32 width, int32 height, int32 depth, int32 bytesPerRow) 346 { 347 TRACE(("frame_buffer_update(buffer = %p, width = %ld, height = %ld, depth = %ld, bytesPerRow = %ld)\n", 348 (void *)baseAddress, width, height, depth, bytesPerRow)); 349 350 mutex_lock(&sConsole.lock); 351 352 sConsole.frame_buffer = baseAddress; 353 sConsole.width = width; 354 sConsole.height = height; 355 sConsole.depth = depth; 356 sConsole.bytes_per_pixel = (depth + 7) / 8; 357 sConsole.bytes_per_row = bytesPerRow; 358 sConsole.columns = sConsole.width / CHAR_WIDTH; 359 sConsole.rows = sConsole.height / CHAR_HEIGHT; 360 361 // initially, the cursor is hidden 362 sConsole.cursor_x = -1; 363 sConsole.cursor_y = -1; 364 365 TRACE(("framebuffer mapped at %p, %ld columns, %ld rows\n", 366 (void *)sConsole.frame_buffer, sConsole.columns, sConsole.rows)); 367 368 mutex_unlock(&sConsole.lock); 369 return B_OK; 370 } 371 372 373 // #pragma mark - 374 375 376 bool 377 frame_buffer_console_available(void) 378 { 379 return sConsole.frame_buffer != NULL; 380 } 381 382 383 status_t 384 frame_buffer_console_init(kernel_args *args) 385 { 386 if (!args->frame_buffer.enabled) 387 return B_OK; 388 389 void *frameBuffer; 390 sConsole.area = map_physical_memory("vesa_fb", 391 (void *)args->frame_buffer.physical_buffer.start, 392 args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS, 393 B_READ_AREA | B_WRITE_AREA | B_USER_CLONEABLE_AREA, &frameBuffer); 394 if (sConsole.area < B_OK) 395 return sConsole.area; 396 397 mutex_init(&sConsole.lock, "console_lock"); 398 399 int32 bytesPerRow = args->frame_buffer.width; 400 switch (args->frame_buffer.depth) { 401 case 1: 402 case 4: 403 // special VGA mode (will always be treated as monochrome) 404 bytesPerRow /= 8; 405 break; 406 case 15: 407 case 16: 408 bytesPerRow *= 2; 409 break; 410 case 24: 411 bytesPerRow *= 3; 412 break; 413 case 32: 414 bytesPerRow *= 4; 415 break; 416 } 417 frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width, args->frame_buffer.height, 418 args->frame_buffer.depth, bytesPerRow); 419 420 sBootInfo.frame_buffer = (addr_t)frameBuffer; 421 sBootInfo.width = args->frame_buffer.width; 422 sBootInfo.height = args->frame_buffer.height; 423 sBootInfo.depth = args->frame_buffer.depth; 424 sBootInfo.bytes_per_row = bytesPerRow; 425 add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo, sizeof(frame_buffer_boot_info)); 426 427 return B_OK; 428 } 429 430 431 status_t 432 frame_buffer_console_init_post_modules(kernel_args *args) 433 { 434 // TODO: enable MTRR in VESA mode! 435 // if (sConsole.frame_buffer == NULL) 436 return B_OK; 437 438 // try to set frame buffer memory to write combined 439 440 // return vm_set_area_memory_type(sConsole.area, 441 // args->frame_buffer.physical_buffer.start, B_MTR_WC); 442 } 443 444 445 // #pragma mark - 446 447 448 status_t 449 _user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height, 450 int32 depth, int32 bytesPerRow) 451 { 452 debug_stop_screen_debug_output(); 453 454 if (geteuid() != 0) 455 return B_NOT_ALLOWED; 456 if (IS_USER_ADDRESS(baseAddress) && baseAddress != NULL) 457 return B_BAD_ADDRESS; 458 459 return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow); 460 } 461 462