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