 --
 type DevicePtr a = ForeignPtr a
 
-withDevicePtr :: DevicePtr a -> (Ptr a -> IO b) -> IO b
-withDevicePtr =  withForeignPtr
-
-newDevicePtr   :: Ptr () -> IO (DevicePtr ())
+withDevicePtr :: DevicePtr a -> (Ptr b -> IO c) -> IO c
+withDevicePtr =  withForeignPtr . castForeignPtr
+
+newDevicePtr   :: Ptr a -> IO (DevicePtr a)
 newDevicePtr p =  (doAutoRelease free_) >>= \f -> newForeignPtr f p
 
 --

-- free   :: Ptr () -> IO (Maybe String)
-- free p =  nothingIfOk `fmap` cudaFree p

free_   :: Ptr () -> IO ()
free_ p =  throwIf_ (/= Success) (describe) (cudaFree p)

{# fun unsafe cudaFree
    { id `Ptr ()' } -> `Status' cToEnum #}

foreign import ccall "wrapper"
    doAutoRelease   :: (Ptr () -> IO ()) -> IO (FunPtr (Ptr () -> IO ()))


-- |
-- Initialise device memory to a given value
--
memset                  :: DevicePtr () -> Int64 -> Int -> IO (Maybe String)
memset ptr bytes symbol =  nothingIfOk `fmap` cudaMemset ptr symbol bytes

{# fun unsafe cudaMemset
    { withDevicePtr* `DevicePtr ()' ,
                     `Int'          ,
      cIntConv       `Int64'        } -> `Status' cToEnum #}


-- |
-- Initialise a matrix to a given value
--
memset2D :: DevicePtr ()                -- ^ The device memory
         -> (Int64,Int64)               -- ^ The (width,height) of the matrix in bytes
         -> Int64                       -- ^ The allocation pitch, as returned by 'malloc2D'
         -> Int                         -- ^ Value to set for each byte

-- Allocate the specified number of bytes in linear memory on the device. The
-- memory is suitably aligned for any kind of variable, and is not cleared.
--
malloc       :: Int64 -> IO (Either String (DevicePtr a))
malloc bytes = do
    (rv,ptr) <- cudaMalloc bytes
    case rv of
        Success -> Right `fmap` newDevicePtr (castPtr ptr)
        _       -> return . Left . describe $ rv

{# fun unsafe cudaMalloc
