{-# LINE 1 "System/Hardware/Serialport/Posix.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LINE 2 "System/Hardware/Serialport/Posix.hsc" #-}
module System.Hardware.Serialport.Posix where

import System.IO
import System.IO.Error
import System.Posix.IO
import System.Posix.Types
import System.Posix.Terminal
import System.Hardware.Serialport.Types
import Foreign
import Foreign.C
import Data.Bits


-- |Open and configure a serial port
openSerial :: FilePath     -- ^ The filename of the serial port, such as @\/dev\/ttyS0@ or @\/dev\/ttyUSB0@
           -> SerialPortSettings
           -> IO SerialPort
openSerial dev settings =
  do fd' <- openFd dev ReadWrite Nothing defaultFileFlags { noctty = True }
     setSerialSettings fd' settings
     return $ SerialPort fd' settings
        

-- |Possibly receive a character unless the timeout given in openSerial is exceeded.
recvChar :: SerialPort -> IO (Maybe Char)
recvChar (SerialPort fd' _) =
  do result <- try $ fdRead fd' 1
     case result of
       Right (str, _) -> return $ Just $ head str
       Left _         -> return Nothing


-- |Send a character
sendChar :: SerialPort -> Char -> IO ()
sendChar (SerialPort fd' _ ) c =
  fdWrite fd' [c] >> return ()


-- |Close the serial port
closeSerial :: SerialPort -> IO ()
closeSerial (SerialPort fd' _ ) =
  closeFd fd' 



{-# LINE 47 "System/Hardware/Serialport/Posix.hsc" #-}

foreign import ccall "ioctl" c_ioctl :: CInt -> CInt -> Ptr () -> IO CInt

c_ioctl' :: Fd -> Int -> Ptr d -> IO ()
c_ioctl' f req =
  throwErrnoIfMinus1_ "ioctl" .
     c_ioctl (fromIntegral f) (fromIntegral req) . castPtr


getTIOCM :: Fd -> IO Int
getTIOCM fd' =
  alloca $ \p -> c_ioctl' fd' (21525) p >> peek p
{-# LINE 59 "System/Hardware/Serialport/Posix.hsc" #-}


setTIOCM :: Fd -> Int -> IO ()
setTIOCM fd' val =
  with val $ c_ioctl' fd' (21528)
{-# LINE 64 "System/Hardware/Serialport/Posix.hsc" #-}
  

-- |Set the Data Terminal Ready level
setDTR :: SerialPort -> Bool -> IO ()
setDTR (SerialPort fd' _) set =
  do current <- getTIOCM fd'
     setTIOCM fd' $ if set
                      then current .|. (2)
{-# LINE 72 "System/Hardware/Serialport/Posix.hsc" #-}
                      else current .&. (complement (2))
{-# LINE 73 "System/Hardware/Serialport/Posix.hsc" #-}


setSerialSettings :: Fd -> SerialPortSettings -> IO ()
setSerialSettings fd' settings = 
  do termOpts <- getTerminalAttributes fd'
     let termOpts' = configureSettings termOpts settings
     setTerminalAttributes fd' termOpts' Immediately


withParity :: TerminalAttributes -> Parity -> TerminalAttributes
withParity termOpts Even = termOpts `withMode` EnableParity 
                                    `withoutMode` OddParity
withParity termOpts Odd = termOpts `withMode` EnableParity
                                   `withMode` OddParity
withParity termOpts NoParity = termOpts `withoutMode` EnableParity


withFlowControl :: TerminalAttributes -> FlowControl -> TerminalAttributes
withFlowControl termOpts NoFlowControl = termOpts
                                         `withoutMode` StartStopInput
                                         `withoutMode` StartStopOutput
withFlowControl termOpts Software = termOpts
                                    `withMode` StartStopInput
                                    `withMode` StartStopOutput


withStopBits :: TerminalAttributes -> StopBits -> TerminalAttributes
withStopBits termOpts One = termOpts `withoutMode` TwoStopBits
withStopBits termOpts Two = termOpts `withMode` TwoStopBits


configureSettings :: TerminalAttributes -> SerialPortSettings -> TerminalAttributes
configureSettings termOpts settings =
    termOpts `withInputSpeed` (baudRate settings)
                 `withOutputSpeed` (baudRate settings)
                 `withBits` (fromIntegral (bitsPerWord settings))
                 `withStopBits` (stopb settings)
                 `withParity` (parity settings)
                 `withFlowControl` (flowControl settings)
                 `withoutMode` EnableEcho
                 `withoutMode` EchoErase
                 `withoutMode` EchoKill
                 `withoutMode` ProcessInput
                 `withoutMode` ProcessOutput
                 `withoutMode` MapCRtoLF
                 `withoutMode` EchoLF
                 `withoutMode` HangupOnClose
                 `withoutMode` KeyboardInterrupts
                 `withoutMode` ExtendedFunctions
                 `withMode` LocalMode
                 `withMode` ReadEnable
                 `withTime` (timeout settings)
                 `withMinInput` 0