From 0c4a1cc1c2083a871562364de5df4f9d88ec321e Mon Sep 17 00:00:00 2001 From: Electric-Blue Date: Tue, 26 Jan 2016 02:03:48 +0300 Subject: First version. --- nimbluez/bluetoothnativesockets.nim | 433 ++++++++++++++++++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 nimbluez/bluetoothnativesockets.nim (limited to 'nimbluez/bluetoothnativesockets.nim') diff --git a/nimbluez/bluetoothnativesockets.nim b/nimbluez/bluetoothnativesockets.nim new file mode 100644 index 0000000..b079b42 --- /dev/null +++ b/nimbluez/bluetoothnativesockets.nim @@ -0,0 +1,433 @@ +# Copyright (c) 2016, Maxim V. Abramov +# All rights reserved. +# Look at license.txt for more info. + +## This module implements a low-level cross-platform sockets interface for +## Bluetooth. + +import os, nativesockets + +const useWinVersion = defined(Windows) or defined(nimdoc) + +when useWinVersion: + import winlean + import msbt/ms_bluetoothapis, msbt/ms_ws2bth + import bluetoothmsbt +else: + from posix import InvalidSocket, getsockname, getpeername + import bluez/bz_bluetooth, bluez/bz_rfcomm, bluez/bz_l2cap + import bluetoothbluez + +export SocketHandle, SockAddr, SockLen, SockType, Port +export + connect, close, listen, accept, send, recv, sendto, recvfrom, `==`, bindAddr, + getSockOptInt, setSockOptInt +export InvalidSocket +export + SOL_L2CAP, SOL_RFCOMM, + SO_ERROR, + SOMAXCONN, + SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE, + SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR + #MSG_PEEK + + +type + ProtocolFamily* = enum ## Protocol family of the socket. + PF_BLUETOOTH = 31 ## for Bluetooth socket. + + BluetoothDomain* = enum ## Address family of the socket. This extends Domain + ## form the nativesockets. + AF_BLUETOOTH = 31 ## for Bluetooth socket. + + BluetoothProtocol* = enum ## third argument to `socket` proc + BTPROTO_L2CAP = 0, ## Logical link control and adaptation protocol. + ## Unsupported on Windows. + BTPROTO_RFCOMM = 3 ## Radio frequency communication. + + RfcommPort* = 0..30 ## RFCOMM channel. The valid range for requesting + ## a specific RFCOMM port is 1 through 30 or 0 for + ## automatic assign. + + L2capPort* = 0..32767 ## L2CAP port (Protocol Service Multiplexer) can take + ## on odd-numbered values between 1 and 32767. Reserved + ## (well known) port numbers are between 1 and 4095. + + +when useWinVersion: + type + RfcommAddr* = SOCKADDR_BTH + L2capAddr* = object +else: + type + RfcommAddr* = sockaddr_rc + L2capAddr* = sockaddr_l2 + + +type + RfcommAddrRef* = ref RfcommAddr + L2capAddrRef* = ref L2capAddr + + + +when useWinVersion: + const + nativePfBluetooth* = ms_ws2bth.PF_BTH + nativeAfBluetooth* = ms_ws2bth.AF_BTH + WSAEPROTONOSUPPORT = OSErrorCode(10043) +else: + const + nativePfBluetooth* = bz_bluetooth.PF_BLUETOOTH + nativeAfBluetooth* = bz_bluetooth.AF_BLUETOOTH + + +proc toInt*(family: ProtocolFamily): cint + ## Converts the ProtocolFamily enum to a platform-dependent ``cint``. + + +proc toInt*(domain: BluetoothDomain): cint + ## Converts the BluetoothDomain enum to a platform-dependent ``cint``. + + +proc toInt*(protocol: BluetoothProtocol): cint + ## Converts the BluetoothProtocol enum to a platform-dependent ``cint``. + + +when useWinVersion: + proc toInt*(family: ProtocolFamily): cint = + case family + of PF_BLUETOOTH: result = cint(ms_ws2bth.PF_BTH) + else: discard + + proc toInt*(domain: BluetoothDomain): cint = + case domain + of AF_BLUETOOTH: result = cint(ms_ws2bth.AF_BTH) + + + proc toInt*(protocol: BluetoothProtocol): cint = + case protocol + of BTPROTO_L2CAP: result = cint(ms_ws2bth.BTHPROTO_L2CAP) + of BTPROTO_RFCOMM: result = cint(ms_ws2bth.BTHPROTO_RFCOMM) + +else: + proc toInt*(family: ProtocolFamily): cint = + case family + of PF_BLUETOOTH: result = cint(bz_bluetooth.PF_BLUETOOTH) + + + proc toInt*(domain: BluetoothDomain): cint = + case domain + of AF_BLUETOOTH: result = cint(bz_bluetooth.AF_BLUETOOTH) + + + proc toInt*(protocol: BluetoothProtocol): cint = + case protocol + of BTPROTO_L2CAP: result = cint(bz_bluetooth.BTPROTO_L2CAP) + of BTPROTO_RFCOMM: result = cint(bz_bluetooth.BTPROTO_RFCOMM) + + +proc htobs*(d: int16): int16 = + ## Converts 16-bit integers from host to Bluetooth byte order. + ## On machines where the host byte order is the same as Bluetooth byte order, + ## this is a no-op; otherwise, it performs a 2-byte swap operation. + when cpuEndian == bigEndian: + swapEndian16(result, d) + else: + result = d + + +proc htobl*(d: int32): int32 = + ## Converts 32-bit integers from host to Bluetooth byte order. + ## On machines where the host byte order is the same as Bluetooth byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + when cpuEndian == bigEndian: + swapEndian32(result, d) + else: + result = d + + +proc htobll*(d: int64): int64 = + ## Converts 64-bit integers from host to Bluetooth byte order. + ## On machines where the host byte order is the same as Bluetooth byte order, + ## this is a no-op; otherwise, it performs a 8-byte swap operation. + when cpuEndian == bigEndian: + swapEndian64(result, d) + else: + result = d + + +template btohs*(d: expr): expr = + ## Converts 16-bit integers from Bluetooth to host byte order. + ## On machines where the host byte order is the same as Bluetooth byte order, + ## this is a no-op; otherwise, it performs a 2-byte swap operation. + htobs(d) + + +template btohl*(d: expr): expr = + ## Converts 32-bit integers from Bluetooth to host byte order. + ## On machines where the host byte order is the same as Bluetooth byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + htobl(d) + + +template btohll*(d: expr): expr = + ## Converts 64-bit integers from Bluetooth to host byte order. + ## On machines where the host byte order is the same as Bluetooth byte order, + ## this is a no-op; otherwise, it performs a 8-byte swap operation. + htobll(d) + + +when not useWinVersion: + proc htob_bdaddr*(d: bdaddr_t): bdaddr_t = + ## Converts bdaddr_t from host toBluetooth byte order. + ## On machines where the host byte order is the same as Bluetooth, + ## this is a no-op; otherwise, it performs a 6-byte swap operation. + when cpuEndian == bigEndian: + for i in countup(0, 5): + result[5 - i] = d[i] + else: + result = d + + template btoh_bdaddr*(d: expr): expr = + ## Converts bdaddr_t from Bluetooth to host byte order. + ## On machines where the host byte order is the same as Bluetooth, + ## this is a no-op; otherwise, it performs a 6-byte swap operation. + htob_bdaddr(d) + + +proc newBluetoothNativeSocket*(sockType: SockType = SOCK_STREAM, + protocol: BluetoothProtocol = BTPROTO_RFCOMM): + SocketHandle = + ## Creates a new Bluetooth socket; returns `InvalidSocket` if an error occurs. + result = + newNativeSocket(toInt(ProtocolFamily.PF_BLUETOOTH), + toInt(sockType), + toInt(protocol)) + + +proc getRfcommAddr*(port = RfcommPort(0), address = ""): RfcommAddr + ## Creates and fills Bluetooth address structure for RFCOMM protocol. + + +proc getL2capAddr*(port = L2capPort(0), address = ""): L2capAddr + ## Creates and fills Bluetooth address structure for L2CAP protocol. + + +proc getRfcommLocalName*(socket: SocketHandle): RfcommAddr + ## Returns the RFCOMM socket's associated port number. + + +proc getL2capLocalName*(socket: SocketHandle): L2capAddr + ## Returns the L2CAP socket's associated port number. + + +proc getRfcommPeerName*(socket: SocketHandle): RfcommAddr + ## Returns the RFCOMM socket's associated port number. + + +proc getL2capPeerName*(socket: SocketHandle): L2capAddr + ## Returns the L2CAP socket's associated port number. + + +proc getAddrString*(sockAddr: RfcommAddr): string + ## Returns the string representation of a Bluetooth address. + + +proc getAddrString*(sockAddr: L2capAddr): string + ## Returns the string representation of a Bluetooth address. + + +proc getAddrPort*(sockAddr: RfcommAddr): RfcommPort + ## Returns port of a Bluetooth address. + + +proc getAddrPort*(sockAddr: L2capAddr): L2capPort + ## Returns port of a Bluetooth address. + + +when useWinVersion: + proc getRfcommAddr*(port = RfcommPort(0), address = ""): RfcommAddr = + result.addressFamily = htobs( + toInt(BluetoothDomain.AF_BLUETOOTH).int16).uint16 + if address != nil and address != "": + result.btAddr = htobll( + parseBluetoothAddress(address).ano_116103095.ullLong.int32).uint32 + #result.serviceClassId = + #TODO: use htob... proc there. + result.port = htobl(if port == 0: -1'i32 else: int32(port)) + #result.port = htobl(ULONG(port)) + + + proc getL2capAddr*(port = L2capPort(0), address = ""): L2capAddr = + raiseOSError(WSAEPROTONOSUPPORT) + + + proc getRfcommLocalName*(socket: SocketHandle): RfcommAddr = + var name = getRfcommAddr() + var nameLen = sizeof(name).SockLen + if getsockname(socket, + cast[ptr SockAddr](addr(name)), + addr(nameLen)) == -1'i32: + raiseOSError(osLastError()) + result = name + + + proc getL2capLocalName*(socket: SocketHandle): L2capAddr = + raiseOSError(WSAEPROTONOSUPPORT) + + + proc getRfcommPeerName*(socket: SocketHandle): RfcommAddr = + var name = getRfcommAddr() + var nameLen = sizeof(name).SockLen + if getpeername(socket, + cast[ptr SockAddr](addr(name)), + addr(nameLen)) == -1'i32: + raiseOSError(osLastError()) + result = name + + + proc getL2capPeerName*(socket: SocketHandle): L2capAddr = + raiseOSError(WSAEPROTONOSUPPORT) + + + proc getAddrString*(sockAddr: RfcommAddr): string = + result = $cast[BLUETOOTH_ADDRESS](btohll(sockAddr.btAddr.int64)) + + + proc getAddrString*(sockAddr: L2capAddr): string = + raiseOSError(WSAEPROTONOSUPPORT) + + + proc getAddrPort*(sockAddr: RfcommAddr): RfcommPort = + result = btohl(sockAddr.port) + + + proc getAddrPort*(sockAddr: L2capAddr): L2capPort = + raiseOSError(WSAEPROTONOSUPPORT) + + +else: + proc getRfcommAddr*(port = RfcommPort(0), address = ""): RfcommAddr = + result.rc_family = htobs(toInt(BluetoothDomain.AF_BLUETOOTH).uint16) + if address != nil and address != "": + result.rc_bdaddr = htob_bdaddr(parseBluetoothAddress(address)) + if port != RfcommPort(0): + result.rc_channel = uint8(port) + + + proc getL2capAddr*(port = L2capPort(0), address = ""): L2capAddr = + result.l2_family = htobs(toInt(BluetoothDomain.AF_BLUETOOTH).uint16) + if address != nil and address != "": + result.l2_bdaddr = htob_bdaddr(parseBluetoothAddress(address)) + if port != L2capPort(0): + result.l2_psm = htobs(cushort(port)) + + + proc getRfcommLocalName*(socket: SocketHandle): RfcommAddr = + var name = getRfcommAddr() + var nameLen = sizeof(name).SockLen + if getsockname(socket, + cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = name + + + proc getL2capLocalName*(socket: SocketHandle): L2capAddr = + var name = getL2capAddr() + var nameLen = sizeof(name).SockLen + if getsockname(socket, + cast[ptr SockAddr](addr(name)), + addr(nameLen)) == -1'i32: + raiseOSError(osLastError()) + result = name + + + proc getRfcommPeerName*(socket: SocketHandle): RfcommAddr = + var name = getRfcommAddr() + var nameLen = sizeof(name).SockLen + if getpeername(socket, + cast[ptr SockAddr](addr(name)), + addr(nameLen)) == -1'i32: + raiseOSError(osLastError()) + result = name + + + proc getL2capPeerName*(socket: SocketHandle): L2capAddr = + var name = getL2capAddr() + var nameLen = sizeof(name).SockLen + if getpeername(socket, + cast[ptr SockAddr](addr(name)), + addr(nameLen)) == -1'i32: + raiseOSError(osLastError()) + result = name + + + proc getAddrString*(sockAddr: RfcommAddr): string = + result = $btoh_bdaddr(sockAddr.rc_bdaddr) + + + proc getAddrString*(sockAddr: L2capAddr): string = + result = $btoh_bdaddr(sockAddr.l2_bdaddr) + + + proc getAddrPort*(sockAddr: RfcommAddr): RfcommPort = + result = sockAddr.rc_channel + + + proc getAddrPort*(sockAddr: L2capAddr): L2capPort = + result = btohs(sockAddr.l2_psm) + + +proc getRfcommSockName*(socket: SocketHandle): RfcommPort = + ## Returns the RFCOMM socket's associated port number. + var name = getRfcommLocalName(socket) + result = getAddrPort(name) + + +proc getL2capSockName*(socket: SocketHandle): L2capPort = + ## Returns the L2CAP socket's associated port number. + var name = getL2capLocalName(socket) + result = getAddrPort(name) + + +proc getRfcommLocalAddr*(socket: SocketHandle): (string, RfcommPort) = + ## Returns the socket's local address and port number. + ## + ## Similar to POSIX's `getsockname`:idx:. + var name = getRfcommLocalName(socket) + result = (getAddrString(name), getAddrPort(name)) + + +proc getL2capLocalAddr*(socket: SocketHandle): (string, L2capPort) = + ## Returns the socket's local address and port number. + ## + ## Similar to POSIX's `getsockname`:idx:. + var name = getL2capLocalName(socket) + result = (getAddrString(name), getAddrPort(name)) + + +proc getRfcommPeerAddr*(socket: SocketHandle): (string, RfcommPort) = + ## Returns the socket's peer address and port number. + ## + ## Similar to POSIX's `getpeername`:idx: + var name = getRfcommAddr() + var nameLen = sizeof(name).SockLen + if getpeername(socket, + cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = (getAddrString(name), getAddrPort(name)) + + +proc getL2capPeerAddr*(socket: SocketHandle): (string, L2capPort) = + ## Returns the socket's peer address and port number. + ## + ## Similar to POSIX's `getpeername`:idx: + var name = getL2capAddr() + var nameLen = sizeof(name).SockLen + if getpeername(socket, + cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = (getAddrString(name), getAddrPort(name)) -- cgit v1.2.3