/**
 * \file dri_util.c
 * \brief DRI utility functions.
 *
 * This module acts as glue between GLX and the actual hardware driver.  A DRI
 * driver doesn't really \e have to use any of this - it's optional.  But, some
 * useful stuff is done here that otherwise would have to be duplicated in most
 * drivers.
 * 
 * Basically, these utility functions take care of some of the dirty details of
 * screen initialization, context creation, context binding, DRM setup, etc.
 *
 * These functions are compiled into each DRI driver so libGL.so knows nothing
 * about them.
 *
 */

#include <assert.h>
#include <fcntl.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/fb.h>
#include <linux/vt.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/shm.h>

#include "dri_util.h"

/**
 * \brief Print message to \c stderr if the \c LIBGL_DEBUG environment variable
 * is set.
 * 
 * Is called from the drivers.
 * 
 * \param f \e printf like format.
 * 
 * \internal
 * This function is a wrapper around vfprintf().
 */
void
__driUtilMessage(const char *f, ...)
{
    va_list args;

    if (getenv("LIBGL_DEBUG")) {
        fprintf(stderr, "libGL error: \n");
        va_start(args, f);
        vfprintf(stderr, f, args);
        va_end(args);
        fprintf(stderr, "\n");
    }
}


/*****************************************************************/
/** \name Visual utility functions                               */
/*****************************************************************/
/*@{*/


/*@}*/


/*****************************************************************/
/** \name Context (un)binding functions                          */
/*****************************************************************/
/*@{*/


/**
 * \brief Unbind context.
 * 
 * \param drawable __DRIdrawable
 * \param context __DRIcontext
 * \param will_rebind not used.
 *
 * \return GL_TRUE on success, or GL_FALSE on failure.
 * 
 * \internal
 * This function calls __DriverAPIRec::UnbindContext, and then decrements
 * __DRIdrawablePrivateRec::refcount which must be non-zero for a successful
 * return.
 * 
 * While casting the opaque private pointers associated with the parameters into their
 * respective real types it also assures they are not null. 
 */
static GLboolean driUnbindContext(__DRIdrawable *drawable, 
				  __DRIcontext *context)
{
    __DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)drawable;
    __DRIcontextPrivate  *pcp =  (__DRIcontextPrivate  *)context;
    __DRIscreenPrivate *psp;

    if (pdp == NULL || pcp == NULL) 
	return GL_FALSE;

    if (!(psp = (__DRIscreenPrivate *)pdp->driScreenPriv)) 
	return GL_FALSE;

    /* Let driver unbind drawable from context */
    (*psp->DriverAPI.UnbindContext)(pcp);

    if (pdp->refcount == 0) 
	return GL_FALSE;

    --pdp->refcount;

    return GL_TRUE;
}


/**
 * \brief Unbind context.
 * 
 * \param pDRIScreen __DRIscreen
 * \param drawable __DRIdrawable
 * \param context __DRIcontext
 *
 * \internal
 * This function and increments __DRIdrawablePrivateRec::refcount and calls
 * __DriverAPIRec::MakeCurrent to binds the drawable.
 * 
 * While casting the opaque private pointers into their
 * respective real types it also assures they are not null. 
 */
static GLboolean driBindContext(__DRIscreen *screen, __DRIdrawable *drawable, 
				__DRIcontext *context)
{
    __DRIscreenPrivate *psp = (__DRIscreenPrivate *)screen;
    __DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)drawable;
    __DRIcontextPrivate *pcp = (__DRIcontextPrivate *)context;
    
    if (psp == NULL)
	return GL_FALSE;
    
    if (pdp == NULL || pcp == NULL) {
	(*psp->DriverAPI.MakeCurrent)(0, 0, 0);
	return GL_TRUE;
    }

    /* Bind the drawable to the context */
    pcp->driDrawablePriv = pdp;
    pdp->driContextPriv = pcp;
    pdp->refcount++;

    /* Call device-specific MakeCurrent */
    (*psp->DriverAPI.MakeCurrent)(pcp, pdp, pdp);

    return GL_TRUE;
}

/*@}*/


/*****************************************************************/
/** \name Drawable handling functions                            */
/*****************************************************************/
/*@{*/


/**
 * \brief Update private drawable information.
 *
 * \param pdp pointer to the private drawable information to update.
 * 
 * \internal
 * This function is a no-op. Should never be called but is referenced as an
 * external symbol from client drivers.
 */
void __driUtilUpdateDrawableInfo(__DRIdrawablePrivate *pdp)
{
   __DRIscreenPrivate *psp = pdp->driScreenPriv;

   pdp->numClipRects = psp->pSAREA->drawableTable[pdp->index].flags ? 1 : 0;
   pdp->lastStamp = *(pdp->pStamp);
}


/**
 * \brief Swap buffers.
 *
 * \param pDRIscreen __DRIscreen
 * \param drawablePrivate opaque pointer to the per-drawable private info.
 * 
 * \internal
 * This function calls __DRIdrawablePrivate::swapBuffers.
 * 
 * Is called directly from glXSwapBuffers().
 */
static void driSwapBuffers(__DRIdrawable *drawable)
{
    __DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)drawable;
    if (pdp)
	pdp->swapBuffers(pdp);
}


/**
 * \brief Destroy per-drawable private information.
 *
 * \param pDRIscreen __DRIscreen
 * \param drawablePrivate opaque pointer to the per-drawable private info.
 *
 * \internal
 * This function calls __DriverAPIRec::DestroyBuffer on \p drawablePrivate,
 * frees the clip rects if any, and finally frees \p drawablePrivate itself.
 */
static void driDestroyDrawable(__DRIdrawable *drawable)
{
    __DRIdrawablePrivate *pdp = (__DRIdrawablePrivate *)drawable;
    __DRIscreenPrivate *psp;

    if (pdp) {
	psp = pdp->driScreenPriv;
        (*psp->DriverAPI.DestroyBuffer)(pdp);
	if (pdp->pClipRects)
	    free(pdp->pClipRects);
	free(pdp);
    }
}


/**
 * \brief Create the per-drawable private driver information.
 * 
 * \param dpy the display handle.
 * \param scrn the screen number.
 * \param draw the GLX drawable info.
 * \param vid visual ID.
 * \param pdraw will receive the drawable dependent methods.
 * 
 *
 * \returns a opaque pointer to the per-drawable private info on success, or NULL
 * on failure.
 * 
 * \internal
 * This function allocates and fills a __DRIdrawablePrivateRec structure,
 * initializing the invariant window dimensions and clip rects.  It obtains the
 * visual config, converts it into a __GLcontextModesRec and passes it to
 * __DriverAPIRec::CreateBuffer to create a buffer.
 */
static void *driCreateDrawable(__DRIscreen *screen,
                               int width, int height, int index,
                               const __GLcontextModes *glVisual)
{
    __DRIscreenPrivate *psp = (__DRIscreenPrivate *)screen;
    __DRIdrawablePrivate *pdp;

    if (!psp)
	return NULL;

    if (!(pdp = (__DRIdrawablePrivate *)malloc(sizeof(__DRIdrawablePrivate))))
	return NULL;

    pdp->index = index;
    pdp->refcount = 0;
    pdp->lastStamp = -1;
    pdp->numBackClipRects = 0;
    pdp->pBackClipRects = NULL;

    /* Initialize with the invariant window dimensions and clip rects here.
     */
    pdp->x = 0;
    pdp->y = 0;
    pdp->w = width;
    pdp->h = height;
    pdp->numClipRects = 0;
    pdp->pClipRects = (drm_clip_rect_t *) malloc(sizeof(drm_clip_rect_t));
    (pdp->pClipRects)[0].x1 = 0;
    (pdp->pClipRects)[0].y1 = 0;
    (pdp->pClipRects)[0].x2 = width;
    (pdp->pClipRects)[0].y2 = height;

    pdp->driScreenPriv = psp;
    pdp->driContextPriv = 0;
    
    pdp->frontBuffer = psp->pFB;
    pdp->currentBuffer = pdp->frontBuffer;
    pdp->currentPitch = psp->fbStride;
    pdp->backBuffer = psp->pFB + psp->fbStride * psp->fbHeight;

    if (!(*psp->DriverAPI.CreateBuffer)(psp, pdp, glVisual, GL_FALSE)) {
       free(pdp);
       return NULL;
    }

    pdp->entry.destroyDrawable = driDestroyDrawable;
    pdp->entry.swapBuffers = driSwapBuffers;  /* called by glXSwapBuffers() */
    pdp->swapBuffers = psp->DriverAPI.SwapBuffers;

    pdp->pStamp = &(psp->pSAREA->drawableTable[pdp->index].stamp);
    return (void *) pdp;
}

/*@}*/


/*****************************************************************/
/** \name Context handling functions                             */
/*****************************************************************/
/*@{*/


/**
 * \brief Destroy the per-context private information.
 * 
 * \param contextPrivate opaque pointer to the per-drawable private info.
 *
 * \internal
 * This function calls __DriverAPIRec::DestroyContext on \p contextPrivate, calls
 * drmDestroyContext(), and finally frees \p contextPrivate.
 */
static void driDestroyContext(__DRIcontext *context)
{
    __DRIcontextPrivate *pcp = (__DRIcontextPrivate *)context;
    __DRIscreenPrivate *psp = NULL;

    if (pcp) {
	(*pcp->driScreenPriv->DriverAPI.DestroyContext)(pcp);
        psp = pcp->driDrawablePriv->driScreenPriv;
	if (psp->fd) {
	   printf(">>> drmDestroyContext(0x%x)\n", (int) pcp->hHWContext);
	   drmDestroyContext(psp->fd, pcp->hHWContext);
	}
	free(pcp);
    }
}

/**
 * \brief Create the per-drawable private driver information.
 * 
 * \param dpy the display handle.
 * \param vis the visual information.
 * \param sharedPrivate the shared context dependent methods or NULL if non-existent.
 * \param pctx will receive the context dependent methods.
 *
 * \returns a opaque pointer to the per-context private information on success, or NULL
 * on failure.
 * 
 * \internal
 * This function allocates and fills a __DRIcontextPrivateRec structure.  It
 * gets the visual, converts it into a __GLcontextModesRec and passes it
 * to __DriverAPIRec::CreateContext to create the context.
 */
static void *driCreateContext(__DRIscreen *screen, 
                              const __GLcontextModes *glVisual,
                              void *sharedPrivate)
{
   __DRIscreenPrivate *psp = (__DRIscreenPrivate *)screen;
   __DRIcontextPrivate *pcp;
   __DRIcontextPrivate *pshare = (__DRIcontextPrivate *) sharedPrivate;
   void *shareCtx;

   if (!psp) 
      return NULL;

   if (!(pcp = (__DRIcontextPrivate *)malloc(sizeof(__DRIcontextPrivate))))
      return NULL;

   pcp->driScreenPriv = psp;
   pcp->driDrawablePriv = NULL;

   if (psp->fd) {
      if (drmCreateContext(psp->fd, &pcp->hHWContext)) {
	 fprintf(stderr, ">>> drmCreateContext failed\n");
	 free(pcp);
	 return NULL;
      }
   }

   shareCtx = pshare ? pshare->driverPrivate : NULL;
   
   if (!(*psp->DriverAPI.CreateContext)(glVisual, pcp, shareCtx)) {
      if (psp->fd) 
	 (void) drmDestroyContext(psp->fd, pcp->hHWContext);
      free(pcp);
      return NULL;
   }

   pcp->entry.destroyContext = driDestroyContext;
   pcp->entry.bindContext    = driBindContext;
   pcp->entry.unbindContext  = driUnbindContext;

   return pcp;
}

/*@}*/


/*****************************************************************/
/** \name Screen handling functions                              */
/*****************************************************************/
/*@{*/


/**
 * \brief Destroy the per-screen private information.
 * 
 * \param pDRIscreen __DRIscreen 
 *
 * \internal
 * This function calls __DriverAPIRec::DestroyScreen on \p screenPrivate, calls
 * drmClose(), and finally frees \p screenPrivate.
 */
static void driDestroyScreen(__DRIscreen *screen)
{
    __DRIscreenPrivate *psp = (__DRIscreenPrivate *)screen;
    if (psp) {
	if (psp->DriverAPI.DestroyScreen)
	    (*psp->DriverAPI.DestroyScreen)(psp);
	
	if (psp->fd) 
	    (void)drmClose(psp->fd);
	
	free(psp->pDevPriv);
	free(psp);
    }
}


/**
 * \brief Create the per-screen private information.
 * 
 * \param dpy the display handle.
 * \param scrn the screen number.
 * \param psc will receive the screen dependent methods.
 * \param numConfigs number of visuals.
 * \param config visuals.
 * \param driverAPI driver callbacks structure.
 *
 * \return a pointer to the per-screen private information.
 * 
 * \internal
 * This function allocates and fills a __DRIscreenPrivateRec structure. It
 * opens the DRM device verifying that the exported version matches the
 * expected.  It copies the driver callback functions and calls
 * __DriverAPIRec::InitDriver.
 *
 * If a client maps the framebuffer and SAREA regions.
 */
__DRIscreenPrivate *    
__driUtilCreateScreen(struct DRIDriverRec *driver,
 		      struct DRIDriverContextRec *driverContext,
		      const struct __DriverAPIRec *driverAPI)
{
    __DRIscreenPrivate *psp;
    
    if(!(psp = (__DRIscreenPrivate *)malloc(sizeof(__DRIscreenPrivate))))
	return NULL;
    
    psp->fd = drmOpen(NULL, driverContext->pciBusID);
    if (psp->fd < 0) {
	fprintf(stderr, "libGL error: failed to open DRM: %s\n", 
		strerror(-psp->fd));
	free(psp);
	return NULL;
    }
    
    {
	drmVersionPtr version = drmGetVersion(psp->fd);
	if (version) {
	    psp->drmMajor = version->version_major;
	    psp->drmMinor = version->version_minor;
	    psp->drmPatch = version->version_patchlevel;
	    drmFreeVersion(version);
	}
	else {
	    fprintf(stderr, "libGL error: failed to get drm version: %s\n", 
		    strerror(-psp->fd));
	    free(psp);
	    return NULL;
	}
    }
    
    /* install driver's callback functions */
    psp->DriverAPI = *driverAPI;
    
    /*
    * Get device-specific info.  pDevPriv will point to a struct
    * (such as DRIRADEONRec in xfree86/driver/ati/radeon_dri.h)
    * that has information about the screen size, depth, pitch,
    * ancilliary buffers, DRM mmap handles, etc.
    */
    psp->fbOrigin = driverContext->shared.fbOrigin;  
    psp->fbSize = driverContext->shared.fbSize; 
    psp->fbStride = driverContext->shared.fbStride;
    psp->devPrivSize = driverContext->driverClientMsgSize;
    psp->pDevPriv = driverContext->driverClientMsg;
    psp->fbWidth = driverContext->shared.virtualWidth;
    psp->fbHeight = driverContext->shared.virtualHeight;
    psp->fbBPP = driverContext->bpp;
    
    if ((driverContext->FBAddress != NULL) && (driverContext->pSAREA != NULL)) {
	/* Already mapped in server */
	psp->pFB = driverContext->FBAddress;
	psp->pSAREA = driverContext->pSAREA;
    } else {
	/*
       * Map the framebuffer region.  
       */
	if (drmMap(psp->fd, driverContext->shared.hFrameBuffer, psp->fbSize, 
		   (drmAddressPtr)&psp->pFB)) {
	    fprintf(stderr, "libGL error: drmMap of framebuffer failed\n");
	    (void)drmClose(psp->fd);
	    free(psp);
	    return NULL;
	}
	
	/*
       * Map the SAREA region.  Further mmap regions may be setup in
       * each DRI driver's "createScreen" function.
       */
	if (drmMap(psp->fd, driverContext->shared.hSAREA,
		   driverContext->shared.SAREASize, 
		   (drmAddressPtr)&psp->pSAREA)) {
	    fprintf(stderr, "libGL error: drmMap of sarea failed\n");
	    (void)drmUnmap((drmAddress)psp->pFB, psp->fbSize);
	    (void)drmClose(psp->fd);
	    free(psp);
	    return NULL;
	}
	
#ifdef _EMBEDDED
	mprotect(psp->pSAREA, driverContext->shared.SAREASize, PROT_READ);
#endif
    }
    
    
    /* Initialize the screen specific GLX driver */
    if (psp->DriverAPI.InitDriver) {
	if (!(*psp->DriverAPI.InitDriver)(psp)) {
	    fprintf(stderr, "libGL error: InitDriver failed\n");
	    free(psp->pDevPriv);
	    (void)drmClose(psp->fd);
	    free(psp);
	    return NULL;
	}
    }
    
    psp->entry.destroyScreen  = driDestroyScreen;
    psp->entry.createContext  = driCreateContext;
    psp->entry.createDrawable = driCreateDrawable;
    
    return psp;
}



/**
 * \brief Create the per-screen private information.
 *
 * Version for drivers without a DRM module.
 * 
 * \param dpy the display handle.
 * \param scrn the screen number.
 * \param numConfigs number of visuals.
 * \param config visuals.
 * \param driverAPI driver callbacks structure.
 * 
 * \internal
 * Same as __driUtilCreateScreen() but without opening the DRM device.
 */
__DRIscreenPrivate *     
__driUtilCreateScreenNoDRM(struct DRIDriverRec *driver,
			   struct DRIDriverContextRec *driverContext,
			   const struct __DriverAPIRec *driverAPI)
{
    __DRIscreenPrivate *psp;
    
    psp = (__DRIscreenPrivate *)calloc(1, sizeof(__DRIscreenPrivate));
    if (!psp) 
	return NULL;
    
    psp->ddxMajor = 4;
    psp->ddxMinor = 0;
    psp->ddxPatch = 1;
    psp->driMajor = 4;
    psp->driMinor = 1;
    psp->driPatch = 0;
    psp->fd = 0;
    
    psp->fbOrigin = driverContext->shared.fbOrigin; 
    psp->fbSize = driverContext->shared.fbSize; 
    psp->fbStride = driverContext->shared.fbStride;
    psp->devPrivSize = driverContext->driverClientMsgSize;
    psp->pDevPriv = driverContext->driverClientMsg;
    psp->fbWidth = driverContext->shared.virtualWidth;
    psp->fbHeight = driverContext->shared.virtualHeight;
    psp->fbBPP = driverContext->bpp;
    
    psp->pFB = driverContext->FBAddress;
    
    /* install driver's callback functions */
    psp->DriverAPI = *driverAPI;
    
    if ((driverContext->FBAddress != NULL) && (driverContext->pSAREA != NULL)) {
	/* Already mapped in server */
	psp->pFB = driverContext->FBAddress;
	psp->pSAREA = driverContext->pSAREA;
    } else {
	int id;
	psp->fd = open("/dev/mem", O_RDWR, 0);
	/*
       * Map the framebuffer region.  
       */
	if (drmMap(psp->fd, driverContext->shared.hFrameBuffer, psp->fbSize, 
		   (drmAddressPtr)&psp->pFB)) {
	    fprintf(stderr, "libGL error: drmMap of framebuffer failed\n");
	    (void)drmClose(psp->fd);
	    free(psp);
	    return NULL;
	}
	driverContext->FBAddress = psp->pFB;
	
	/*
       * Map the SAREA region.  Non-DRM drivers use a shmem SAREA
       */
	id = shmget(driverContext->shared.hSAREA, driverContext->shared.SAREASize, 0);
	driverContext->pSAREA = shmat(id, NULL, 0);
	if (!driverContext->pSAREA) {
	    fprintf(stderr, "libGL error: shmget of sarea failed\n");
	    (void)drmUnmap((drmAddress)psp->pFB, psp->fbSize);
	    (void)drmClose(psp->fd);
	    free(psp);
	    return NULL;
	}
	
	close(psp->fd);
	psp->fd = 0;
    }
    
    /* Initialize the screen specific GLX driver */
    if (psp->DriverAPI.InitDriver) {
	if (!(*psp->DriverAPI.InitDriver)(psp)) {
	    fprintf(stderr, "libGL error: InitDriver failed\n");
	    free(psp->pDevPriv);
	    free(psp);
	    return NULL;
	}
    }
    
    psp->entry.destroyScreen  = driDestroyScreen;
    psp->entry.createContext  = driCreateContext;
    psp->entry.createDrawable = driCreateDrawable;
    
    return psp;
}

/**
 * Calculate amount of swap interval used between GLX buffer swaps.
 * 
 * The usage value, on the range [0,max], is the fraction of total swap
 * interval time used between GLX buffer swaps is calculated.
 *
 *            \f$p = t_d / (i * t_r)\f$
 * 
 * Where \f$t_d\$f is the time since the last GLX buffer swap, \f$i\f$ is the
 * swap interval (as set by \c glXSwapIntervalSGI), and \f$t_r\f$ time
 * required for a single vertical refresh period (as returned by \c
 * glXGetMscRateOML).
 * 
 * See the documentation for the GLX_MESA_swap_frame_usage extension for more
 * details.
 *
 * \param   dPriv  Pointer to the private drawable structure.
 * \return  If less than a single swap interval time period was required
 *          between GLX buffer swaps, a number greater than 0 and less than
 *          1.0 is returned.  If exactly one swap interval time period is
 *          required, 1.0 is returned, and if more than one is required then
 *          a number greater than 1.0 will be returned.
 *
 * \sa glXSwapIntervalSGI(), glXGetMscRateOML().
 */
float
driCalculateSwapUsage( __DRIdrawablePrivate *dPriv, int64_t last_swap_ust,
		       int64_t current_ust )
{
   return 0.0f;
}

/**
 * Compare the current GLX API version with a driver supplied required version.
 * 
 * The minimum required version is compared with the API version exported by
 * the \c __glXGetInternalVersion function (in libGL.so).
 * 
 * \param   required_version Minimum required internal GLX API version.
 * \return  A tri-value return, as from strcmp is returned.  A value less
 *          than, equal to, or greater than zero will be returned if the
 *          internal GLX API version is less than, equal to, or greater
 *          than \c required_version.
 *
 * \sa __glXGetInternalVersion().
 */
int driCompareGLXAPIVersion( GLuint required_version )
{
   return 0;
}

/*@}*/
