Fixes to x86 error and interrupt handling
[tpg/acess2.git] / Modules / Display / BochsGA / bochsvbe.c
1 /**\r
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
5 */\r
6 #define DEBUG   0\r
7 #include <acess.h>\r
8 #include <errno.h>\r
9 #include <modules.h>\r
10 #include <vfs.h>\r
11 #include <fs_devfs.h>\r
12 #include <drv_pci.h>\r
13 #include <tpl_drv_video.h>\r
14 \r
15 #define INT\r
16 \r
17 // === TYPES ===\r
18 typedef struct {\r
19         Uint16  width;\r
20         Uint16  height;\r
21         Uint16  bpp;\r
22         Uint16  flags;\r
23         Uint32  fbSize;\r
24 } t_bga_mode;\r
25 \r
26 // === CONSTANTS ===\r
27 enum eMode_Flags {\r
28         MODEFLAG_TEXT = 1\r
29 };\r
30 #define BGA_LFB_MAXSIZE (1024*768*4)\r
31 #define VBE_DISPI_BANK_ADDRESS  0xA0000\r
32 #define VBE_DISPI_LFB_PHYSICAL_ADDRESS  0xE0000000\r
33 #define VBE_DISPI_IOPORT_INDEX  0x01CE\r
34 #define VBE_DISPI_IOPORT_DATA   0x01CF\r
35 #define VBE_DISPI_DISABLED      0x00\r
36 #define VBE_DISPI_ENABLED       0x01\r
37 #define VBE_DISPI_LFB_ENABLED   0x40\r
38 #define VBE_DISPI_NOCLEARMEM    0x80\r
39 enum {\r
40         VBE_DISPI_INDEX_ID,\r
41         VBE_DISPI_INDEX_XRES,\r
42         VBE_DISPI_INDEX_YRES,\r
43         VBE_DISPI_INDEX_BPP,\r
44         VBE_DISPI_INDEX_ENABLE,\r
45         VBE_DISPI_INDEX_BANK,\r
46         VBE_DISPI_INDEX_VIRT_WIDTH,\r
47         VBE_DISPI_INDEX_VIRT_HEIGHT,\r
48         VBE_DISPI_INDEX_X_OFFSET,\r
49         VBE_DISPI_INDEX_Y_OFFSET\r
50 };\r
51 \r
52 \r
53 // === PROTOTYPES ===\r
54 // Driver\r
55  int    BGA_Install(char **Arguments);\r
56 void    BGA_Uninstall();\r
57 // Internal\r
58 void    BGA_int_WriteRegister(Uint16 reg, Uint16 value);\r
59 Uint16  BGA_int_ReadRegister(Uint16 reg);\r
60 void    BGA_int_SetBank(Uint16 bank);\r
61 void    BGA_int_SetMode(Uint16 width, Uint16 height);\r
62  int    BGA_int_UpdateMode(int id);\r
63  int    BGA_int_FindMode(tVideo_IOCtl_Mode *info);\r
64  int    BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);\r
65  int    BGA_int_MapFB(void *Dest);\r
66 // Filesystem\r
67 Uint64  BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
68 Uint64  BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
69  int    BGA_Ioctl(tVFS_Node *node, int id, void *data);\r
70 \r
71 // === GLOBALS ===\r
72 MODULE_DEFINE(0, 0x0032, BochsGA, BGA_Install, NULL, "PCI", NULL);\r
73 tDevFS_Driver   gBGA_DriverStruct = {\r
74         NULL, "BochsGA",\r
75         {\r
76         .Read = BGA_Read,\r
77         .Write = BGA_Write,\r
78         .IOCtl = BGA_Ioctl\r
79         }\r
80 };\r
81  int    giBGA_CurrentMode = -1;\r
82 tVideo_IOCtl_Pos        gBGA_CursorPos = {-1,-1};\r
83  int    giBGA_DriverId = -1;\r
84 Uint    *gBGA_Framebuffer;\r
85 t_bga_mode      gBGA_Modes[] = {\r
86         {},\r
87         { 80,25, 12, MODEFLAG_TEXT, 80*25*8},   // 640 x 480\r
88         {100,37, 12, MODEFLAG_TEXT, 100*37*8},  // 800 x 600\r
89         {640,480,8, 0, 640*480},\r
90         {640,480,32, 0, 640*480*4},\r
91         {800,600,8, 0, 800*600},\r
92         {800,600,32, 0, 800*600*4},\r
93 };\r
94 #define BGA_MODE_COUNT  (sizeof(gBGA_Modes)/sizeof(gBGA_Modes[0]))\r
95 \r
96 // === CODE ===\r
97 /**\r
98  * \fn int BGA_Install(char **Arguments)\r
99  */\r
100 int BGA_Install(char **Arguments)\r
101 {\r
102          int    bga_version = 0;\r
103         \r
104         // Check BGA Version\r
105         bga_version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);\r
106         // NOTE: This driver was written for 0xB0C4, but they seem to be backwards compatable\r
107         if(bga_version < 0xB0C4 || bga_version > 0xB0C5) {\r
108                 Warning("[BGA ] Bochs Adapter Version is not 0xB0C4 or 0xB0C5, instead 0x%x", bga_version);\r
109                 return MODULE_ERR_NOTNEEDED;\r
110         }\r
111         \r
112         // Install Device\r
113         giBGA_DriverId = DevFS_AddDevice( &gBGA_DriverStruct );\r
114         if(giBGA_DriverId == -1) {\r
115                 Warning("[BGA ] Unable to register with DevFS, maybe already loaded?");\r
116                 return MODULE_ERR_MISC;\r
117         }\r
118         \r
119         // Map Framebuffer to hardware address\r
120         gBGA_Framebuffer = (void *) MM_MapHWPage(VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768);  // 768 pages (3Mb)\r
121         \r
122         return MODULE_ERR_OK;\r
123 }\r
124 \r
125 /**\r
126  * \fn void BGA_Uninstall()\r
127  */\r
128 void BGA_Uninstall()\r
129 {\r
130         //DevFS_DelDevice( giBGA_DriverId );\r
131         MM_UnmapHWPage( VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768 );\r
132 }\r
133 \r
134 /**\r
135  * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
136  * \brief Read from the framebuffer\r
137  */\r
138 Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
139 {\r
140         // Check Mode\r
141         if(giBGA_CurrentMode == -1)     return -1;\r
142         \r
143         // Check Offset and Length against Framebuffer Size\r
144         if(off+len > gBGA_Modes[giBGA_CurrentMode].fbSize)\r
145                 return -1;\r
146         \r
147         // Copy from Framebuffer\r
148         memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);\r
149         return len;\r
150 }\r
151 \r
152 /**\r
153  * \fn Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
154  * \brief Write to the framebuffer\r
155  */\r
156 Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *Buffer)\r
157 {       \r
158         ENTER("xoff xlen", off, len);\r
159         \r
160         // Check Mode\r
161         if(giBGA_CurrentMode == -1) {\r
162                 LEAVE('i', -1);\r
163                 return -1;\r
164         }\r
165         \r
166         // Check Input against Frambuffer Size\r
167         if(off + len > gBGA_Modes[giBGA_CurrentMode].fbSize) {\r
168                 LEAVE('i', -1);\r
169                 return -1;\r
170         }\r
171         \r
172         // Text Mode\r
173         if( gBGA_Modes[giBGA_CurrentMode].flags & MODEFLAG_TEXT )\r
174         {\r
175                 tVT_Char        *chars = Buffer;\r
176                  int    pitch = gBGA_Modes[giBGA_CurrentMode].width * giVT_CharWidth;\r
177                  int    x, y;\r
178                 Uint32  *dest;\r
179                 \r
180                 off /= sizeof(tVT_Char);\r
181                 dest = (void*)gBGA_Framebuffer;\r
182                 x = (off % gBGA_Modes[giBGA_CurrentMode].width) * giVT_CharWidth;\r
183                 y = (off / gBGA_Modes[giBGA_CurrentMode].width) * giVT_CharHeight;\r
184                 dest += y * pitch;\r
185                 dest += x * giVT_CharWidth;\r
186                 len /= sizeof(tVT_Char);\r
187                 while(len--)\r
188                 {\r
189                         VT_Font_Render(\r
190                                 chars->Ch,\r
191                                 dest, pitch,\r
192                                 VT_Colour12to24(chars->BGCol),\r
193                                 VT_Colour12to24(chars->FGCol)\r
194                                 );\r
195                         \r
196                         dest += giVT_CharWidth;\r
197                         \r
198                         chars ++;\r
199                         x += giVT_CharWidth;\r
200                         if( x >= pitch ) {\r
201                                 x = 0;\r
202                                 y += giVT_CharHeight;\r
203                                 dest += pitch*(giVT_CharHeight-1);\r
204                         }\r
205                 }\r
206         }\r
207         else\r
208         {\r
209                 Uint8   *destBuf = (Uint8*) ((Uint)gBGA_Framebuffer + (Uint)off);\r
210                 \r
211                 Log("buffer = %p", Buffer);\r
212                 Log("Updating Framebuffer (%p to %p)",\r
213                         destBuf, destBuf + (Uint)len);\r
214                 \r
215                 \r
216                 // Copy to Frambuffer\r
217                 memcpy(destBuf, Buffer, len);\r
218                 \r
219                 Log("BGA Framebuffer updated");\r
220         }\r
221         \r
222         LEAVE('i', len);\r
223         return len;\r
224 }\r
225 \r
226 /**\r
227  * \fn INT int BGA_Ioctl(tVFS_Node *node, int ID, void *Data)\r
228  * \brief Handle messages to the device\r
229  */\r
230 INT int BGA_Ioctl(tVFS_Node *node, int ID, void *Data)\r
231 {\r
232          int    ret = -2;\r
233         ENTER("pNode iId pData", node, ID, Data);\r
234         \r
235         switch(ID)\r
236         {\r
237         case DRV_IOCTL_TYPE:\r
238                 ret = DRV_TYPE_VIDEO;\r
239                 break;\r
240         case DRV_IOCTL_IDENT:\r
241                 memcpy(Data, "BGA1", 4);\r
242                 ret = 1;\r
243                 break;\r
244         case DRV_IOCTL_VERSION:\r
245                 ret = 0x100;\r
246                 break;\r
247         case DRV_IOCTL_LOOKUP:  // TODO: Implement\r
248                 ret = 0;\r
249                 break;\r
250                 \r
251         case VIDEO_IOCTL_GETSETMODE:\r
252                 if( Data )\r
253                         ret = BGA_int_UpdateMode(*(int*)(Data));\r
254                 else\r
255                         ret = giBGA_CurrentMode;\r
256                 break;\r
257         \r
258         case VIDEO_IOCTL_FINDMODE:\r
259                 ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)Data);\r
260                 break;\r
261         \r
262         case VIDEO_IOCTL_MODEINFO:\r
263                 ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)Data);\r
264                 break;\r
265         \r
266         // Request Access to LFB\r
267         case VIDEO_IOCTL_REQLFB:\r
268                 ret = BGA_int_MapFB( *(void**)Data );\r
269                 break;\r
270         \r
271         case VIDEO_IOCTL_SETCURSOR:\r
272                 gBGA_CursorPos.x = ((tVideo_IOCtl_Pos*)Data)->x;\r
273                 gBGA_CursorPos.y = ((tVideo_IOCtl_Pos*)Data)->y;\r
274                 break;\r
275         \r
276         default:\r
277                 LEAVE('i', -2);\r
278                 return -2;\r
279         }\r
280         \r
281         LEAVE('i', ret);\r
282         return ret;\r
283 }\r
284 \r
285 //== Internal Functions ==\r
286 /**\r
287  * \fn void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
288  * \brief Writes to a BGA register\r
289  */\r
290 void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
291 {\r
292         outw(VBE_DISPI_IOPORT_INDEX, reg);\r
293         outw(VBE_DISPI_IOPORT_DATA, value);\r
294 }\r
295 \r
296 INT Uint16 BGA_int_ReadRegister(Uint16 reg)\r
297 {\r
298         outw(VBE_DISPI_IOPORT_INDEX, reg);\r
299         return inw(VBE_DISPI_IOPORT_DATA);\r
300 }\r
301 \r
302 #if 0\r
303 INT void BGA_int_SetBank(Uint16 bank)\r
304 {\r
305         BGA_int_WriteRegister(VBE_DISPI_INDEX_BANK, bank);\r
306 }\r
307 #endif\r
308 \r
309 /**\r
310  * \fn void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp)\r
311  * \brief Sets the video mode from the dimensions and bpp given\r
312  */\r
313 void BGA_int_SetMode(Uint16 width, Uint16 height)\r
314 {\r
315         ENTER("iwidth iheight ibpp", width, height, bpp);\r
316         BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);\r
317     BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, width);\r
318     BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, height);\r
319     BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP,  32);\r
320     BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);\r
321     //BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM);\r
322         LEAVE('-');\r
323 }\r
324 \r
325 /**\r
326  * \fn int BGA_int_UpdateMode(int id)\r
327  * \brief Set current vide mode given a mode id\r
328  */\r
329 int BGA_int_UpdateMode(int id)\r
330 {\r
331         // Sanity Check\r
332         if(id < 0 || id >= BGA_MODE_COUNT)      return -1;\r
333         \r
334         // Check if it is a text mode\r
335         if( gBGA_Modes[id].flags & MODEFLAG_TEXT )\r
336                 BGA_int_SetMode(\r
337                         gBGA_Modes[id].width*giVT_CharWidth,\r
338                         gBGA_Modes[id].height*giVT_CharHeight);\r
339         else    // Graphics?\r
340                 BGA_int_SetMode(\r
341                         gBGA_Modes[id].width,\r
342                         gBGA_Modes[id].height);\r
343         \r
344         giBGA_CurrentMode = id;\r
345         return id;\r
346 }\r
347 \r
348 /**\r
349  * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
350  * \brief Find a mode matching the given options\r
351  */\r
352 int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
353 {\r
354          int    i;\r
355          int    best = 0, bestFactor = 1000;\r
356          int    factor, tmp;\r
357          int    rqdProduct = info->width * info->height * info->bpp;\r
358         \r
359         ENTER("pinfo", info);\r
360         LOG("info = {width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);\r
361         \r
362         for(i = 0; i < BGA_MODE_COUNT; i++)\r
363         {\r
364                 #if DEBUG >= 2\r
365                 LogF("Mode %i (%ix%ix%i), ", i, gBGA_Modes[i].width, gBGA_Modes[i].height, gBGA_Modes[i].bpp);\r
366                 #endif\r
367                 \r
368                 // Check if this mode is the same type as what we want\r
369                 if( !(gBGA_Modes[i].flags & MODEFLAG_TEXT) != !(info->flags & VIDEO_FLAG_TEXT) )\r
370                         continue;\r
371                 \r
372                 // Ooh! A perfect match\r
373                 if(gBGA_Modes[i].width == info->width\r
374                 && gBGA_Modes[i].height == info->height\r
375                 && gBGA_Modes[i].bpp == info->bpp)\r
376                 {\r
377                         #if DEBUG >= 2\r
378                         LogF("Perfect!\n");\r
379                         #endif\r
380                         best = i;\r
381                         break;\r
382                 }\r
383                 \r
384                 // If not, how close are we?\r
385                 tmp = gBGA_Modes[i].width * gBGA_Modes[i].height * gBGA_Modes[i].bpp;\r
386                 tmp -= rqdProduct;\r
387                 tmp = tmp < 0 ? -tmp : tmp;     // tmp = ABS(tmp)\r
388                 factor = tmp * 100 / rqdProduct;\r
389                 \r
390                 #if DEBUG >= 2\r
391                 LogF("factor = %i\n", factor);\r
392                 #endif\r
393                 \r
394                 if(factor < bestFactor)\r
395                 {\r
396                         bestFactor = factor;\r
397                         best = i;\r
398                 }\r
399         }\r
400         \r
401         info->id = best;\r
402         info->width = gBGA_Modes[best].width;\r
403         info->height = gBGA_Modes[best].height;\r
404         info->bpp = gBGA_Modes[best].bpp;\r
405         \r
406         info->flags = 0;\r
407         if(gBGA_Modes[best].flags & MODEFLAG_TEXT)\r
408                 info->flags |= VIDEO_FLAG_TEXT;\r
409         \r
410         return best;\r
411 }\r
412 \r
413 /**\r
414  * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
415  * \brief Get mode information\r
416  */\r
417 int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
418 {\r
419         // Sanity Check\r
420         //if( !MM_IsUser( (Uint)info, sizeof(tVideo_IOCtl_Mode) ) ) {\r
421         //      return -EINVAL;\r
422         //}\r
423         \r
424         if(info->id < 0 || info->id >= BGA_MODE_COUNT)  return -1;\r
425         \r
426         info->width = gBGA_Modes[info->id].width;\r
427         info->height = gBGA_Modes[info->id].height;\r
428         info->bpp = gBGA_Modes[info->id].bpp;\r
429         \r
430         info->flags = 0;\r
431         if(gBGA_Modes[info->id].flags & MODEFLAG_TEXT)\r
432                 info->flags |= VIDEO_FLAG_TEXT;\r
433         \r
434         return 1;\r
435 }\r
436 \r
437 /**\r
438  * \fn int BGA_int_MapFB(void *Dest)\r
439  * \brief Map the framebuffer into a process's space\r
440  * \param Dest  User address to load to\r
441  */\r
442 int BGA_int_MapFB(void *Dest)\r
443 {\r
444         Uint    i;\r
445         Uint    pages;\r
446         \r
447         // Sanity Check\r
448         if((Uint)Dest > 0xC0000000)     return 0;\r
449         if(gBGA_Modes[giBGA_CurrentMode].bpp < 15)      return 0;       // Only non-pallete modes are supported\r
450         \r
451         // Count required pages\r
452         pages = (gBGA_Modes[giBGA_CurrentMode].fbSize + 0xFFF) >> 12;\r
453         \r
454         // Check if there is space\r
455         for( i = 0; i < pages; i++ )\r
456         {\r
457                 if(MM_GetPhysAddr( (Uint)Dest + (i << 12) ))\r
458                         return 0;\r
459         }\r
460         \r
461         // Map\r
462         for( i = 0; i < pages; i++ )\r
463                 MM_Map( (Uint)Dest + (i<<12), VBE_DISPI_LFB_PHYSICAL_ADDRESS + (i<<12) );\r
464         \r
465         return 1;\r
466 }\r

UCC git Repository :: git.ucc.asn.au