blob: d72fd9cdfe0c1bc9e0d106edb79e1bbbd2b67e50 [file] [log] [blame]
/* Simple PCI video driver for use with Mac-On-Linux emulator
*
* Basically, this driver forward Apple video driver calls to
* the emulator via a fake HW (and later, a "sc" based API).
*/
#include "VideoDriverPrivate.h"
#include "VideoDriverPrototypes.h"
#include "DriverQDCalls.h"
#include "QemuVga.h"
DriverDescription TheDriverDescription = {
/*
* Signature info
*/
kTheDescriptionSignature, /* OSType driverDescSignature */
kInitialDriverDescriptor, /* DriverDescVersion driverDescVersion */
QEMU_PCI_VIDEO_NAME,
0x01, 0x01,
0, 0,
/*
* DriverOSRuntime driverOSRuntimeInfo
*/
0 /* RuntimeOptions driverRuntime */
| (0 * kDriverIsLoadedUponDiscovery) /* Loader runtime options */
| (1 * kDriverIsOpenedUponLoad) /* Opened when loaded */
| (1 * kDriverIsUnderExpertControl) /* I/O expert handles loads/opens */
| (0 * kDriverIsConcurrent) /* concurrent */
| (0 * kDriverQueuesIOPB), /* Internally queued */
QEMU_PCI_VIDEO_PNAME, /* Str31 driverName (OpenDriver param) */
0, 0, 0, 0, 0, 0, 0, 0, /* UInt32 driverDescReserved[8] */
/*
* DriverOSService Information. This section contains a vector count followed by
* a vector of structures, each defining a driver service.
*/
1, /* ServiceCount nServices */
/*
* DriverServiceInfo service[0]
*/
kServiceCategoryNdrvDriver, /* OSType serviceCategory */
kNdrvTypeIsVideo, /* OSType serviceType */
1, 0, 0, 0
};
/*
* All driver-global information is in a structure defined in NCRDriverPrivate.
* Note that "modern" drivers do not have access to their dce. In native Power PC
* environments, the global world is created by the Code Fragment Manager (hmm,
* perhaps it is created by CFMInitialize).
*/
DriverGlobal gDriverGlobal;
/*
* DoDriverIO
*
* In the new driver environment, DoDriverIO performs all driver
* functions. It is called with the following parameters:
* IOCommandID A unique reference for this driver request. In
* the emulated environment, this will be the ParamBlkPtr
* passed in from the Device Manager.
*
* IOCommandContents A union structure that contains information for the
* specific request. For the emulated environment, this
* will contain the following:
* Initialize Driver RefNum and the name registry id for this driver.
* Finalize Driver RefNum and the name registry id for this driver.
* Others The ParamBlkPtr
*
* IOCommandCode A switch value that specifies the required function.
*
* IOCommandKind A bit-mask indicating Synchronous, Asynchronous, and Immediate
*
* For Synchronous and Immediate commands, DoDriverIO returns the final status to
* the Device Manager. For Asynchronous commands, DoDriverIO may return kIOBusyStatus.
* If it returns busy status, the driver promises to call IOCommandIsComplete when
* the transaction has completed.
*/
OSStatus
DoDriverIO( AddressSpaceID addressSpaceID, IOCommandID ioCommandID, IOCommandContents ioCommandContents,
IOCommandCode ioCommandCode, IOCommandKind ioCommandKind )
{
OSStatus status;
/*
* Note: Initialize, Open, KillIO, Close, and Finalize are either synchronous
* or immediate. Read, Write, Control, and Status may be immediate,
* synchronous, or asynchronous.
*/
lprintf("DoDriverIO cmdCode=%d\n", ioCommandCode);
switch( ioCommandCode ) {
case kInitializeCommand: /* Always immediate */
status = DriverInitializeCmd(addressSpaceID, ioCommandContents.initialInfo);
CheckStatus(status, "Initialize failed");
break;
case kFinalizeCommand: /* Always immediate */
status = DriverFinalizeCmd(ioCommandContents.finalInfo);
break;
case kSupersededCommand:
status = DriverSupersededCmd(ioCommandContents.supersededInfo, FALSE);
break;
case kReplaceCommand: /* replace an old driver */
status = DriverReplaceCmd(addressSpaceID, ioCommandContents.replaceInfo);
break;
case kOpenCommand: /* Always immediate */
status = DriverOpenCmd(addressSpaceID, ioCommandContents.pb);
break;
case kCloseCommand: /* Always immediate */
status = DriverCloseCmd(ioCommandContents.pb);
break;
case kControlCommand:
/* lprintf("kControlCommand\n"); */
status = DriverControlCmd( addressSpaceID, ioCommandID, ioCommandKind,
(CntrlParam*)ioCommandContents.pb );
break;
case kStatusCommand:
status = DriverStatusCmd( ioCommandID, ioCommandKind,
(CntrlParam *)ioCommandContents.pb );
break;
case kReadCommand:
status = DriverReadCmd( addressSpaceID, ioCommandID, ioCommandKind,
ioCommandContents.pb );
break;
case kWriteCommand:
status = DriverWriteCmd( addressSpaceID, ioCommandID, ioCommandKind,
ioCommandContents.pb);
break;
case kKillIOCommand: /* Always immediate */
status = DriverKillIOCmd(ioCommandContents.pb);
break;
default:
status = paramErr;
break;
}
lprintf("Completing with status=%d (kind: %x)\n", status, ioCommandKind);
/*
* Force a valid result for immediate commands -- they must return a valid
* status to the Driver Manager: returning kIOBusyStatus would be a bug..
* Non-immediate commands return a status from the lower-level routine. If the
* status is kIOBusyStatus, we just return -- an asynchronous I/O completion
* routine will eventually complete the request. If it's some other status, the
* lower-level routine has completed a non-immediate task, so we call
* IOCommandIsComplete and return its (presumably noErr) status.
*/
if( (ioCommandKind & kImmediateIOCommandKind) != 0 ) {
; /* Immediate commands return the operation status */
}
else if (status == ioInProgress) {
/*
* An asynchronous operation is in progress. The driver handler promises
* to call IOCommandIsComplete when the operation concludes.
*/
status = noErr;
} else {
/*
* Normal command that completed synchronously. Dequeue the user's
* parameter block.
*/
status = (OSStatus)IOCommandIsComplete(ioCommandID, (OSErr)status);
}
return status;
}
/*
* DriverInitializeCmd
*
* The New Driver Manager calls this when the driver is first opened.
*/
OSStatus
DriverInitializeCmd( AddressSpaceID addressSpaceID, DriverInitInfoPtr driverInitInfoPtr )
{
OSStatus status;
Trace(DriverInitializeCmd);
lprintf("** First call:\n");
lprintf(" DoDriverIO @ %p\n", DoDriverIO);
lprintf(" DriverStatusCmd @ %p\n", DriverStatusCmd);
lprintf(" DriverControlCmd @ %p\n", DriverControlCmd);
GLOBAL.refNum = driverInitInfoPtr->refNum;
GLOBAL.openCount = 0;
GLOBAL.inInterrupt = false;
GLOBAL.hasTimer = false;
RegistryEntryIDInit( &GLOBAL.deviceEntry );
status = RegistryEntryIDCopy( &driverInitInfoPtr->deviceEntry, &GLOBAL.deviceEntry );
if( status != noErr )
return status;
GLOBAL.isOpen = false;
GLOBAL.qdInterruptsEnable = false;
GLOBAL.qdVBLInterrupt = NULL;
GLOBAL.boardFBAddress = GetDeviceBARAddress(&GLOBAL.deviceEntry,
QEMU_PCI_VIDEO_BASE_REG,
&GLOBAL.boardFBMappedSize,
NULL);
if (GLOBAL.boardFBAddress == NULL) {
status = paramErr;
goto bail;
}
lprintf("boardFBAddress %08lX boardFBMappedSize %08lX\n",
GLOBAL.boardFBAddress, GLOBAL.boardFBMappedSize);
GLOBAL.boardRegAddress = GetDeviceBARAddress(&GLOBAL.deviceEntry,
QEMU_PCI_VIDEO_MMIO_REG,
&GLOBAL.boardRegMappedSize,
NULL);
if (GLOBAL.boardRegAddress == NULL) {
status = paramErr;
goto bail;
}
lprintf("boardRegAddress %08lX boardRegMappedSize %08lX\n",
GLOBAL.boardRegAddress, GLOBAL.boardRegMappedSize);
lprintf("Enabling memory space..\n");
status = EnablePCIMemorySpace(&GLOBAL.deviceEntry);
if (status != noErr) {
lprintf("EnablePCIMemorySpace returned %d\n", status);
goto bail;
}
status = QemuVga_Init();
if (status != noErr)
goto bail;
bail:
DBG(lprintf("Driver init result: %d\n", status));
return status;
}
/*
* DriverReplaceCmd
*
* We are replacing an existing driver -- or are completing an initialization sequence.
* Retrieve any state information from the Name Registry (we have none), install
* our interrupt handlers, and activate the device.
*
* We don't use the calledFromInitialize parameter, but it's here so that a driver can
* distinguish between initialization (fetch only the NVRAM parameter) and replacement
* (fetch state information that may be left-over from the previous incantation).
*/
OSStatus
DriverReplaceCmd( AddressSpaceID addressSpaceID, DriverReplaceInfoPtr driverReplaceInfoPtr )
{
OSStatus status;
Trace(DriverReplaceCmd);
GLOBAL.refNum = driverReplaceInfoPtr->refNum;
GLOBAL.deviceEntry = driverReplaceInfoPtr->deviceEntry;
status = DriverInitializeCmd(addressSpaceID, driverReplaceInfoPtr);
return status;
}
/*
* DriverFinalizeCmd
*
* Process a DoDriverIO finalize command.
*/
OSStatus
DriverFinalizeCmd( DriverFinalInfoPtr driverFinalInfoPtr )
{
Trace(DriverFinializeCmd);
(void) DriverSupersededCmd((DriverSupersededInfoPtr) driverFinalInfoPtr, TRUE);
return noErr;
}
/*
* DriverSupersededCmd
*
* We are shutting down, or being replaced by a later driver. Wait for all I/O to
* complete and store volatile state in the Name Registry whree it will be retrieved
* by our replacement.
*/
OSStatus
DriverSupersededCmd( DriverSupersededInfoPtr driverSupersededInfoPtr, Boolean calledFromFinalize )
{
Trace(DriverSupersededCmd);
/*
* This duplicates DriverKillIOCmd, the correct algorithm would wait for
* concurrent I/O to complete. Hmm, what about "infinite wait" I/O, such
* as would be posted by a modem server or socket listener? Note that
* this section needs to be extended to handle all pending requests.
*
* It's safe to call CompleteThisRequest, as that routine uses an atomic
* operation that allows it to be called when no request is pending without
* any possible problems. Since it's a secondary interrupt handler, we
* need to call it through the Driver Services Library.
*
* Warning: GLOBAL.perRequestDataPtr will be NULL if initialization fails
* and the Driver Manager tries to terminate us. When we permit concurrent
* requests, this will loop on all per-request records.
*/
QemuVga_Exit();
RegistryEntryIDDispose( &GLOBAL.deviceEntry );
return noErr;
}
/*
* DriverControlCmd
*
* Process a PBControl command.
*/
OSStatus
DriverControlCmd( AddressSpaceID addressSpaceID, IOCommandID ioCommandID,
IOCommandKind ioCommandKind, CntrlParam *pb )
{
OSStatus status;
void *genericPtr;
/* The 'csParam' field of the 'CntrlParam' stucture is defined as 'short csParam[11]'. This is
* meant for 'operation defined parameters.' For the graphics driver, only the first 4 bytes are
* used. They are used as a pointer to another structure.
* To help code readability, the pointer will be extracted as a generic 'void *' and then cast as
* appropriate.
*/
genericPtr = (void *) *((UInt32 *) &(pb->csParam[0]));
Trace(DriverControlCmd);
switch( pb->csCode ) {
case cscReset: // Old obsolete call..return a 'controlErr'
return controlErr;
break;
case cscKillIO: // Old obsolete call..do nothing
return noErr;
case cscSetMode:
status = GraphicsCoreSetMode((VDPageInfo *) genericPtr);
break;
case cscSetEntries:
status = GraphicsCoreSetEntries((VDSetEntryRecord *) genericPtr);
// if ((status == noErr)&&(GLOBAL.qdDeskServiceCreated)&&(ioCommandKind == kSynchronousIOCommandKind))
// VSLWaitOnInterruptService(GLOBAL.qdVBLInterrupt, 1000);
break;
case cscSetGamma:
status = GraphicsCoreSetGamma((VDGammaRecord *) genericPtr);
break;
case cscGrayPage:
status = GraphicsCoreGrayPage((VDPageInfo *) genericPtr);
break;
case cscSetGray:
status = GraphicsCoreSetGray((VDGrayRecord *) genericPtr);
break;
case cscSetInterrupt:
status = GraphicsCoreSetInterrupt((VDFlagRecord *) genericPtr);
break;
case cscDirectSetEntries:
status = GraphicsCoreDirectSetEntries((VDSetEntryRecord *) genericPtr);
break;
case cscSetDefaultMode:
return controlErr;
case cscSwitchMode:
status = GraphicsCoreSwitchMode((VDSwitchInfoRec *) genericPtr);
break;
case cscSetSync:
status = GraphicsCoreSetSync((VDSyncInfoRec *) genericPtr);
break;
case cscSavePreferredConfiguration:
status = GraphicsCoreSetPreferredConfiguration((VDSwitchInfoRec *) genericPtr);
break;
case cscSetHardwareCursor:
status = GraphicsCoreSetHardwareCursor((VDSetHardwareCursorRec *) genericPtr);
break;
case cscDrawHardwareCursor:
status = GraphicsCoreDrawHardwareCursor((VDDrawHardwareCursorRec *) genericPtr);
break;
case cscSetPowerState:
status = GraphicsCoreSetPowerState((VDPowerStateRec *) genericPtr);
break;
default:
break;
}
if (status)
status = paramErr;
return status;
}
/*
* DriverStatusCmd
*
* Process a PBStatus command. We support the driver gestalt call and our private
* debugging commands.
*/
OSStatus
DriverStatusCmd( IOCommandID ioCommandID, IOCommandKind ioCommandKind, CntrlParam *pb )
{
OSStatus status;
void *genericPtr;
/* The 'csParam' field of the 'CntrlParam' stucture is defined as 'short csParam[11]'. This is
* meant for 'operation defined parameters.' For the graphics driver, only the first 4 bytes are
* used. They are used as a pointer to another structure.
* To help code readability, the pointer will be extracted as a generic 'void *' and then cast as
* appropriate.
*/
genericPtr = (void *) *((UInt32 *) &(pb->csParam[0]));
Trace(DriverStatusCmd);
lprintf("csCode=%d\n", pb->csCode);
switch( pb->csCode ) {
case cscGetMode:
status = GraphicsCoreGetMode((VDPageInfo *) genericPtr);
break;
case cscGetEntries:
status = GraphicsCoreGetEntries((VDSetEntryRecord *) genericPtr);
break;
case cscGetPages:
status = GraphicsCoreGetPages((VDPageInfo *) genericPtr);
break;
case cscGetBaseAddr:
status = GraphicsCoreGetBaseAddress((VDPageInfo *) genericPtr);
break;
case cscGetGray:
status = GraphicsCoreGetGray((VDGrayRecord *) genericPtr);
break;
case cscGetInterrupt:
status = GraphicsCoreGetInterrupt((VDFlagRecord *) genericPtr);
break;
case cscGetGamma:
status = GraphicsCoreGetGamma((VDGammaRecord *) genericPtr);
break;
case cscGetDefaultMode:
status = statusErr;
break;
case cscGetCurMode:
status = GraphicsCoreGetCurrentMode((VDSwitchInfoRec *) genericPtr);
break;
case cscGetSync:
status = GraphicsCoreGetSync((VDSyncInfoRec *) genericPtr);
break;
case cscGetConnection:
status = GraphicsCoreGetConnection((VDDisplayConnectInfoRec *) genericPtr);
break;
case cscGetModeTiming:
status = GraphicsCoreGetModeTiming((VDTimingInfoRec *) genericPtr);
break;
case cscGetPreferredConfiguration:
status = GraphicsCoreGetPreferredConfiguration((VDSwitchInfoRec *) genericPtr);
break;
case cscGetNextResolution:
status = GraphicsCoreGetNextResolution((VDResolutionInfoRec *) genericPtr);
break;
case cscGetVideoParameters:
status = GraphicsCoreGetVideoParams((VDVideoParametersInfoRec *) genericPtr);
break;
case cscGetGammaInfoList:
status = GraphicsCoreGetGammaInfoList((VDGetGammaListRec *) genericPtr);
break;
case cscRetrieveGammaTable:
status = GraphicsCoreRetrieveGammaTable((VDRetrieveGammaRec *) genericPtr);
break;
case cscSupportsHardwareCursor:
status = GraphicsCoreSupportsHardwareCursor((VDSupportsHardwareCursorRec *) genericPtr);
break;
case cscGetHardwareCursorDrawState:
status = GraphicsCoreGetHardwareCursorDrawState((VDHardwareCursorDrawStateRec *) genericPtr);
break;
case kDriverGestaltCode:
status = DriverGestaltHandler(pb);
break;
case cscGetPowerState:
status = GraphicsCoreGetPowerState((VDPowerStateRec *) genericPtr);
break;
case cscGetClutBehavior:
*(VDClutBehaviorPtr)genericPtr = kSetClutAtSetEntries;
status = noErr;
break;
default:
return statusErr;
}
if (status)
status = paramErr;
return status;
}
/*
* DriverKillIOCmd stops all I/O for this chip. It's a big hammer, use it wisely.
* This will need revision when we support concurrent I/O as we must stop all
* pending requests.
*/
OSStatus
DriverKillIOCmd( ParmBlkPtr pb )
{
#define REQUEST (GLOBAL.perRequestData)
Trace(DriverKillIOCmd);
return noErr;
#undef REQUEST
}
/*
* DriverReadCmd
*
* The caller passes the data buffer and buffer length in the IOParam record and
* a pointer to a SCSI NCRSCSIParam in the ioMisc field.
*/
OSStatus
DriverReadCmd( AddressSpaceID addressSpaceID, IOCommandID ioCommandID,
IOCommandKind ioCommandKind, ParmBlkPtr pb )
{
Trace(DriverReadCmd);
return paramErr;
}
/*
* DriverWriteCmd
*
* The caller passes the data buffer and buffer length in the IOParam record and
* a pointer to a SCSI NCRSCSIParam in the ioMisc field.
*/
OSStatus
DriverWriteCmd( AddressSpaceID addressSpaceID, IOCommandID ioCommandID,
IOCommandKind ioCommandKind, ParmBlkPtr pb )
{
Trace(DriverWriteCmd);
return paramErr;
}
/*
* DriverCloseCmd does nothing..
*/
OSStatus
DriverCloseCmd( ParmBlkPtr pb )
{
Trace(DriverCloseCmd);
if( !GLOBAL.openCount )
return notOpenErr;
GLOBAL.openCount--;
if (!GLOBAL.openCount)
QemuVga_Close();
return noErr;
}
/*
* DriverOpenCmd does nothing: remember that many applications will open a device, but
* never close it..
*/
OSStatus
DriverOpenCmd( AddressSpaceID addressSpaceID, ParmBlkPtr pb )
{
Trace(DriverOpenCmd);
GLOBAL.openCount++;
if (GLOBAL.openCount == 1)
QemuVga_Open();
return noErr;
}