module System.Hardware.LinuxDevI2C
(
Addr
,Command
,Device
,withDevice
,openDevice
,closeDevice
,setSlaveAddr
,setSlaveAddrForce
,writeByte
,writeByteData
,readByte
,readByteData
,readI2CBlockData
,writeI2CBlockData
)
where
import Data.ByteString as BS
import System.Posix.IO (openFd,closeFd,OpenMode(..),defaultFileFlags)
import System.Posix.Types (Fd(..))
import Foreign.C.Types
import Foreign.C.Error (Errno (..), errnoToIOError)
import Foreign.ForeignPtr (withForeignPtr,withForeignPtr,mallocForeignPtrBytes)
import Foreign.Ptr (Ptr (..))
import Foreign.Ptr (Ptr (..),castPtr)
import GHC.Stack (HasCallStack,callStack,prettyCallStack)
import Control.Exception.Base (bracket)
import Data.Word (Word8)
import Control.Monad
type Addr = Word8
type Command = Word8
type Device = Fd
type File=CInt
withDevice :: FilePath -> (Device -> IO a) -> IO a
withDevice fp action = bracket (openDevice fp) closeDevice action
openDevice :: FilePath -> IO Device
openDevice fp = openFd fp ReadWrite Nothing defaultFileFlags
closeDevice :: Device -> IO ()
closeDevice = closeFd
setSlaveAddr :: HasCallStack => Device -> Addr -> IO ()
setSlaveAddr (Fd file) addr
= checkReturn $ ioctl_set_slave_addr file 0x0703 (fromIntegral addr)
setSlaveAddrForce :: HasCallStack => Device -> Addr -> IO ()
setSlaveAddrForce (Fd file) addr
= checkReturn $ ioctl_set_slave_addr file 0x0706 (fromIntegral addr)
writeByte :: HasCallStack => Device -> Command -> IO ()
writeByte (Fd file) cmd
= checkReturn $ c_writeByte file (fromIntegral cmd)
writeByteData :: HasCallStack => Device -> Command -> Word8 -> IO ()
writeByteData (Fd file) cmd byte
= checkReturn $ c_writeByteData file (fromIntegral cmd) (fromIntegral byte)
readByte :: HasCallStack => Device -> IO ()
readByte (Fd file)
= checkReturn $ c_readByte file
readByteData :: HasCallStack => Device -> Command -> IO Word8
readByteData (Fd file) cmd
= castRet fromIntegral $ c_readByteData file (fromIntegral cmd)
readI2CBlockData :: HasCallStack => Device -> Command -> Int -> IO ByteString
readI2CBlockData (Fd file) cmd size = do
checkBlockSizeLimit size
p <- mallocForeignPtrBytes size
withForeignPtr p $ \ptr -> do
readLen <- (c_readI2CBlockData
file
(fromIntegral cmd)
(fromIntegral size)
(castPtr ptr))
when (fromIntegral readLen /= size) $ do
fail ("short read :\n"
++ "read :" ++ show readLen
++ " expected :" ++ show size
++ "\n" ++ prettyCallStack callStack
)
packCStringLen (ptr,size)
writeI2CBlockData :: HasCallStack => Device -> Command -> ByteString -> IO ()
writeI2CBlockData (Fd file) cmd block
= BS.useAsCStringLen block $ \(cstring,len) -> do
checkBlockSizeLimit len
checkReturn $ c_writeI2CBlockData file
(fromIntegral cmd) (fromIntegral len) (castPtr cstring)
checkBlockSizeLimit :: HasCallStack => Int -> IO ()
checkBlockSizeLimit s
= when (s > 32) $ do
fail ("blockSize>32 ( blockSize=" ++ show s ++ ") \n"
++ prettyCallStack callStack)
checkReturn :: HasCallStack => IO CInt -> IO ()
checkReturn action = do
ret <- action
if ret < 0
then ioError $ errnoToIOError (prettyCallStack callStack)
(Errno ret) Nothing Nothing
else return ()
castRet :: HasCallStack => (CInt -> a) -> IO CInt -> IO a
castRet cast action = do
ret <- action
if ret < 0
then ioError $ errnoToIOError (prettyCallStack callStack)
(Errno ret) Nothing Nothing
else return $ cast ret
foreign import ccall unsafe
"smbus.h i2c_smbus_write_quick" c_writeQuick
:: File -> CUChar -> IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_read_byte" c_readByte
:: File -> IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_write_byte" c_writeByte
:: File -> CUChar ->IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_read_byte_data" c_readByteData
:: File -> CUChar -> IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_write_byte_data" c_writeByteData
:: File -> CUChar -> CUChar -> IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_read_word_data" c_readWordData
:: File -> CUChar -> IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_write_word_data" c_writeWordData
:: File -> CUChar -> CUShort ->IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_process_call" c_processCall
:: File -> CUChar -> CUShort ->IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_read_block_data" c_readBlockData
:: File -> CUChar -> Ptr CUChar ->IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_write_block_data" c_writeBlockData
:: File -> CUChar -> CUChar -> Ptr CUChar ->IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_read_i2c_block_data" c_readI2CBlockData
:: File -> CUChar -> CUChar -> Ptr CUChar ->IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_write_i2c_block_data" c_writeI2CBlockData
:: File -> CUChar -> CUChar -> Ptr CUChar ->IO CInt
foreign import ccall unsafe
"smbus.h i2c_smbus_block_process_call" c_blockProcessCall
:: File -> CUChar -> CUChar -> Ptr CUChar ->IO CInt
foreign import ccall unsafe
"sys/ioctl.h ioctl" ioctl_set_slave_addr
:: File -> CULong -> CInt ->IO CInt