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