simple-bluetooth-0.1.0.0: Network/Bluetooth/Win32.hsc
module Network.Bluetooth.Win32 where
#include <windows.h>
import Network.Bluetooth.Types
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Internal as BI
import Control.Applicative
import Control.Exception
import Foreign
import Foreign.C
import System.Win32.Types
data SockAddrBTH = SockAddrBTH {
bthFamily :: USHORT,
bthAddr :: BluetoothAddr,
bthPort :: LONG
}
deriving Show
sockAddrBTH :: BluetoothAddr -> Word8 -> SockAddrBTH
sockAddrBTH addr port = SockAddrBTH aF_BTH addr (fromIntegral port)
instance Storable SockAddrBTH where
sizeOf _ = 30
alignment _ = alignment (undefined :: Word64)
poke p bth = do
BI.memset (castPtr p) 0 (fromIntegral $ sizeOf bth)
poke (p `plusPtr` 0) (bthFamily bth)
poke (p `plusPtr` 2) (bthAddr bth)
poke (p `plusPtr` 26) (bthPort bth)
peek p = SockAddrBTH <$> peek (p `plusPtr` 0)
<*> peek (p `plusPtr` 2)
<*> peek (p `plusPtr` 26)
data SOCKET_ADDRESS sa = SOCKET_ADDRESS {
saSockaddr :: Ptr sa,
saSockaddrLength :: INT
}
deriving Show
instance Storable (SOCKET_ADDRESS sa) where
sizeOf _ = (#const sizeof(SOCKET_ADDRESS))
alignment _ = alignment (undefined :: Word64)
poke _ _ = fail "SOCKET_ADDRESS.poke not defined"
peek p = SOCKET_ADDRESS <$> peek (p `plusPtr` (#const offsetof(SOCKET_ADDRESS,lpSockaddr)))
<*> peek (p `plusPtr` (#const offsetof(SOCKET_ADDRESS,iSockaddrLength)))
data CSADDR_INFO sa = CSADDR_INFO {
csaLocalAddr :: SOCKET_ADDRESS sa,
csaRemoteAddr :: SOCKET_ADDRESS sa
}
deriving Show
instance Storable sa => Storable (CSADDR_INFO sa) where
sizeOf _ = (#const sizeof(CSADDR_INFO))
alignment _ = alignment (undefined :: Word64)
poke _ _ = fail "CSADDR_INFO.poke not defined"
peek p = CSADDR_INFO <$> peek (p `plusPtr` (#const offsetof(CSADDR_INFO,LocalAddr)))
<*> peek (p `plusPtr` (#const offsetof(CSADDR_INFO,RemoteAddr)))
data WSAQUERYSET sa = WSAQUERYSET {
qsSize :: DWORD,
qsServiceInstanceName :: CString,
qsNameSpace :: DWORD,
qsNumberOfCsAddrs :: DWORD,
qsCsAddrs :: Ptr (CSADDR_INFO sa),
qsBlob :: Ptr Word8
}
deriving Show
instance Storable (WSAQUERYSET sa) where
sizeOf _ = (#const sizeof(WSAQUERYSET))
alignment _ = alignment (undefined :: Word64)
poke p qs = do
BI.memset (castPtr p) 0 (fromIntegral $ sizeOf qs)
poke (p `plusPtr` (#const offsetof(WSAQUERYSET,dwSize))) (qsSize qs)
poke (p `plusPtr` (#const offsetof(WSAQUERYSET,lpszServiceInstanceName))) (qsServiceInstanceName qs)
poke (p `plusPtr` (#const offsetof(WSAQUERYSET,dwNameSpace))) (qsNameSpace qs)
poke (p `plusPtr` (#const offsetof(WSAQUERYSET,dwNumberOfCsAddrs))) (qsNumberOfCsAddrs qs)
poke (p `plusPtr` (#const offsetof(WSAQUERYSET,lpcsaBuffer))) (qsCsAddrs qs)
poke (p `plusPtr` (#const offsetof(WSAQUERYSET,lpBlob))) (qsBlob qs)
peek p =
WSAQUERYSET <$> peek (p `plusPtr` (#const offsetof(WSAQUERYSET,dwSize)))
<*> peek (p `plusPtr` (#const offsetof(WSAQUERYSET,lpszServiceInstanceName)))
<*> peek (p `plusPtr` (#const offsetof(WSAQUERYSET,dwNameSpace)))
<*> peek (p `plusPtr` (#const offsetof(WSAQUERYSET,dwNumberOfCsAddrs)))
<*> peek (p `plusPtr` (#const offsetof(WSAQUERYSET,lpcsaBuffer)))
<*> peek (p `plusPtr` (#const offsetof(WSAQUERYSET,lpBlob)))
foreign import stdcall safe "WSALookupServiceBeginA" wsaLookupServiceBegin
:: Ptr (WSAQUERYSET SockAddrBTH) -> DWORD -> Ptr HANDLE -> IO CInt
foreign import stdcall safe "WSALookupServiceNextA" wsaLookupServiceNext
:: HANDLE -> DWORD -> Ptr DWORD -> Ptr (WSAQUERYSET SockAddrBTH) -> IO CInt
foreign import stdcall safe "WSALookupServiceEnd" wsaLookupServiceEnd
:: HANDLE -> IO CInt
-- | The Bluetooth namespace
nS_BTH :: DWORD
nS_BTH = 16
aF_BTH :: USHORT
aF_BTH = 32
wsaServiceNotFound :: ErrCode
wsaServiceNotFound = 10108
wsaENoMore :: ErrCode
wsaENoMore = 10110
bTHPROTO_RFCOMM :: CInt
bTHPROTO_RFCOMM = 0x0003
discover' :: Adapter -> DWORD -> IO [(BluetoothAddr, Maybe ByteString)]
discover' a flags = alloca $ \pqs -> alloca $ \ph -> do
poke pqs $ WSAQUERYSET {
qsSize = fromIntegral $ sizeOf (undefined :: WSAQUERYSET SockAddrBTH),
qsServiceInstanceName = nullPtr,
qsNameSpace = nS_BTH,
qsNumberOfCsAddrs = 0,
qsCsAddrs = nullPtr,
qsBlob = nullPtr
}
ret <- wsaLookupServiceBegin pqs flags ph
none <- if ret < 0 then do
err <- getLastError
if err == wsaServiceNotFound -- This error means that there are no devices
then pure True
else throwIO =<< BluetoothException "discover" <$> (peekTString =<< getErrorMessage err)
else
pure False
if none then
return []
else do
h <- peek ph
do
let bufSize = 5000
alloca $ \pResults -> allocaBytes bufSize $ \buf -> alloca $ \pdwSize -> do
poke pResults $ WSAQUERYSET {
qsSize = fromIntegral $ sizeOf (undefined :: WSAQUERYSET SockAddrBTH),
qsServiceInstanceName = nullPtr,
qsNameSpace = nS_BTH,
qsNumberOfCsAddrs = 0,
qsCsAddrs = nullPtr,
qsBlob = nullPtr
}
let loop acc = do
poke pdwSize (fromIntegral bufSize)
ret <- wsaLookupServiceNext h flags pdwSize pResults
if ret < 0 then do
err <- getLastError
if err == wsaENoMore
then pure $ reverse acc
else throwIO =<< BluetoothException "discover" <$> (peekTString =<< getErrorMessage err)
else do
results <- peek pResults
csAddrs <- peekArray (fromIntegral $ qsNumberOfCsAddrs results) (qsCsAddrs results)
addrs <- mapM (peek . saSockaddr . csaRemoteAddr) csAddrs
serviceName <- if qsServiceInstanceName results == nullPtr
then pure Nothing
else Just . C.pack <$> peekCString (qsServiceInstanceName results)
loop $ reverse (map (\addr -> (bthAddr addr, serviceName)) addrs) ++ acc
loop []
`finally`
wsaLookupServiceEnd h