diff --git a/Package.swift b/Package.swift index 8f78277..6942aa6 100644 --- a/Package.swift +++ b/Package.swift @@ -18,6 +18,9 @@ let package = Package( Target( name: "LEScanTest", dependencies: [.Target(name: "BluetoothLinux")]), + Target( + name: "LECreateConnection", + dependencies: [.Target(name: "BluetoothLinux")]), Target( name: "BluetoothLinux") ], diff --git a/Sources/BluetoothLinux/DeviceRequest.swift b/Sources/BluetoothLinux/DeviceRequest.swift index 5a4674a..671190d 100644 --- a/Sources/BluetoothLinux/DeviceRequest.swift +++ b/Sources/BluetoothLinux/DeviceRequest.swift @@ -18,24 +18,29 @@ import Bluetooth public extension Adapter { /// Sends a command to the device and waits for a response. - /* @inline(__always) func deviceRequest(commandParameter: CP, eventParameterType: EP.Type, timeout: Int = 1000) throws -> EP { let command = CP.command - let opcodeGroupField = command.dynamicType.opcodeGroupField - - let parameterData = commandParameter.bytes - - let data = try HCISendRequest(internalSocket, opcode: (command.rawValue, opcodeGroupField.rawValue), commandParameterData: parameterData, eventParameterLength: EP.length, event: EP.event.rawValue, timeout: timeout) - - guard let eventParameter = EP(bytes: data) - else { throw AdapterError.GarbageResponse(Data(bytes: data)) } + let opcodeGroupField = CP.HCICommandType.opcodeGroupField + let parameterData = commandParameter.byteValue + + let data = try HCISendRequest(internalSocket, + opcode: (command.rawValue, opcodeGroupField.rawValue), + commandParameterData: parameterData, + event: EP.event.rawValue, + eventParameterLength: EP.length, + timeout: timeout) + + guard let eventParameter = EP(byteValue: data) + else { throw AdapterError.garbageResponse(Data(bytes: data)) } + return eventParameter } - + + /* @inline(__always) func deviceRequest(command: C, eventParameterType: EP.Type, timeout: Int = 1000) throws -> EP { diff --git a/Sources/BluetoothLinux/L2CAP.swift b/Sources/BluetoothLinux/L2CAP.swift index ad5497c..2167be3 100644 --- a/Sources/BluetoothLinux/L2CAP.swift +++ b/Sources/BluetoothLinux/L2CAP.swift @@ -97,8 +97,11 @@ public final class L2CAPSocket { let socketLength = socklen_t(MemoryLayout.size) // bind socket to port and address - guard withUnsafePointer(to: &localAddress, { bind(internalSocket, unsafeBitCast($0, to: UnsafeMutablePointer.self), socketLength) }) == 0 - else { close(internalSocket); throw POSIXError.fromErrno! } + guard withUnsafeMutablePointer(to: &localAddress, { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1, { + bind(internalSocket, $0, socketLength) == 0 + }) + }) else { close(internalSocket); throw POSIXError.fromErrno! } // set security level var security = bt_security() @@ -141,8 +144,12 @@ public final class L2CAPSocket { var socketLength = socklen_t(MemoryLayout.size) // accept new client - let client = withUnsafeMutablePointer(to: &remoteAddress, { accept(internalSocket, unsafeBitCast($0, to: UnsafeMutablePointer.self), &socketLength) }) - + let client = withUnsafeMutablePointer(to: &remoteAddress, { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1, { + accept(internalSocket, $0, &socketLength) + }) + }) + // error accepting new connection guard client >= 0 else { throw POSIXError.fromErrno! } diff --git a/Sources/BluetoothLinux/LowEnergyConnection.swift b/Sources/BluetoothLinux/LowEnergyConnection.swift index dd4e03d..737a3f7 100644 --- a/Sources/BluetoothLinux/LowEnergyConnection.swift +++ b/Sources/BluetoothLinux/LowEnergyConnection.swift @@ -21,20 +21,31 @@ public extension Adapter { public func lowEnergyCreateConnection(address peerAddress: Address, type peerAddressType: LowEnergyAddressType = .public, ownAddressType: LowEnergyAddressType = .public, - commandTimeout timeout: Int = 1000) throws { + commandTimeout timeout: Int = 1000) throws -> UInt16 { let parameters = LowEnergyCommand.CreateConnectionParameter(peerAddressType: peerAddressType, peerAddress: peerAddress, ownAddressType: ownAddressType) - try lowEnergyCreateConnection(parameters: parameters, commandTimeout: timeout) + return try lowEnergyCreateConnection(parameters: parameters, commandTimeout: timeout) } public func lowEnergyCreateConnection(parameters: LowEnergyCommand.CreateConnectionParameter, - commandTimeout timeout: Int = 1000) throws { + commandTimeout timeout: Int = 1000) throws -> UInt16 { // connect with specified parameters - try deviceRequest(parameters, timeout: timeout) + let event = try deviceRequest(commandParameter: parameters, + eventParameterType: LowEnergyEvent.ConnectionCompleteParameter.self, + timeout: timeout) + + switch event.status { + + case let .error(error): + throw error + + case .success: + return event.handle + } } /// LE Create Connection Cancel Command diff --git a/Sources/L2CAPServerTest/L2CAPServerTest.swift b/Sources/L2CAPServerTest/L2CAPServerTest.swift index b7c4811..3e698dd 100644 --- a/Sources/L2CAPServerTest/L2CAPServerTest.swift +++ b/Sources/L2CAPServerTest/L2CAPServerTest.swift @@ -22,8 +22,11 @@ func PeripheralTest(adapter: Adapter) { let address = adapter.address! - let server = try L2CAPSocket(adapterAddress: address, channelIdentifier: ATT.CID, addressType: .LowEnergyPublic, securityLevel: .Low) - + let server = try L2CAPSocket(adapterAddress: address, + channelIdentifier: ATT.CID, + addressType: .LowEnergyPublic, + securityLevel: .Low) + print("Created L2CAP server") let newConnection = try server.waitForConnection() diff --git a/Sources/LECreateConnection/LECreateConnection.swift b/Sources/LECreateConnection/LECreateConnection.swift new file mode 100644 index 0000000..19b12d2 --- /dev/null +++ b/Sources/LECreateConnection/LECreateConnection.swift @@ -0,0 +1,46 @@ +// +// LEScanTest.swift +// BluetoothLinux +// +// Created by Alsey Coleman Miller on 11/29/17. +// Copyright © 2017 PureSwift. All rights reserved. +// + +#if os(Linux) + import BluetoothLinux + import Glibc +#elseif os(macOS) || os(iOS) + import Darwin.C +#endif + +import Foundation +import Bluetooth + +/// Tests the Scanning functionality +func LECreateConnection(adapter: Adapter, peerAddress: Address, duration: TimeInterval) { + + typealias ConnectionInterval = LowEnergyCommand.CreateConnectionParameter.ConnectionInterval + + let connectionParameters = LowEnergyCommand.CreateConnectionParameter + .init(scanInterval: .min, // 0x0004 + scanWindow: .min, // 0x0004 + initiatorFilterPolicy: .peerAddress, + peerAddressType: .public, + peerAddress: peerAddress, + ownAddressType: .public, + connectionInterval: ConnectionInterval(rawValue: (0x000F ... 0x000F))!, + connectionLatency: .zero, + supervisionTimeout: .max, + connectionLength: .init(rawValue: 0x0001 ... 0x0001)) + + do { + + let handle = try adapter.lowEnergyCreateConnection(parameters: connectionParameters, commandTimeout: 25000) + + print("Connection handle \(handle)") + + + } + + catch { Error("Could not scan: \(error)") } +} diff --git a/Sources/LECreateConnection/main.swift b/Sources/LECreateConnection/main.swift new file mode 100644 index 0000000..f007b20 --- /dev/null +++ b/Sources/LECreateConnection/main.swift @@ -0,0 +1,38 @@ +// +// main.swift +// BluetoothLinux +// +// Created by Alsey Coleman Miller on 12/6/15. +// Copyright © 2015 PureSwift. All rights reserved. +// + +#if os(Linux) + import BluetoothLinux + import Glibc +#elseif os(OSX) || os(iOS) + import Darwin.C +#endif + +import Foundation + +func Error(_ text: String) -> Never { + + print(text) + exit(1) +} + +// get Bluetooth device + +let adapter: Adapter + +do { adapter = try Adapter() } + +catch { Error("Error: \(error)") } + +print("Found Bluetooth adapter with device ID: \(adapter.identifier)") + +print("Address: \(adapter.address!)") + +/// Perform Test +LECreateConnection(adapter: adapter, duration: 10) + diff --git a/Xcode/BluetoothLinux.xcodeproj/project.pbxproj b/Xcode/BluetoothLinux.xcodeproj/project.pbxproj index d2f0ae2..a92471a 100644 --- a/Xcode/BluetoothLinux.xcodeproj/project.pbxproj +++ b/Xcode/BluetoothLinux.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 6E196CCB1C87B47E009FA9CF /* DeviceCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E196CCA1C87B47E009FA9CF /* DeviceCommand.swift */; }; 6E1DC1EC1FCF9B7400C8DE52 /* LowEnergyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E1DC1EB1FCF9B7400C8DE52 /* LowEnergyConnection.swift */; }; 6E1DC1EE1FCFA29700C8DE52 /* LowEnergyWhiteList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E1DC1ED1FCFA29700C8DE52 /* LowEnergyWhiteList.swift */; }; + 6E1DC1F21FCFC1A800C8DE52 /* LECreateConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E1DC1F01FCFC1A800C8DE52 /* LECreateConnection.swift */; }; 6E3229181FCDDB320035605D /* LowEnergyScan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E3229171FCDDB320035605D /* LowEnergyScan.swift */; }; 6E53E1731C84FD8200AC8FCA /* GATTServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E53E1721C84FD8200AC8FCA /* GATTServer.swift */; }; 6E53E1751C84FD8C00AC8FCA /* GATTClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E53E1741C84FD8C00AC8FCA /* GATTClient.swift */; }; @@ -78,6 +79,8 @@ 6E196CCA1C87B47E009FA9CF /* DeviceCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceCommand.swift; sourceTree = ""; }; 6E1DC1EB1FCF9B7400C8DE52 /* LowEnergyConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LowEnergyConnection.swift; sourceTree = ""; }; 6E1DC1ED1FCFA29700C8DE52 /* LowEnergyWhiteList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LowEnergyWhiteList.swift; sourceTree = ""; }; + 6E1DC1F01FCFC1A800C8DE52 /* LECreateConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LECreateConnection.swift; sourceTree = ""; }; + 6E1DC1F11FCFC1A800C8DE52 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 6E3229171FCDDB320035605D /* LowEnergyScan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LowEnergyScan.swift; sourceTree = ""; }; 6E53E1721C84FD8200AC8FCA /* GATTServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GATTServer.swift; sourceTree = ""; }; 6E53E1741C84FD8C00AC8FCA /* GATTClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GATTClient.swift; sourceTree = ""; }; @@ -137,6 +140,7 @@ 6E0C5BBA1C83794700AF63E5 /* Tests Tools */ = { isa = PBXGroup; children = ( + 6E1DC1EF1FCFC1A800C8DE52 /* LECreateConnection */, 6E6AD9841FCE819A007F0250 /* LEScanTest */, 6EF3551D1C965E8F00730BAB /* GATTServerTest */, 6EA37EF01C84B4EA00A61969 /* L2CAPServerTest */, @@ -166,6 +170,16 @@ path = ../Sources/iBeaconTest; sourceTree = ""; }; + 6E1DC1EF1FCFC1A800C8DE52 /* LECreateConnection */ = { + isa = PBXGroup; + children = ( + 6E1DC1F01FCFC1A800C8DE52 /* LECreateConnection.swift */, + 6E1DC1F11FCFC1A800C8DE52 /* main.swift */, + ); + name = LECreateConnection; + path = ../Sources/LECreateConnection; + sourceTree = ""; + }; 6E6AD9841FCE819A007F0250 /* LEScanTest */ = { isa = PBXGroup; children = ( @@ -405,6 +419,7 @@ 6E894C001C83EA2000109F45 /* main.swift in Sources */, 6E196CC31C8659DA009FA9CF /* HCI.swift in Sources */, 6E196CC71C86A5EC009FA9CF /* BluetoothProtocol.swift in Sources */, + 6E1DC1F21FCFC1A800C8DE52 /* LECreateConnection.swift in Sources */, 6E894BF41C83B51B00109F45 /* AddressType.swift in Sources */, 6E53E1791C8503CA00AC8FCA /* ATTConnection.swift in Sources */, 6EA37EF41C84B4EA00A61969 /* L2CAPServerTest.swift in Sources */,