2 * \file drv_bochsvbe.c
\r
3 * \brief BGA (Bochs Graphic Adapter) Driver for Acess2
\r
4 * \warning This driver does NOT support the Bochs PCI VGA driver
\r
11 #include <fs_devfs.h>
\r
12 #include <drv_pci.h>
\r
13 #include <tpl_drv_video.h>
\r
18 typedef struct sBGA_Mode {
\r
25 // === CONSTANTS ===
\r
26 #define BGA_LFB_MAXSIZE (1024*768*4)
\r
27 #define VBE_DISPI_BANK_ADDRESS 0xA0000
\r
28 #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
\r
29 #define VBE_DISPI_IOPORT_INDEX 0x01CE
\r
30 #define VBE_DISPI_IOPORT_DATA 0x01CF
\r
31 #define VBE_DISPI_DISABLED 0x00
\r
32 #define VBE_DISPI_ENABLED 0x01
\r
33 #define VBE_DISPI_LFB_ENABLED 0x40
\r
34 #define VBE_DISPI_NOCLEARMEM 0x80
\r
37 VBE_DISPI_INDEX_XRES,
\r
38 VBE_DISPI_INDEX_YRES,
\r
39 VBE_DISPI_INDEX_BPP,
\r
40 VBE_DISPI_INDEX_ENABLE,
\r
41 VBE_DISPI_INDEX_BANK,
\r
42 VBE_DISPI_INDEX_VIRT_WIDTH,
\r
43 VBE_DISPI_INDEX_VIRT_HEIGHT,
\r
44 VBE_DISPI_INDEX_X_OFFSET,
\r
45 VBE_DISPI_INDEX_Y_OFFSET
\r
49 // === PROTOTYPES ===
\r
51 int BGA_Install(char **Arguments);
\r
52 void BGA_Uninstall();
\r
54 void BGA_int_WriteRegister(Uint16 reg, Uint16 value);
\r
55 Uint16 BGA_int_ReadRegister(Uint16 reg);
\r
56 void BGA_int_SetBank(Uint16 bank);
\r
57 void BGA_int_SetMode(Uint16 width, Uint16 height);
\r
58 int BGA_int_UpdateMode(int id);
\r
59 int BGA_int_FindMode(tVideo_IOCtl_Mode *info);
\r
60 int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);
\r
61 int BGA_int_MapFB(void *Dest);
\r
63 Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
\r
64 Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);
\r
65 int BGA_Ioctl(tVFS_Node *node, int id, void *data);
\r
68 MODULE_DEFINE(0, 0x0032, BochsGA, BGA_Install, NULL, "PCI", NULL);
\r
69 tDevFS_Driver gBGA_DriverStruct = {
\r
77 int giBGA_CurrentMode = -1;
\r
78 int giBGA_BufferFormat = 0;
\r
79 tVideo_IOCtl_Pos gBGA_CursorPos = {-1,-1};
\r
80 Uint *gBGA_Framebuffer;
\r
81 const tBGA_Mode *gpBGA_CurrentMode;
\r
82 const tBGA_Mode gBGA_Modes[] = {
\r
83 {640,480,32, 640*480*4},
\r
84 {800,600,32, 800*600*4},
\r
85 {1024,768,32, 1024*768*4}
\r
87 #define BGA_MODE_COUNT (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0]))
\r
91 * \fn int BGA_Install(char **Arguments)
\r
93 int BGA_Install(char **Arguments)
\r
97 // Check BGA Version
\r
98 version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);
\r
100 // NOTE: This driver was written for 0xB0C4, but they seem to be backwards compatable
\r
101 if(version != 0xB0C0 && (version < 0xB0C4 || version > 0xB0C5)) {
\r
102 Log_Warning("BGA", "Bochs Adapter Version is not 0xB0C4 or 0xB0C5, instead 0x%x", version);
\r
103 return MODULE_ERR_NOTNEEDED;
\r
107 if(DevFS_AddDevice( &gBGA_DriverStruct ) == -1) {
\r
108 Log_Warning("BGA", "Unable to register with DevFS, maybe already loaded?");
\r
109 return MODULE_ERR_MISC;
\r
112 // Map Framebuffer to hardware address
\r
113 gBGA_Framebuffer = (void *) MM_MapHWPages(VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768); // 768 pages (3Mb)
\r
115 return MODULE_ERR_OK;
\r
119 * \fn void BGA_Uninstall()
\r
121 void BGA_Uninstall()
\r
123 DevFS_DelDevice( &gBGA_DriverStruct );
\r
124 MM_UnmapHWPages( VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768 );
\r
128 * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
\r
129 * \brief Read from the framebuffer
\r
131 Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
\r
134 if(giBGA_CurrentMode == -1) return -1;
\r
136 // Check Offset and Length against Framebuffer Size
\r
137 if(off+len > gpBGA_CurrentMode->fbSize)
\r
140 // Copy from Framebuffer
\r
141 memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);
\r
146 * \fn Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)
\r
147 * \brief Write to the framebuffer
\r
149 Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *Buffer)
\r
151 ENTER("xoff xlen", off, len);
\r
154 if(giBGA_CurrentMode == -1) {
\r
155 Log_Notice("BGA", "Setting video mode to #0 (640x480x32)");
\r
156 BGA_int_UpdateMode(0); // Mode Zero is 640x480
\r
160 switch( giBGA_BufferFormat )
\r
162 case VIDEO_BUFFMT_TEXT:
\r
164 tVT_Char *chars = Buffer;
\r
165 int x, y; // Characters/Rows
\r
166 int widthInChars = gpBGA_CurrentMode->width/giVT_CharWidth;
\r
169 off /= sizeof(tVT_Char);
\r
170 len /= sizeof(tVT_Char);
\r
172 x = (off % widthInChars);
\r
173 y = (off / widthInChars);
\r
176 if(y > gpBGA_CurrentMode->height / giVT_CharHeight) {
\r
181 dest = (Uint32 *)gBGA_Framebuffer;
\r
182 dest += y * gpBGA_CurrentMode->width * giVT_CharHeight;
\r
187 dest + x*giVT_CharWidth, 32, gpBGA_CurrentMode->width*4,
\r
188 VT_Colour12to24(chars->BGCol),
\r
189 VT_Colour12to24(chars->FGCol)
\r
194 if( x >= widthInChars ) {
\r
196 y ++; // Why am I keeping track of this?
\r
197 dest += gpBGA_CurrentMode->width*giVT_CharHeight;
\r
203 case VIDEO_BUFFMT_FRAMEBUFFER:
\r
205 Uint8 *destBuf = (Uint8*) ((Uint)gBGA_Framebuffer + (Uint)off);
\r
207 if( off + len > gpBGA_CurrentMode->fbSize ) {
\r
212 LOG("buffer = %p", Buffer);
\r
213 LOG("Updating Framebuffer (%p to %p)", destBuf, destBuf + (Uint)len);
\r
216 // Copy to Frambuffer
\r
217 memcpy(destBuf, Buffer, len);
\r
219 LOG("BGA Framebuffer updated");
\r
232 * \fn int BGA_Ioctl(tVFS_Node *Node, int ID, void *Data)
\r
233 * \brief Handle messages to the device
\r
235 int BGA_Ioctl(tVFS_Node *Node, int ID, void *Data)
\r
238 ENTER("pNode iId pData", Node, ID, Data);
\r
242 case DRV_IOCTL_TYPE:
\r
243 ret = DRV_TYPE_VIDEO;
\r
245 case DRV_IOCTL_IDENT:
\r
246 memcpy(Data, "BGA1", 4);
\r
249 case DRV_IOCTL_VERSION:
\r
252 case DRV_IOCTL_LOOKUP: // TODO: Implement
\r
256 case VIDEO_IOCTL_GETSETMODE:
\r
258 ret = BGA_int_UpdateMode(*(int*)(Data));
\r
260 ret = giBGA_CurrentMode;
\r
263 case VIDEO_IOCTL_FINDMODE:
\r
264 ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data);
\r
267 case VIDEO_IOCTL_MODEINFO:
\r
268 ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data);
\r
271 case VIDEO_IOCTL_SETBUFFORMAT:
\r
272 ret = giBGA_BufferFormat;
\r
274 giBGA_BufferFormat = *(int*)Data;
\r
277 case VIDEO_IOCTL_SETCURSOR:
\r
278 gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x;
\r
279 gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y;
\r
282 // Request Access to LFB
\r
283 // case VIDEO_IOCTL_REQLFB:
\r
284 // ret = BGA_int_MapFB( *(void**)Data );
\r
296 //== Internal Functions ==
\r
298 * \fn void BGA_int_WriteRegister(Uint16 reg, Uint16 value)
\r
299 * \brief Writes to a BGA register
\r
301 void BGA_int_WriteRegister(Uint16 reg, Uint16 value)
\r
303 outw(VBE_DISPI_IOPORT_INDEX, reg);
\r
304 outw(VBE_DISPI_IOPORT_DATA, value);
\r
307 Uint16 BGA_int_ReadRegister(Uint16 reg)
\r
309 outw(VBE_DISPI_IOPORT_INDEX, reg);
\r
310 return inw(VBE_DISPI_IOPORT_DATA);
\r
314 void BGA_int_SetBank(Uint16 bank)
\r
316 BGA_int_WriteRegister(VBE_DISPI_INDEX_BANK, bank);
\r
321 * \fn void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp)
\r
322 * \brief Sets the video mode from the dimensions and bpp given
\r
324 void BGA_int_SetMode(Uint16 Width, Uint16 Height)
\r
326 ENTER("iWidth iheight ibpp", Width, Height, bpp);
\r
327 BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
\r
328 BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, Width);
\r
329 BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, Height);
\r
330 BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, 32);
\r
331 BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);
\r
336 * \fn int BGA_int_UpdateMode(int id)
\r
337 * \brief Set current vide mode given a mode id
\r
339 int BGA_int_UpdateMode(int id)
\r
342 if(id < 0 || id >= BGA_MODE_COUNT) return -1;
\r
345 gBGA_Modes[id].width,
\r
346 gBGA_Modes[id].height);
\r
348 giBGA_CurrentMode = id;
\r
349 gpBGA_CurrentMode = &gBGA_Modes[id];
\r
354 * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)
\r
355 * \brief Find a mode matching the given options
\r
357 int BGA_int_FindMode(tVideo_IOCtl_Mode *info)
\r
360 int best = 0, bestFactor = 1000;
\r
362 int rqdProduct = info->width * info->height * info->bpp;
\r
364 ENTER("pinfo", info);
\r
365 LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);
\r
367 for(i = 0; i < BGA_MODE_COUNT; i++)
\r
370 LogF("Mode %i (%ix%ix%i), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp);
\r
373 // Ooh! A perfect match
\r
374 if(gBGA_Modes[i].width == info->width
\r
375 && gBGA_Modes[i].height == info->height
\r
376 && gBGA_Modes[i].bpp == info->bpp)
\r
379 LogF("Perfect!\n");
\r
385 // If not, how close are we?
\r
386 tmp = gBGA_Modes[i].width * gBGA_Modes[i].height * gBGA_Modes[i].bpp;
\r
388 tmp = tmp < 0 ? -tmp : tmp; // tmp = ABS(tmp)
\r
389 factor = tmp * 100 / rqdProduct;
\r
392 LogF("factor = %i\n", factor);
\r
395 if(factor < bestFactor)
\r
397 bestFactor = factor;
\r
403 info->width = gBGA_Modes[best].width;
\r
404 info->height = gBGA_Modes[best].height;
\r
405 info->bpp = gBGA_Modes[best].bpp;
\r
411 * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)
\r
412 * \brief Get mode information
\r
414 int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)
\r
417 //if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) {
\r
421 if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1;
\r
423 info->width = gBGA_Modes[info->id].width;
\r
424 info->height = gBGA_Modes[info->id].height;
\r
425 info->bpp = gBGA_Modes[info->id].bpp;
\r
431 * \fn int BGA_int_MapFB(void *Dest)
\r
432 * \brief Map the framebuffer into a process's space
\r
433 * \param Dest User address to load to
\r
435 int BGA_int_MapFB(void *Dest)
\r
441 if((Uint)Dest > 0xC0000000) return 0;
\r
442 if(gpBGA_CurrentMode->bpp < 15) return 0; // Only non-pallete modes are supported
\r
444 // Count required pages
\r
445 pages = (gpBGA_CurrentMode->fbSize + 0xFFF) >> 12;
\r
447 // Check if there is space
\r
448 for( i = 0; i < pages; i++ )
\r
450 if(MM_GetPhysAddr( (Uint)Dest + (i << 12) ))
\r
455 for( i = 0; i < pages; i++ )
\r
456 MM_Map( (Uint)Dest + (i<<12), VBE_DISPI_LFB_PHYSICAL_ADDRESS + (i<<12) );
\r