----------------------------------------------------------------------------
-- |
-- Module      :  STM32.STLinkUSB.USBUtils
-- Copyright   :  (c) Marc Fontaine 2017
-- License     :  BSD3
-- 
-- Maintainer  :  Marc.Fontaine@gmx.de
-- Stability   :  experimental
-- Portability :  GHC-only
--
-- This module contains low-level functions for initializing the USB connection.
-- In most setups 'STM32.STLinkUSB.Env.runSTLink' does all the work
-- and there is no need to include this module.

module STM32.STLinkUSB.USBUtils
where
import qualified Data.Vector as Vector
import System.USB
import Control.Monad

findDefaultEndpoints
  :: IO (Ctx, Device, EndpointAddress, EndpointAddress, EndpointAddress)
findDefaultEndpoints = do
  ctx <- newCtx
  setDebug ctx PrintWarnings
  list <- findUSBDevices ctx defaultSTLProductID
  let device = case list of
        [x] -> x
        [] -> error "no STLink dongle found"
        (_:_:_) -> error "more than one STLink dongle found"
  
  findEndpoints ctx device
  
findUSBDevices ::
  Ctx -> ProductId -> IO [Device]
findUSBDevices ctx stlProductID = do
  devices <- fmap Vector.toList $ getDevices ctx
  flip filterM devices $ \ device -> do
      descr <- getDeviceDesc device
      return (deviceProductId descr == stlProductID)

defaultSTLProductID :: ProductId
defaultSTLProductID = 0x3748
                   
findEndpoints ::
     Ctx -> Device
  -> IO (Ctx, Device, EndpointAddress, EndpointAddress, EndpointAddress)
findEndpoints ctx device = do
  config <- getConfigDesc device 0
  let
     endPoints = interfaceEndpoints
                   $ Vector.head $ Vector.head $ configInterfaces config
     rxEndpoint = endpointAddress $ endPoints Vector.! 0
     txEndpoint = endpointAddress $ endPoints Vector.! 1
     traceEndpoint = endpointAddress $ endPoints Vector.! 2
  return (ctx, device, rxEndpoint, txEndpoint, traceEndpoint)

withUSB :: Device -> (DeviceHandle -> IO a) -> IO a
withUSB device action
  = withDeviceHandle device $
     \deviceHandle  -> withDetachedKernelDriver deviceHandle 0
                         $ action deviceHandle

usbReadTimeout :: Int
usbReadTimeout = 1000

usbWriteTimeout :: Int
usbWriteTimeout = 100