Ditto, doesn't build yet.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17665 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
François Revol 2006-05-31 13:27:14 +00:00
parent e61dfc9edb
commit e108c13902
5 changed files with 1320 additions and 0 deletions

View File

@ -0,0 +1,21 @@
SubDir HAIKU_TOP src add-ons kernel drivers graphics cirrus ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders graphics ;
UsePrivateHeaders [ FDirName graphics cirrus ] ;
KernelAddon cirrus.driver : kernel drivers bin :
driver.c
;
Package haiku-cirrus-cvs :
README.html UPDATE.html ;
Package haiku-cirrus-cvs :
skel.driver :
boot home config add-ons kernel drivers bin ;
PackageDriverSymLink haiku-cirrus-cvs : graphics cirrus.driver ;
Package haiku-cirrus-cvs :
cirrus.settings :
boot home config settings kernel drivers ;

View File

@ -0,0 +1,150 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="The DarkSite">
<title>Readme for Haiku skeleton graphics driver</title>
</head>
<body>
<h2 align="center">Haiku skeleton graphics driver</h2></align><br><br>
<hr>
<h3><strong>NOTE PLEASE:</strong><br>
You use this software at your own risk! Although I don't expect it to damage your PC, videocard or Monitor, I cannot guarantee this!</h3>
<hr>
<h2>Supported cards (as far as is known):</h2>
<ul>
<li>None.
</ul>
<br>
<hr>
<h2>Features:</h2>
<ul>
<li>None.
</ul>
<strong>Known limitations:</strong>
<ul>
<li>Everything you can think of.
</ul>
<br>
<hr>
<h2>Installation:</h2>
<br>
In contrary to what I have said before you don't need to de-install official Be drivers for this driver to work correctly. This driver will install in the user part of the BeOS, so not in the system part where the official drivers are.<br>
BeOS first checks (during boot) if there are 'user-addons' that should be loaded for a device. If not, it loads it's own drivers (if any). You can select which driver should be loaded by hitting the spacebar as soon as the BeOS 'icons' screen appears. If you select <strong>disable user addons</strong> the system will load it's own drivers. If you don't do anything, the system will load the Haiku skeleton graphics driver.<br>
<br>
<strong>Note:</strong> This might turn out to be handy if you run into trouble upon testing the driver, or if you are 'tweaking' the skel.settings file...<br>
<br><br>
<strong>actual INSTALLATION:</strong><br>
<br>
Doubleclick on the install.sh file and follow the instructions. You have to reboot in order to load the driver. Make sure you read the <strong>Settings</strong> information below before you do that...<br>
<br>
<br>
<strong>alternate INSTALLATION method:</strong><br>
<br>
Unzip the zip file that contains the driver to the root folder. Now reboot and you should be using the new driver.<br>
<br>
<br>
<strong>DE-INSTALLATION:</strong><br>
<br>
Currently there's no uninstall script included. Just do it manually:<br>
<br>
Delete the <strong>skel.accelerant</strong> file in <strong>home/config/add-ons/accelerants/</strong><br>
Delete the <strong>skel.driver</strong> file in <strong>home/config/add-ons/kernel/drivers/bin/</strong><br>
Delete the <strong>skel.settings</strong> file in <strong>home/config/settings/kernel/drivers/</strong><br>
Delete the <strong>skel.driver shortcut</strong> in <strong>home/config/add-ons/kernel/drivers/dev/graphics/</strong> which pointed to the file <strong>skel.driver</strong>.<br>
<br>
You have to reboot in order to apply the original configuration.<br>
<br>
<br>
<hr>
<a name="settings"></a><h2>Settings:</h2><br>
Please read this information carefully *before* installing and using the Haiku skeleton graphics driver. It might spare you some trouble afterwards..<br>
<p>The driver uses a file named <strong>skel.settings</strong> to determine how to use your card. After installation this file will be located at <strong>home/config/settings/kernel/drivers/</strong>. How you should setup this file depends on what you want to do with the driver. While it has a 'failsave' default configuration, you might be able to do better than that... Anyway, read the nifty details below.<br>
<br>
<strong>Note:</strong> The driver only reads this file during it's initialisation. This means that you have to reboot in order to let changes take effect.<br>
<br>
<br>
<br>
<strong>skel.settings driver configuration:</strong><br>
<ul>
<li><strong>usebios:</strong><br>
The name of this item may be somewhat misleading, it might be changed in the future. It actually tells the driver if it should coldstart the card or not. The driver will rely on the VGA BIOS to have coldstarted the card before BeOS booted if you specify 'true'.<br>
To make things look even more complex the driver might actually use the BIOS to determine your cards specifications on *both* possible settings.
<ul>
<li><strong>false:</strong><br>
If you specify <strong>usebios false</strong> the driver will attempt to coldstart the card, which is the preferred way of doing it because of the better tuned setup if all is right. Unfortunately there's not enough info available to make this work reliably, so it's not used by default. This setting would enable you to use your card as a secondary card in your system. Be advised though that BeOS officially does not (yet) support multiple VGA cards, so you need special software in order to be able to actually use it (a video consumer node for instance).
<li><strong>true:</strong> (default setting)<br>
Specify <strong>usebios true</strong> unless you want to try to use a card as a secondary card in your system.
</ul>
<li><strong>memory:</strong> (disabled by default)<br>
This option enables you to override the 'memory amount autodetection' of the driver. If autodetection is working incorrect, you can manually set the amount this way. You could also lower the amount of RAM to a lower value than actually there to test with for instance overlay use in applications. So this option is probably mostly of interest to developers. Specify the RAM amount in Mb (use only 'whole' numbers!).<br>
This option is disabled by default (preceded by a '#').<br>
<li><strong>hardcursor:</strong><br>
A hardcursor is nessesary for DirectWindow windowed mode support.
<ul>
<li><strong>false:</strong><br>
If you have trouble with the hardcursor (on one or both of the heads), select <strong>hardcursor false</strong>. Make sure you let me know about the hardcursor trouble also: this should not happen!
<li><strong>true:</strong> (default setting)<br>
A software cursor 'flickers' a bit sometimes because it has to be redrawn constantly. So <strong>hardcursor true</strong> is the preferred setting. For DirectWindow windowed mode functionality you need to use this setting also (Chart demo app for instance).
</ul>
<li><strong>logmask:</strong> (set to minimal by default)<br>
The logmask option is very handy to track down trouble in the driver. You should only enable this if you are doing so, otherwise keep it turned off because it slows down your system. (All lines have a '#' preceding 'logmask' by default.) Logging creates a logfile called <strong>skel.accelerant.0.log</strong> in your <strong>~ (home)</strong> folder. A second logfile may get created depending on how the driver is used (on cloning; for BWindowScreen for example). The second file is called <strong>skel.accelerant.1.log</strong>, and it will also be in your home folder.<br>
<strong>Note:</strong>
<ul>
<li>You may only enable *one* logmask-line. The value you place after it (hexadecimal 32bit) determines what will be logged. The first 7 digits determine the part of the driver that will be logging, the last single digit determines the level of logging (like 'all messages', or only 'error messages').
</ul>
<li><strong>dumprom:</strong><br>
Dumprom is another 'tool' for bug-tracking purposes.
<ul>
<li><strong>false:</strong> (default setting)<br>
Keep it set to <strong>dumprom false</strong>, unless you want the driver to dump the contents of your VGA BIOS ROM in a file.
<li><strong>true:</strong><br>
<strong>dumprom true</strong> lets the driver dump a copy of your VGA BIOS in a file called <strong>skel.rom</strong> in your <strong>~ (home)</strong> folder.
</ul>
<li><strong>switchhead:</strong><br>
The driver always autodetects which output should be used as primary one, but you can let the driver 'invert' the outcome of that detection with this option (only for dualhead cards).
<ul>
<li><strong>false:</strong> (default setting)<br>
Keep it set to <strong>switchhead false</strong>, unless you feel you want the card's other output to be used as primary one. Note that if a single connected screen is found, that screen will be the driver's primary output with this setting.
<li><strong>true:</strong><br>
<strong>switchhead true</strong> lets the driver 'invert' the output assignments for all modes. Use only when you have two screens connected, otherwise the one connected screen will remain black as the other (not connected) output is being used.<br>
</ul>
<strong>Note:</strong>
<ul>
<li>If the driver determines it cannot use a digital panel <strong>despite</strong> it being physically connected and powered on, using the switchhead option will not fix this. This is no fault in your card or the panel, but happens only because the driver relies on certain functions inside your cards BIOS to behave in a certain way.
</ul>
<li><strong>force_pci:</strong><br>
On AGP cards you can block the use of AGP mode transfers.
<ul>
<li><strong>false:</strong> (default setting)<br>
Keep this option set to <strong>force_pci false</strong>, unless the graphics card or motherboard has trouble using AGP.
<li><strong>true:</strong><br>
<strong>force_pci true</strong> prevents the graphicsdriver from activating AGP mode, so it will be using PCI mode like it has always been in the past. The downside of this is that this comes at a performance penalty if your motherboard supports the AGP 'fastwrite' (FW) option, which won't be utilized with this setting.
</ul>
<strong>Note:</strong>
<ul>
<li>If you have trouble using AGP mode, you should prefer tweaking the AGP busmanager settings file as it might well enable you to use a 'lesser' AGP mode instead of falling back to PCI mode alltogether.
</ul>
<li><strong>unhide_fw:</strong><br>
This option is only used if your card is running in AGP mode. It's a real <strong>tweak</strong> option. It's officially unsupported, and it's unknown if it can do harm to your card or system. It exists because using it <strong>can</strong> speedup unaccelerated graphics <strong>a lot</strong>. Think about video playback or playing quake2 in software rendering mode...
<ul>
<li><strong>false:</strong> (default setting)<br>
Keep this option set to <strong>unhide_fw false</strong> unless you are certain you want to try the 'unsupported' graphics speedup. On cards supporting FW by default the unhide_fw option has no effect.
<li><strong>true:</strong><br>
If you have an older card that officially doesn't support the AGP FW feature, you could possibly get this feature anyway by setting <strong>unhide_fw true</strong>. Please <strong>make sure</strong> that at the first sign of trouble (system hanging, displaying artifacts, etc) you disable this feature here again, or you might risk <strong>destroying</strong> your card and/or AGP slot.
</ul>
<li><strong>pgm_panel:</strong><br>
This option only has an effect if you have a laptop panel or DVI panel connected. It's existing because it's currently impossible to setup the driver in a way every single panel outthere is happy about it.
<ul>
<li><strong>false:</strong><br>
If you select <strong>pgm_panel false</strong> the driver will not program the panel's pixelclock (refreshrate). Instead it relies on your cardBIOS to have done that correctly. While this is probably the case, it might introduce some displaying errors every now and then.
<li><strong>true:</strong> (default setting)<br>
With the <strong>pgm_panel true</strong> setting, the driver will fix your panel's refreshrate to 60Hz. While this should be working on all panels outthere, some panels are particular picky about refreshrates below 60.0Hz (they shut off), and some other panels are particular picky about refreshrates above 60.0Hz (they shut off). While the driver requests the hardware to set 60.0Hz, this isn't exactly possible, so the actual setting is <strong>bound</strong> to be a tiny bit below or above 60.0Hz.
</ul>
</ul>
<hr>
<br>
<a href="mailto:info.be-hold@inter.nl.net">Rudolf Cornelissen.</a>
<p>(Page last updated on November 4, 2004)</p>
</body>
</html>

View File

@ -0,0 +1,102 @@
<html>
<head>
<title>Changes</title>
</head>
<body>
<p><h2>Changes done for each driverversion:</h2></p>
<p><h1>skel_driver 0.00, (Rudolf)</h1></p>
<ul>
<li>based on nVidia driver 0.30;
<li>Kerneldriver uses MTR-WC mapping where available;
<li>Accelerant uses AGP busmanager to enable AGP FW where available;
<li>This is all this driver really does currently.
</ul>
<p><h1>Still todo:</h1></p>
<ul>
<li>Clean up this driver a bit;
<li>TVout support;
<li>Improve/extend various stuff when/if possible.
</ul>
</p>
<hr><br>
<strong>Welcome at the Haiku-OS skeleton graphicsdriver.</strong><br>
<br>
It contains a lot of non-working programming, almost all of which sits in the 'engine' folder of the accelerant. The main task to make this a working driver for any card outthere, is rewriting the engine. The code there is kept in place because in fact most of the time you only have to replace the actual card-register programming. And it's always handy to have an example of previous working code at hand when you do that: at least, that's my experience.<Br>
<br>
<strong>Using this driver as a starting point for development</strong><br>
<br>
You have to add your cardID and vendorID to both the kerneldriver (driver.c) and the accelerant (general.c). On top of that, you need to make the kerneldriver framebuffer mapping work. Probably the only thing you need to know is the correct area in which the buffer sits: probably the largest one available.<br>
<br>
Once you have this done in the driver, you should setup a VESA file for the resolution and depth you are going to work in (you will be developing the driver while you already work with it). Use the lowest resolution you can live with, and choose 8-bit colordepth.<br>
Now bootup the system in vesa mode or failsafe video mode (Startup option). Select the vesa mode you will be using <strong>explicitly</strong> for <strong>all</strong> workspaces in the Screen preferences app.<br>
<br>
OK, you should be set now. Install the new driver and reboot... You should have a normal Vesa like screen, only you'll find you have the kerneldriver loaded, the accelerant is running, and you can set modes via the Screen prefs panel: although it will only mess-up your screen. (Luckily the app falls-back to the old mode if you don't confirm the new mode ;-).<br>
<br>
If your CPU supports MTRR, the kerneldriver will have activated that, and if your card is AGP and you have the busmanager installed, the accelerant will have activated that. This means you should already have optimum speed for framebuffer access: working with BeOS should be smoother already than it was before when you where using <strong>real</strong> Vesa mode (and nothing else).<br>
<br>
<hr>
<br>
OK, your first task is get to the point I am describing above. Once you have that going, it's time to really start development. Personally, I have a certain list I always run-down. My advice would be to follow it more or less, as the order of doing things has a certain logic to it, making the steps as testable as can be using this development method.<br>
<br>
<ul>
<li><strong>step 1. Setup VESA mode.</strong><br>
You can do this via the 'vesa' settings file in /boot/home/config/settings/kernel/drivers/. Make sure there are no spaces or other characters after the last character defining the colordepth of the mode, or the mode will maybe not get recognized. A mode set here will get activated after the next normal reboot. A sample vesa settings file can be found in the 'sample' folder in the before mentioned folder.
<li><strong>step 2. Setup new driver with VESA running underneath, as described above.</strong><br>
This is possible because the VESA mode is set somewhere during the bootprocess (around icon 2 or 3 in the splashscreen), while the real (new) driver is loaded when the splashscreen disappears. The real driver doesn't actually have to do anything, as the system is already capable of running in the VESA mode set before. The one thing we actually do, is map the framebuffer ourself instead of relying on the access via VESA/legacy space; and point the app_server at it so it uses the newly mapped buffer to write to the screen. All other functions are simply shut-off, as far as they are card-type specific. Developing the driver means we have to fill in the gaps one by one, testing the new functions as we go.<br>
<br>
Note: the card has to support VESA VBE2 (VESA BIOS Extensions V2) somehow or this development method won't work...
<li><strong>step 3. Setup hardware cursor.</strong> (for BeOS and Zeta at least)<br>
As soon as the cursor hooks are exported, the app_server will start using the driver's cursor. As long as those hooks are not exported, the app_server will generate a fake cursor itself, which I'd call a software cursor. Software cursors tend to flicker when the content displayed 'below' it gets updated. The driver's cursor is a bitmap in the graphicshardware that gets overlayed on top of the screen, so this cursor has nothing to do with the content of the desktop.<br>
While it's hard or next to impossible to work with a driver that displays no cursor while the app_server is relying on it, you can get it going anyway. Of course, it will require some rebooting from time to time (get used to that ;-). I'd suggest first implementing the enable/disable bit for the cursor, and setting it's foreground and background color to white and black. Once activation of the cursor works you'll probably see a static (non-moving) random bitmap somewhere on the screen, measuring 32x32 or 64x64 pixels. Once you can see that, you can implement moving the cursor. As soon as that works, the driver is useable again more or less, as you can see what you point at.<br>
As last 'item' of the cursor you need to draw the cursor's bitmap (see below on starting adress info). On modern cards this bitmap is placed somewhere in the cards graphicsmemory, and mostly needs to be on a boundary of 2K or 4K. Some older cards have specific cursor memory inside the GPU (graphics processor unit), which you then probably access in a serial way (index and data registers).<br>
<br>
Once the cursor works, you'll need to make sure you are telling the hardware where it can find it. By default it will probably be at cardRAM adress offset zero. Which is also where the framebuffer is placed by default mostly. This means you will see distortions in the upper few lines of the desktop as long as you place the cursor bitmap there (it's just 2K in size mostly, because 4 colors are supported in the BeOS supported mode, needing 2 bits per pixel), but the upside of this is that this is your actual confirmation the app_server is writing cursor bitmaps to graphicsRAM. So, in theory you have two choices: move the cursor bitmap, or move the Desktop. It's probably the safest bet to move the desktop (in the next step: step 3), as the visible screenbuffer can be much more precisely placed. On some cards it might not be possible however to move the desktop from adress zero, or, the cursor. It's upto you to find that out.<br>
always keep in mind that you need to tell the location of the cursor and visible screenbuffer (Desktop) to two 'clients': the app_server AND the hardware.<br>
<br>
Things to consider with cursors might be these:
<ul>
<li>if you inform the card's GPU about the cursor's location, you do so by specifying the relative offset to the beginning of the cardRAM, not by specifying a physical adress. For informing the app_server you use the physical adress.
<li>redrawing needs to be done during/not during vertical retrace on some hardware (otherwise you could see distortions on some hardware);
<li>repositioning needs to be done during/not during vertical retrace on some hardware (likewise);
<li>the bitmap's reference point can be left-top or right-bottom. You'll have to correct the coordinates the app_server relays for this sometimes;
<li>the cursor bitmap may not be positioned below reference point 0,0 as the variables are mostly unsigned integers. If you do that anyway, the cursor will 'magically' disappear (wraparound). This problem occurs when you try to point at something at the left or top of the screen if it does: the source is the fact that cursors have a 'hot spot', indicating what exact postion in the bitmap the pointer is.
</ul>
<li><strong>step 4. Setup framebuffer startadress.</strong><br>
Once you have setup the framebuffer (relative offset) startadress correctly, the 'collision' of the hardware cursor and the desktop should be gone. Mostly you specify the starting adress in 32-bit words, as opposed to byte adresses. Some hardware even has larger granularity, you'll have to see for yourself. Most hardware can support byte-adressing however, but you need to tell the lowest bits to an old 'VGA' register then (called pixelpanning or so).<br>
You'll probably be wanting to do your best to setup byte-adressing, as this will offer smooth panning in virtual modes (MOVE_DISPLAY hook), without granularity steps taken. Byte adressing (panning) is tested in the next step (5). On the site I have for the nVidia/Matrox/Neomagic drivers you can find a small command line app called 'virtualscreen', that sets a virtual mode you request, which you can then test for this step in development, and for some other steps as well (like the next one).<br>
<br>
Setup a virtual screen with ONLY a large virtual height now: the width should NOT be changed (will not work yet due to missing register programming that will be added in the next step). Now move the cursor up and down so the screen starts to scroll (only possible if you have setup and activated a hardware cursor as the software cursor in the app_server does not support this feature). If you should see distortions now and then the hardware requires you to do the adress-updating during- or: not during- retrace (so synced). If syncing should be done make sure you add a timeout, as the startadress setting is also done during a SETMODE hook command, when no retraces will occur at all (screen is shut-off then!)<br>
<br>
Note that a screen with virtual WIDTH will use byte adressing (in 8 bit colordepth) indicating if the lower bits work OK. A screen with large virtual HEIGHT can be used to see if you got the upper bits right, or if you are missing a bit: screen wraparounds will occur then, showing you the top of the desktop while you are way below.<br>
If you scroll the screen memory startadresses will be increased or decreased with the width of the screen, or to be exact: width the 'bytes_per_row' variable in the Be 'frame_buffer_config' struct (and nothing else!).<br>
<br>
Hints:<br>
<ul>
<li>Know that the app_server will only work correctly on virtualscreens upto and including 10000 x 10000 pixels. Beyond that things still work, but the fill_color function will only 'handle' a rectangle upto/including 10000 x 10000 pixels;
<li>BeIDE for example cannot create windows with height beyond 2048 pixels. (it will nicely limit at that though.);
<li>Make sure that if you cannot program for byte granularity, you adjust the steps taken in MOVE_DISPLAY and MOVE_CURSOR to reflect this, otherwise you will for instance loose the cursor from the visible part of the screen on panning sometimes. Note that both functions work with pixels, while you program in bytes: the granularity is therefore colordepth dependant;
<li>Note that the VGA register pixelpanning probably works in bytes, but some hardware uses pixels here! You need to do some conversion in the calculations then.
</ul>
<li><strong>step 5. Setup framebuffer pitch.</strong><br>
The framebuffer pitch, so the distance (in bytes, or pixels depending on hardware) between two adjacent horizontal 'lines' usually is NOT programmed in bytes (or pixels), meaning that there is a step-granularity here. The driver is required to be able to set any width anyway, so you have to program the equal, or nearest bigger possible 'value' in the hardware. The 'slopspace' that might exist now, is indicated to the app_Server via the difference in the virtual screen width and the bytes_per_row variable also mentioned in the previous step.<br>
<br>
Once you think you have setup the framebuffer startadress and pitch both correctly, you are adviced to actually test that by setting a large virtual-width mode, and start panning slowly and fast both. Panning slowly will tell you if you are actually byte-adressing as the movement of the screen will be smooth, and fast moving will probably point you at distortions if the hardware requires you to do the adress-updating during/not during retrace (so synced). Also see if the cursor always stays nicely onscreen, or if it dissapears (partly) sometimes: if so the MOVE_CURSOR function granularity needs to be adjusted.<br>
<br>
Note that panning will only work if you have a hardware cursor in the driver and it's used. The software cursor in the app_server does not support panning in modes.
<li><strong>step 6. Setup colordepth.</strong><br>
<li><strong>step 7. Setup colorpalette.</strong><br>
<li><strong>step 8. Setup DPMS.</strong><br>
<li><strong>step 9. Setup refreshrate (pixelPLL).</strong><br>
<li><strong>step 10. Setup screentiming (CRTC, modeline).</strong><br>
<li><strong>step 11. Setup 'enhanced mode'.</strong><br>
<li><strong>step 12. Setup 2D acceleration.</strong><br>
<li><strong>step 13. Setup video overlay.</strong><br>
<li><strong>step 14. Setup card coldstart.</strong><br>
</ul>
<br>
Todo: complete list..<br>
<br>
<a href="mailto:info.be-hold@inter.nl.net">Rudolf Cornelissen.</a>
<p>(Page last updated on November 8, 2004)</p>
</body>
</html>

View File

@ -0,0 +1,57 @@
# Settings file for the cirrus driver and accelerant
#
# This file should be moved to the directory
# ~/config/settings/kernel/drivers/
#
# nv.driver parameters
# accelerant "nv.accelerant"
# nv.accelerant parameters
usebios true # if true rely on bios to coldstart the card
memory 2 # in MB, override builtin memory size detection
hardcursor false # if true use on-chip cursor capabilities
#logmask 0x00000000 # nothing logged, is default
#logmask 0x08000604 # log overlay use in full
#logmask 0xffffffff # log everything
dumprom false # dump bios rom in ~/nv.rom
switchhead false # switch head assignment (dualhead cards only)
force_pci false # block AGP mode use if true (AGP cards only)
# WARNING: tweak alert! modify stuff below on your own risk...
unhide_fw false # if true 'unhide' cards AGP fastwrite support on cards that hide it
pgm_panel true # if false don't program DVI and laptop panel pixelclocks (refreshrates)
#--------- that's all.
#logmask setup info:
#log level select:
#logmask 0x0000000x # lowest digit: bitmask to select logging level.
#log modules select:
#logmask 0xxxxxxxx0 # highest 7 digits: bitmask to select individual modules to log.
#log modules:
#logmask 0x00000100 # engine: agp
#logmask 0x00000200 # engine: bes
#logmask 0x00000400 # overlay
#logmask 0x00000800 # engine: support
#logmask 0x00001000 # engine: dac2
#logmask 0x00002000 # engine: info
#logmask 0x00004000 # engine: i2c
#logmask 0x00008000 # engine: general
#logmask 0x00010000 # engine: dac1
#logmask 0x00020000 # engine: crtc2
#logmask 0x00040000 # engine: crtc1
#logmask 0x00080000 # engine: acc
#logmask 0x00100000 # engine: brooktree tv
#logmask 0x00200000 # set displaymode
#logmask 0x00400000 # propose displaymode
#logmask 0x00800000 # init accelerant
#logmask 0x01000000 # get timing constraints
#logmask 0x02000000 # get mode info
#logmask 0x04000000 # get device info
#logmask 0x08000000 # get accelerant hook
#logmask 0x10000000 # engine management
#logmask 0x20000000 # cursor
#logmask 0x40000000 # acceleration

View File

@ -0,0 +1,990 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
Other authors:
Mark Watson;
Rudolf Cornelissen 3/2002-4/2006.
François Revol 2006.
*/
/* standard kernel driver stuff */
#include <KernelExport.h>
#include <ISA.h>
#include <PCI.h>
#include <OS.h>
#include <driver_settings.h>
#include <malloc.h>
#include <stdlib.h> // for strtoXX
#include "AGP.h"
/* this is for the standardized portion of the driver API */
/* currently only one operation is defined: B_GET_ACCELERANT_SIGNATURE */
#include <graphic_driver.h>
/* this is for sprintf() */
#include <stdio.h>
/* this is for string compares */
#include <string.h>
/* The private interface between the accelerant and the kernel driver. */
#include "DriverInterface.h"
#include "macros.h"
#define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
#define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
#define MAX_DEVICES 8
#define DEVICE_FORMAT "%04x_%04x_%02x%02x%02x" // apsed
/* Tell the kernel what revision of the driver API we support */
int32 api_version = B_CUR_DRIVER_API_VERSION; // apsed, was 2, is 2 in R5
/* these structures are private to the kernel driver */
typedef struct device_info device_info;
typedef struct {
timer te; /* timer entry for add_timer() */
device_info *di; /* pointer to the owning device */
bigtime_t when_target; /* when we're supposed to wake up */
} timer_info;
struct device_info {
uint32 is_open; /* a count of how many times the devices has been opened */
area_id shared_area; /* the area shared between the driver and all of the accelerants */
shared_info *si; /* a pointer to the shared area, for convenience */
vuint32 *regs; /* kernel's pointer to memory mapped registers */
pci_info pcii; /* a convenience copy of the pci info for this device */
char name[B_OS_NAME_LENGTH]; /* where we keep the name of the device for publishing and comparing */
};
typedef struct {
uint32 count; /* number of devices actually found */
benaphore kernel; /* for serializing opens/closes */
char *device_names[MAX_DEVICES+1]; /* device name pointer storage */
device_info di[MAX_DEVICES]; /* device specific stuff */
} DeviceData;
/* prototypes for our private functions */
static status_t open_hook (const char* name, uint32 flags, void** cookie);
static status_t close_hook (void* dev);
static status_t free_hook (void* dev);
static status_t read_hook (void* dev, off_t pos, void* buf, size_t* len);
static status_t write_hook (void* dev, off_t pos, const void* buf, size_t* len);
static status_t control_hook (void* dev, uint32 msg, void *buf, size_t len);
static status_t map_device(device_info *di);
static void unmap_device(device_info *di);
static void probe_devices(void);
static int32 eng_interrupt(void *data);
static DeviceData *pd;
static isa_module_info *isa_bus = NULL;
static pci_module_info *pci_bus = NULL;
static agp_module_info *agp_bus = NULL;
static device_hooks graphics_device_hooks = {
open_hook,
close_hook,
free_hook,
control_hook,
read_hook,
write_hook,
NULL,
NULL,
NULL,
NULL
};
#define VENDOR_ID_CIRRUS 0x1013 /* Cirrus */
static uint16 cirrus_device_list[] = {
CIRRUS_ID_CLGD5430,
CIRRUS_ID_CLGD5434,
//CIRRUS_ID_CLGD5436,
//CIRRUS_ID_CLGD5446,
CIRRUS_ID_CLGD5462,
CIRRUS_ID_CLGD5465,
0
};
static struct {
uint16 vendor;
uint16 *devices;
} SupportedDevices[] = {
{VENDOR_ID_CIRRUS, cirrus_device_list},
{0x0000, NULL}
};
static settings current_settings = { // see comments in skel.settings
// for driver
DRIVER_PREFIX ".accelerant",
false, // dumprom
// for accelerant
0x00000000, // logmask
0, // memory
true, // usebios
true, // hardcursor
false, // switchhead
false, // force_pci
false, // unhide_fw
true, // pgm_panel
};
static void dumprom (void *rom, uint32 size)
{
int fd;
uint32 cnt;
fd = open ("/boot/home/" DRIVER_PREFIX ".rom", O_WRONLY | O_CREAT, 0666);
if (fd < 0) return;
/* apparantly max. 32kb may be written at once;
* the ROM size is a multiple of that anyway. */
for (cnt = 0; (cnt < size); cnt += 32768)
write (fd, ((void *)(((uint8 *)rom) + cnt)), 32768);
close (fd);
}
/* return 1 if vblank interrupt has occured */
static int caused_vbi(vuint32 * regs)
{
// return (ENG_RG32(RG32_CRTC_INTS) & 0x00000001);
return 0;
}
/* clear the vblank interrupt */
static void clear_vbi(vuint32 * regs)
{
// ENG_RG32(RG32_CRTC_INTS) = 0x00000001;
}
static void enable_vbi(vuint32 * regs)
{
/* clear the vblank interrupt */
// ENG_RG32(RG32_CRTC_INTS) = 0x00000001;
/* enable nVidia interrupt source vblank */
// ENG_RG32(RG32_CRTC_INTE) |= 0x00000001;
/* enable nVidia interrupt system hardware (b0-1) */
// ENG_RG32(RG32_MAIN_INTE) = 0x00000001;
}
static void disable_vbi(vuint32 * regs)
{
/* disable nVidia interrupt source vblank */
// ENG_RG32(RG32_CRTC_INTE) &= 0xfffffffe;
/* clear the vblank interrupt */
// ENG_RG32(RG32_CRTC_INTS) = 0x00000001;
/* disable nVidia interrupt system hardware (b0-1) */
// ENG_RG32(RG32_MAIN_INTE) = 0x00000000;
}
/*
init_hardware() - Returns B_OK if one is
found, otherwise returns B_ERROR so the driver will be unloaded.
*/
status_t
init_hardware(void) {
long pci_index = 0;
pci_info pcii;
bool found_one = false;
/* choke if we can't find the PCI bus */
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
return B_ERROR;
/* choke if we can't find the ISA bus */
if (get_module(B_ISA_MODULE_NAME, (module_info **)&isa_bus) != B_OK)
{
put_module(B_PCI_MODULE_NAME);
return B_ERROR;
}
/* while there are more pci devices */
while ((*pci_bus->get_nth_pci_info)(pci_index, &pcii) == B_NO_ERROR) {
int vendor = 0;
/* if we match a supported vendor */
while (SupportedDevices[vendor].vendor) {
if (SupportedDevices[vendor].vendor == pcii.vendor_id) {
uint16 *devices = SupportedDevices[vendor].devices;
/* while there are more supported devices */
while (*devices) {
/* if we match a supported device */
if (*devices == pcii.device_id ) {
found_one = true;
goto done;
}
/* next supported device */
devices++;
}
}
vendor++;
}
/* next pci_info struct, please */
pci_index++;
}
done:
/* put away the module manager */
put_module(B_PCI_MODULE_NAME);
return (found_one ? B_OK : B_ERROR);
}
status_t
init_driver(void) {
void *settings_handle;
// get driver/accelerant settings, apsed
settings_handle = load_driver_settings (DRIVER_PREFIX ".settings");
if (settings_handle != NULL) {
const char *item;
char *end;
uint32 value;
// for driver
item = get_driver_parameter (settings_handle, "accelerant", "", "");
if ((strlen (item) > 0) && (strlen (item) < sizeof (current_settings.accelerant) - 1)) {
strcpy (current_settings.accelerant, item);
}
current_settings.dumprom = get_driver_boolean_parameter (settings_handle, "dumprom", false, false);
// for accelerant
item = get_driver_parameter (settings_handle, "logmask", "0x00000000", "0x00000000");
value = strtoul (item, &end, 0);
if (*end == '\0') current_settings.logmask = value;
item = get_driver_parameter (settings_handle, "memory", "0", "0");
value = strtoul (item, &end, 0);
if (*end == '\0') current_settings.memory = value;
current_settings.hardcursor = get_driver_boolean_parameter (settings_handle, "hardcursor", false, false);
current_settings.usebios = get_driver_boolean_parameter (settings_handle, "usebios", false, false);
current_settings.switchhead = get_driver_boolean_parameter (settings_handle, "switchhead", false, false);
current_settings.force_pci = get_driver_boolean_parameter (settings_handle, "force_pci", false, false);
current_settings.unhide_fw = get_driver_boolean_parameter (settings_handle, "unhide_fw", false, false);
current_settings.pgm_panel = get_driver_boolean_parameter (settings_handle, "pgm_panel", false, false);
unload_driver_settings (settings_handle);
}
/* get a handle for the pci bus */
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
return B_ERROR;
/* get a handle for the isa bus */
if (get_module(B_ISA_MODULE_NAME, (module_info **)&isa_bus) != B_OK)
{
put_module(B_PCI_MODULE_NAME);
return B_ERROR;
}
/* get a handle for the agp bus if it exists */
get_module(B_AGP_MODULE_NAME, (module_info **)&agp_bus);
/* driver private data */
pd = (DeviceData *)calloc(1, sizeof(DeviceData));
if (!pd) {
put_module(B_PCI_MODULE_NAME);
return B_ERROR;
}
/* initialize the benaphore */
INIT_BEN(pd->kernel);
/* find all of our supported devices */
probe_devices();
return B_OK;
}
const char **
publish_devices(void) {
/* return the list of supported devices */
return (const char **)pd->device_names;
}
device_hooks *
find_device(const char *name) {
int index = 0;
while (pd->device_names[index]) {
if (strcmp(name, pd->device_names[index]) == 0)
return &graphics_device_hooks;
index++;
}
return NULL;
}
void uninit_driver(void) {
/* free the driver data */
DELETE_BEN(pd->kernel);
free(pd);
pd = NULL;
/* put the pci module away */
put_module(B_PCI_MODULE_NAME);
put_module(B_ISA_MODULE_NAME);
/* put the agp module away if it's there */
if (agp_bus) put_module(B_AGP_MODULE_NAME);
}
static status_t map_device(device_info *di)
{
char buffer[B_OS_NAME_LENGTH]; /*memory for device name*/
shared_info *si = di->si;
uint32 tmpUlong;
pci_info *pcii = &(di->pcii);
system_info sysinfo;
/*storage for the physical to virtual table (used for dma buffer)*/
// physical_entry physical_memory[2];
// #define G400_DMA_BUFFER_SIZE 1024*1024
/* variables for making copy of ROM */
uint8* rom_temp;
area_id rom_area;
/* Nvidia cards have registers in [0] and framebuffer in [1] */
int registers = 1;
int frame_buffer = 0;
// int pseudo_dma = 2;
/* enable memory mapped IO, disable VGA I/O - this is defined in the PCI standard */
tmpUlong = get_pci(PCI_command, 2);
/* enable PCI access */
tmpUlong |= PCI_command_memory;
/* enable busmastering */
tmpUlong |= PCI_command_master;
/* disable ISA I/O access */
tmpUlong &= ~PCI_command_io;
set_pci(PCI_command, 2, tmpUlong);
/*work out which version of BeOS is running*/
get_system_info(&sysinfo);
if (0)//sysinfo.kernel_build_date[0]=='J')/*FIXME - better ID version*/
{
si->use_clone_bugfix = 1;
}
else
{
si->use_clone_bugfix = 0;
}
/* work out a name for the register mapping */
sprintf(buffer, DEVICE_FORMAT " regs",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
/* get a virtual memory address for the registers*/
si->regs_area = map_physical_memory(
buffer,
/* WARNING: Nvidia needs to map regs as viewed from PCI space! */
(void *) di->pcii.u.h0.base_registers_pci[registers],
di->pcii.u.h0.base_register_sizes[registers],
B_ANY_KERNEL_ADDRESS,
(si->use_clone_bugfix ? B_READ_AREA|B_WRITE_AREA : 0),
(void **)&(di->regs));
si->clone_bugfix_regs = (uint32 *) di->regs;
/* if mapping registers to vmem failed then pass on error */
if (si->regs_area < 0) return si->regs_area;
/* work out a name for the ROM mapping*/
sprintf(buffer, DEVICE_FORMAT " rom",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
/* disable ROM shadowing, we want the guaranteed exact contents of the chip */
/* warning:
* don't touch: (confirmed) NV04, NV05, NV05-M64, NV11 all shutoff otherwise.
* NV18, NV28 and NV34 keep working.
* confirmed NV28 and NV34 to use upper part of shadowed ROM for scratch purposes,
* however the actual ROM content (so the used part) is intact (confirmed). */
//set_pci(ENCFG_ROMSHADOW, 4, 0);
/* get ROM memory mapped base adress - this is defined in the PCI standard */
tmpUlong = get_pci(PCI_rom_base, 4);
if (tmpUlong)
{
/* ROM was assigned an adress, so enable ROM decoding - see PCI standard */
tmpUlong |= 0x00000001;
set_pci(PCI_rom_base, 4, tmpUlong);
rom_area = map_physical_memory(
buffer,
(void *)di->pcii.u.h0.rom_base_pci,
di->pcii.u.h0.rom_size,
B_ANY_KERNEL_ADDRESS,
B_READ_AREA,
(void **)&(rom_temp)
);
/* check if we got the BIOS signature (might fail on laptops..) */
if (rom_temp[0]!=0x55 || rom_temp[1]!=0xaa)
{
/* apparantly no ROM is mapped here */
delete_area(rom_area);
rom_area = -1;
/* force using ISA legacy map as fall-back */
tmpUlong = 0x00000000;
}
}
if (!tmpUlong)
{
/* ROM was not assigned an adress, fetch it from ISA legacy memory map! */
rom_area = map_physical_memory(
buffer,
(void *)0x000c0000,
65536,
B_ANY_KERNEL_ADDRESS,
B_READ_AREA,
(void **)&(rom_temp)
);
}
/* if mapping ROM to vmem failed then clean up and pass on error */
if (rom_area < 0) {
delete_area(si->regs_area);
si->regs_area = -1;
return rom_area;
}
/* dump ROM to file if selected in skel.settings
* (ROM always fits in 64Kb: checked TNT1 - FX5950) */
if (current_settings.dumprom) dumprom (rom_temp, 65536);
/* make a copy of ROM for future reference */
memcpy (si->rom_mirror, rom_temp, 65536);
/* disable ROM decoding - this is defined in the PCI standard, and delete the area */
tmpUlong = get_pci(PCI_rom_base, 4);
tmpUlong &= 0xfffffffe;
set_pci(PCI_rom_base, 4, tmpUlong);
delete_area(rom_area);
/* work out a name for the framebuffer mapping*/
sprintf(buffer, DEVICE_FORMAT " framebuffer",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
/* map the framebuffer into vmem, using Write Combining*/
si->fb_area = map_physical_memory(
buffer,
/* WARNING: Nvidia needs to map framebuffer as viewed from PCI space! */
(void *) di->pcii.u.h0.base_registers_pci[frame_buffer],
di->pcii.u.h0.base_register_sizes[frame_buffer],
B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
B_READ_AREA + B_WRITE_AREA,
&(si->framebuffer));
/*if failed with write combining try again without*/
if (si->fb_area < 0) {
si->fb_area = map_physical_memory(
buffer,
/* WARNING: Nvidia needs to map framebuffer as viewed from PCI space! */
(void *) di->pcii.u.h0.base_registers_pci[frame_buffer],
di->pcii.u.h0.base_register_sizes[frame_buffer],
B_ANY_KERNEL_BLOCK_ADDRESS,
B_READ_AREA + B_WRITE_AREA,
&(si->framebuffer));
}
/* if there was an error, delete our other areas and pass on error*/
if (si->fb_area < 0)
{
delete_area(si->regs_area);
si->regs_area = -1;
return si->fb_area;
}
//fixme: retest for card coldstart and PCI/virt_mem mapping!!
/* remember the DMA address of the frame buffer for BDirectWindow?? purposes */
si->framebuffer_pci = (void *) di->pcii.u.h0.base_registers_pci[frame_buffer];
// remember settings for use here and in accelerant
si->settings = current_settings;
/* in any case, return the result */
return si->fb_area;
}
static void unmap_device(device_info *di) {
shared_info *si = di->si;
uint32 tmpUlong;
pci_info *pcii = &(di->pcii);
/* disable memory mapped IO */
tmpUlong = get_pci(PCI_command, 4);
tmpUlong &= 0xfffffffc;
set_pci(PCI_command, 4, tmpUlong);
/* delete the areas */
if (si->regs_area >= 0) delete_area(si->regs_area);
if (si->fb_area >= 0) delete_area(si->fb_area);
si->regs_area = si->fb_area = -1;
si->framebuffer = NULL;
di->regs = NULL;
}
static void probe_devices(void) {
uint32 pci_index = 0;
uint32 count = 0;
device_info *di = pd->di;
/* while there are more pci devices */
while ((count < MAX_DEVICES) && ((*pci_bus->get_nth_pci_info)(pci_index, &(di->pcii)) == B_NO_ERROR)) {
int vendor = 0;
/* if we match a supported vendor */
while (SupportedDevices[vendor].vendor) {
if (SupportedDevices[vendor].vendor == di->pcii.vendor_id) {
uint16 *devices = SupportedDevices[vendor].devices;
/* while there are more supported devices */
while (*devices) {
/* if we match a supported device */
if (*devices == di->pcii.device_id ) {
/* publish the device name */
sprintf(di->name, "graphics/" DEVICE_FORMAT,
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
/* remember the name */
pd->device_names[count] = di->name;
/* mark the driver as available for R/W open */
di->is_open = 0;
/* mark areas as not yet created */
di->shared_area = -1;
/* mark pointer to shared data as invalid */
di->si = NULL;
/* inc pointer to device info */
di++;
/* inc count */
count++;
/* break out of these while loops */
goto next_device;
}
/* next supported device */
devices++;
}
}
vendor++;
}
next_device:
/* next pci_info struct, please */
pci_index++;
}
/* propagate count */
pd->count = count;
/* terminate list of device names with a null pointer */
pd->device_names[pd->count] = NULL;
}
static uint32 thread_interrupt_work(int32 *flags, vuint32 *regs, shared_info *si) {
uint32 handled = B_HANDLED_INTERRUPT;
/* release the vblank semaphore */
if (si->vblank >= 0) {
int32 blocked;
if ((get_sem_count(si->vblank, &blocked) == B_OK) && (blocked < 0)) {
release_sem_etc(si->vblank, -blocked, B_DO_NOT_RESCHEDULE);
handled = B_INVOKE_SCHEDULER;
}
}
return handled;
}
static int32
eng_interrupt(void *data)
{
int32 handled = B_UNHANDLED_INTERRUPT;
device_info *di = (device_info *)data;
shared_info *si = di->si;
int32 *flags = &(si->flags);
vuint32 *regs;
/* is someone already handling an interrupt for this device? */
if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED) {
goto exit0;
}
/* get regs */
regs = di->regs;
/* was it a VBI? */
if (caused_vbi(regs)) {
/*clear the interrupt*/
clear_vbi(regs);
/*release the semaphore*/
handled = thread_interrupt_work(flags, regs, si);
}
/* note that we're not in the handler any more */
atomic_and(flags, ~SKD_HANDLER_INSTALLED);
exit0:
return handled;
}
static status_t open_hook (const char* name, uint32 flags, void** cookie) {
int32 index = 0;
device_info *di;
shared_info *si;
thread_id thid;
thread_info thinfo;
status_t result = B_OK;
vuint32 *regs;
char shared_name[B_OS_NAME_LENGTH];
/* find the device name in the list of devices */
/* we're never passed a name we didn't publish */
while (pd->device_names[index] && (strcmp(name, pd->device_names[index]) != 0)) index++;
/* for convienience */
di = &(pd->di[index]);
/* make sure no one else has write access to the common data */
AQUIRE_BEN(pd->kernel);
/* if it's already open for writing */
if (di->is_open) {
/* mark it open another time */
goto mark_as_open;
}
/* create the shared area */
sprintf(shared_name, DEVICE_FORMAT " shared",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
/* create this area with NO user-space read or write permissions, to prevent accidental dammage */
di->shared_area = create_area(shared_name, (void **)&(di->si), B_ANY_KERNEL_ADDRESS, ((sizeof(shared_info) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)), B_FULL_LOCK, 0);
if (di->shared_area < 0) {
/* return the error */
result = di->shared_area;
goto done;
}
/* save a few dereferences */
si = di->si;
/* save the vendor and device IDs */
si->vendor_id = di->pcii.vendor_id;
si->device_id = di->pcii.device_id;
si->revision = di->pcii.revision;
si->bus = di->pcii.bus;
si->device = di->pcii.device;
si->function = di->pcii.function;
/* note the amount of system RAM the system BIOS assigned to the card if applicable:
* unified memory architecture (UMA) */
switch ((((uint32)(si->device_id)) << 16) | si->vendor_id)
{
case 0x01a010de: /* Nvidia GeForce2 Integrated GPU */
/* device at bus #0, device #0, function #1 holds value at byte-index 0x7C */
si->ps.memory_size = 1024 * 1024 *
(((((*pci_bus->read_pci_config)(0, 0, 1, 0x7c, 4)) & 0x000007c0) >> 6) + 1);
/* last 64kB RAM is used for the BIOS (or something else?) */
si->ps.memory_size -= (64 * 1024);
break;
case 0x01f010de: /* Nvidia GeForce4 MX Integrated GPU */
/* device at bus #0, device #0, function #1 holds value at byte-index 0x84 */
si->ps.memory_size = 1024 * 1024 *
(((((*pci_bus->read_pci_config)(0, 0, 1, 0x84, 4)) & 0x000007f0) >> 4) + 1);
/* last 64kB RAM is used for the BIOS (or something else?) */
si->ps.memory_size -= (64 * 1024);
break;
default:
/* all other cards have own RAM: the amount of which is determined in the
* accelerant. */
break;
}
/* map the device */
result = map_device(di);
if (result < 0) goto free_shared;
result = B_OK;
/* create a semaphore for vertical blank management */
si->vblank = create_sem(0, di->name);
if (si->vblank < 0) {
result = si->vblank;
goto unmap;
}
/* change the owner of the semaphores to the opener's team */
/* this is required because apps can't aquire kernel semaphores */
thid = find_thread(NULL);
get_thread_info(thid, &thinfo);
set_sem_owner(si->vblank, thinfo.team);
/* assign local regs pointer for SAMPLExx() macros */
regs = di->regs;
/* disable and clear any pending interrupts */
disable_vbi(regs);
/* If there is a valid interrupt line assigned then set up interrupts */
if ((di->pcii.u.h0.interrupt_pin == 0x00) ||
(di->pcii.u.h0.interrupt_line == 0xff) || /* no IRQ assigned */
(di->pcii.u.h0.interrupt_line <= 0x02)) /* system IRQ assigned */
{
/* we are aborting! */
/* Note: the R4 graphics driver kit lacks this statement!! */
result = B_ERROR;
/* interrupt does not exist so exit without installing our handler */
goto delete_the_sem;
}
else
{
/* otherwise install our interrupt handler */
result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line, eng_interrupt, (void *)di, 0);
/* bail if we couldn't install the handler */
if (result != B_OK) goto delete_the_sem;
}
mark_as_open:
/* mark the device open */
di->is_open++;
/* send the cookie to the opener */
*cookie = di;
goto done;
delete_the_sem:
delete_sem(si->vblank);
unmap:
unmap_device(di);
free_shared:
/* clean up our shared area */
delete_area(di->shared_area);
di->shared_area = -1;
di->si = NULL;
done:
/* end of critical section */
RELEASE_BEN(pd->kernel);
/* all done, return the status */
return result;
}
/* ----------
read_hook - does nothing, gracefully
----- */
static status_t
read_hook (void* dev, off_t pos, void* buf, size_t* len)
{
*len = 0;
return B_NOT_ALLOWED;
}
/* ----------
write_hook - does nothing, gracefully
----- */
static status_t
write_hook (void* dev, off_t pos, const void* buf, size_t* len)
{
*len = 0;
return B_NOT_ALLOWED;
}
/* ----------
close_hook - does nothing, gracefully
----- */
static status_t
close_hook (void* dev)
{
/* we don't do anything on close: there might be dup'd fd */
return B_NO_ERROR;
}
/* -----------
free_hook - close down the device
----------- */
static status_t
free_hook (void* dev) {
device_info *di = (device_info *)dev;
shared_info *si = di->si;
vuint32 *regs = di->regs;
/* lock the driver */
AQUIRE_BEN(pd->kernel);
/* if opened multiple times, decrement the open count and exit */
if (di->is_open > 1)
goto unlock_and_exit;
/* disable and clear any pending interrupts */
disable_vbi(regs);
/* remove interrupt handler */
remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, eng_interrupt, di);
/* delete the semaphores, ignoring any errors ('cause the owning team may have died on us) */
delete_sem(si->vblank);
si->vblank = -1;
/* free regs and framebuffer areas */
unmap_device(di);
/* clean up our shared area */
delete_area(di->shared_area);
di->shared_area = -1;
di->si = NULL;
unlock_and_exit:
/* mark the device available */
di->is_open--;
/* unlock the driver */
RELEASE_BEN(pd->kernel);
/* all done */
return B_OK;
}
/* -----------
control_hook - where the real work is done
----------- */
static status_t
control_hook (void* dev, uint32 msg, void *buf, size_t len) {
device_info *di = (device_info *)dev;
status_t result = B_DEV_INVALID_IOCTL;
uint32 tmpUlong;
switch (msg) {
/* the only PUBLIC ioctl */
case B_GET_ACCELERANT_SIGNATURE: {
char *sig = (char *)buf;
strcpy(sig, current_settings.accelerant);
result = B_OK;
} break;
/* PRIVATE ioctl from here on */
case ENG_GET_PRIVATE_DATA: {
eng_get_private_data *gpd = (eng_get_private_data *)buf;
if (gpd->magic == SKEL_PRIVATE_DATA_MAGIC) {
gpd->shared_info_area = di->shared_area;
result = B_OK;
}
} break;
case ENG_GET_PCI: {
eng_get_set_pci *gsp = (eng_get_set_pci *)buf;
if (gsp->magic == SKEL_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
gsp->value = get_pci(gsp->offset, gsp->size);
result = B_OK;
}
} break;
case ENG_SET_PCI: {
eng_get_set_pci *gsp = (eng_get_set_pci *)buf;
if (gsp->magic == SKEL_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
set_pci(gsp->offset, gsp->size, gsp->value);
result = B_OK;
}
} break;
case ENG_DEVICE_NAME: { // apsed
eng_device_name *dn = (eng_device_name *)buf;
if (dn->magic == SKEL_PRIVATE_DATA_MAGIC) {
strcpy(dn->name, di->name);
result = B_OK;
}
} break;
case ENG_RUN_INTERRUPTS: {
eng_set_bool_state *ri = (eng_set_bool_state *)buf;
if (ri->magic == SKEL_PRIVATE_DATA_MAGIC) {
vuint32 *regs = di->regs;
if (ri->do_it) {
enable_vbi(regs);
} else {
disable_vbi(regs);
}
result = B_OK;
}
} break;
case ENG_GET_NTH_AGP_INFO: {
eng_nth_agp_info *nai = (eng_nth_agp_info *)buf;
if (nai->magic == SKEL_PRIVATE_DATA_MAGIC) {
nai->exist = false;
nai->agp_bus = false;
if (agp_bus) {
nai->agp_bus = true;
if ((*agp_bus->get_nth_agp_info)(nai->index, &(nai->agpi)) == B_NO_ERROR) {
nai->exist = true;
}
}
result = B_OK;
}
} break;
case ENG_ENABLE_AGP: {
eng_cmd_agp *nca = (eng_cmd_agp *)buf;
if (nca->magic == SKEL_PRIVATE_DATA_MAGIC) {
if (agp_bus) {
nca->agp_bus = true;
(*agp_bus->enable_agp)(&(nca->cmd));
} else {
nca->agp_bus = false;
nca->cmd = 0;
}
result = B_OK;
}
} break;
case ENG_ISA_OUT: {
eng_in_out_isa *io_isa = (eng_in_out_isa *)buf;
if (io_isa->magic == SKEL_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
/* lock the driver:
* no other graphics card may have ISA I/O enabled when we enter */
AQUIRE_BEN(pd->kernel);
/* enable ISA I/O access */
tmpUlong = get_pci(PCI_command, 2);
tmpUlong |= PCI_command_io;
set_pci(PCI_command, 2, tmpUlong);
if (io_isa->size == 1)
isa_bus->write_io_8(io_isa->adress, (uint8)io_isa->data);
else
isa_bus->write_io_16(io_isa->adress, io_isa->data);
result = B_OK;
/* disable ISA I/O access */
tmpUlong = get_pci(PCI_command, 2);
tmpUlong &= ~PCI_command_io;
set_pci(PCI_command, 2, tmpUlong);
/* end of critical section */
RELEASE_BEN(pd->kernel);
}
} break;
case ENG_ISA_IN: {
eng_in_out_isa *io_isa = (eng_in_out_isa *)buf;
if (io_isa->magic == SKEL_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
/* lock the driver:
* no other graphics card may have ISA I/O enabled when we enter */
AQUIRE_BEN(pd->kernel);
/* enable ISA I/O access */
tmpUlong = get_pci(PCI_command, 2);
tmpUlong |= PCI_command_io;
set_pci(PCI_command, 2, tmpUlong);
if (io_isa->size == 1)
io_isa->data = isa_bus->read_io_8(io_isa->adress);
else
io_isa->data = isa_bus->read_io_16(io_isa->adress);
result = B_OK;
/* disable ISA I/O access */
tmpUlong = get_pci(PCI_command, 2);
tmpUlong &= ~PCI_command_io;
set_pci(PCI_command, 2, tmpUlong);
/* end of critical section */
RELEASE_BEN(pd->kernel);
}
} break;
}
return result;
}