simple-bluetooth-0.1.0.0: Network/Bluetooth/Adapter.hsc
{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
module Network.Bluetooth.Adapter (
allAdapters,
defaultAdapter,
discover,
module Network.Bluetooth.Types
) where
#if defined(mingw32_HOST_OS)
#include <windows.h>
#else
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#endif
#include <stddef.h>
import Network.Bluetooth.Device
import Network.Bluetooth.Types
#if defined(mingw32_HOST_OS)
import Network.Bluetooth.Win32
#endif
import qualified Data.ByteString.Internal as BI
import Control.Applicative
import Control.Exception
import Control.Monad
import Data.Bits
import Data.IORef
import Data.Word
import Foreign
import Foreign.C
#if defined(mingw32_HOST_OS)
import System.Win32.Types
#endif
#if !defined(mingw32_HOST_OS)
foreign import ccall safe "hci_for_each_dev" hci_for_each_dev
:: CInt -> FunPtr (CInt -> CInt -> CLong -> IO CInt) -> CLong -> IO ()
foreign import ccall safe "wrapper" mkVisitDev
:: (CInt -> CInt -> CLong -> IO CInt) ->
IO (FunPtr (CInt -> CInt -> CLong -> IO CInt))
foreign import ccall safe "hci_open_dev" hci_open_dev
:: CInt -> IO CInt
#endif
#if !defined(mingw32_HOST_OS)
openDev :: CInt -> IO Adapter
openDev dev_id = do
ret <- hci_open_dev dev_id
if ret < 0 then do
errno@(Errno errno_) <- getErrno
if errno == eINTR
then openDev dev_id
else do
err <- peekCString (strerror errno_)
throwIO $ BluetoothException "openDev" err
else
pure $ Adapter dev_id ret
#endif
allAdapters :: IO [Adapter]
#if defined(mingw32_HOST_OS)
allAdapters = return [Adapter]
#else
allAdapters = do
devsRef <- newIORef []
cb <- mkVisitDev $ \_ dev_id _ -> do
modifyIORef devsRef (dev_id:)
pure 0
hci_for_each_dev (#const HCI_UP) cb 0
`finally`
freeHaskellFunPtr cb
dev_ids <- reverse <$> readIORef devsRef
mapM openDev dev_ids
#endif
#if !defined(mingw32_HOST_OS)
foreign import ccall unsafe "hci_get_route" hci_get_route
:: Ptr BluetoothAddr -> IO CInt
#endif
defaultAdapter :: IO (Maybe Adapter)
#if defined(mingw32_HOST_OS)
defaultAdapter = return $ Just Adapter
#else
defaultAdapter = do
ret <- hci_get_route nullPtr
if ret < 0 then do
errno@(Errno errno_) <- getErrno
if errno == eINTR
then defaultAdapter
else if errno == eNODEV
then pure Nothing
else do
err <- peekCString (strerror errno_)
throwIO $ BluetoothException "defaultAdapter" err
else
Just <$> openDev ret
#endif
#if !defined(mingw32_HOST_OS)
foreign import ccall safe "hci_inquiry" hci_inquiry
:: CInt -> CInt -> CInt -> Ptr Word8 -> Ptr (Ptr InquiryInfo) -> CLong -> IO CInt
data InquiryInfo = InquiryInfo {
iiAddr :: BluetoothAddr
}
deriving Show
instance Storable InquiryInfo where
sizeOf _ = (#const sizeof(inquiry_info))
alignment _ = alignment (undefined :: Word64)
peek p = InquiryInfo <$> peek (p `plusPtr` (#const offsetof(inquiry_info, bdaddr)))
poke _ _ = fail "InquiryInfo.poke not defined"
#endif
discover :: Adapter -> IO [Device]
#if defined(mingw32_HOST_OS)
discover a = map (Device a . fst) <$> discover' a flags
where
flags = (#const LUP_CONTAINERS) .|.
(#const LUP_RETURN_ADDR) .|.
(#const LUP_FLUSHCACHE)
#else
discover a@(Adapter dev_id _) = go 0 -- (#const IREQ_CACHE_FLUSH)
where
go flags = do
mDevices <- allocaArray 255 $ \ppDevs -> do
poke ppDevs nullPtr
n <- hci_inquiry dev_id 8 255 nullPtr ppDevs flags
if n < 0 then do
errno@(Errno errno_) <- getErrno
if errno == eINTR
then pure Nothing
else do
err <- peekCString (strerror errno_)
throwIO $ BluetoothException "discover" err
else do
pDevs <- peek ppDevs
iis <- peekArray (fromIntegral n) pDevs
free pDevs
pure $ Just $ map (Device a . iiAddr) iis
case mDevices of
Just devices -> pure devices
Nothing -> go 0 -- eINTR ? Retry.
#endif