From 6d28c278da2ee61807aae0571088f224e4f25dd7 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 14:19:39 +0200 Subject: [PATCH 1/8] Initial WIP on OSX High Sierra compat. Signed-off-by: deadprogram --- darwin/client.go | 26 ++++---- darwin/conn.go | 2 +- darwin/device.go | 122 ++++++++++++++-------------------- darwin/xpcid.go | 170 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 86 deletions(-) create mode 100644 darwin/xpcid.go diff --git a/darwin/client.go b/darwin/client.go index 98c21d1..af0fd2c 100644 --- a/darwin/client.go +++ b/darwin/client.go @@ -68,7 +68,7 @@ func (cln *Client) DiscoverProfile(force bool) (*ble.Profile, error) { // DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1] // If filter is specified, only filtered services are returned. func (cln *Client) DiscoverServices(ss []ble.UUID) ([]*ble.Service, error) { - rsp := cln.conn.sendReq(45, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdDiscoverServices], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgUUIDs": uuidSlice(ss), }) @@ -93,7 +93,7 @@ func (cln *Client) DiscoverServices(ss []ble.UUID) ([]*ble.Service, error) { // DiscoverIncludedServices finds the included services of a service. [Vol 3, Part G, 4.5.1] // If filter is specified, only filtered services are returned. func (cln *Client) DiscoverIncludedServices(ss []ble.UUID, s *ble.Service) ([]*ble.Service, error) { - rsp := cln.conn.sendReq(60, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdDiscoverIncludedServices], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgServiceStartHandle": s.Handle, "kCBMsgArgServiceEndHandle": s.EndHandle, @@ -108,7 +108,7 @@ func (cln *Client) DiscoverIncludedServices(ss []ble.UUID, s *ble.Service) ([]*b // DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1] // If filter is specified, only filtered characteristics are returned. func (cln *Client) DiscoverCharacteristics(cs []ble.UUID, s *ble.Service) ([]*ble.Characteristic, error) { - rsp := cln.conn.sendReq(62, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdDiscoverCharacteristics], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgServiceStartHandle": s.Handle, "kCBMsgArgServiceEndHandle": s.EndHandle, @@ -132,7 +132,7 @@ func (cln *Client) DiscoverCharacteristics(cs []ble.UUID, s *ble.Service) ([]*bl // DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1] // If filter is specified, only filtered descriptors are returned. func (cln *Client) DiscoverDescriptors(ds []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) { - rsp := cln.conn.sendReq(70, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdDiscoverDescriptors], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgCharacteristicHandle": c.Handle, "kCBMsgArgCharacteristicValueHandle": c.ValueHandle, @@ -150,7 +150,7 @@ func (cln *Client) DiscoverDescriptors(ds []ble.UUID, c *ble.Characteristic) ([] // ReadCharacteristic reads a characteristic value from a server. [Vol 3, Part G, 4.8.1] func (cln *Client) ReadCharacteristic(c *ble.Characteristic) ([]byte, error) { - rsp := cln.conn.sendReq(65, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdReadCharacteristic], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgCharacteristicHandle": c.Handle, "kCBMsgArgCharacteristicValueHandle": c.ValueHandle, @@ -176,15 +176,15 @@ func (cln *Client) WriteCharacteristic(c *ble.Characteristic, b []byte, noRsp bo "kCBMsgArgType": map[bool]int{false: 0, true: 1}[noRsp], } if noRsp { - cln.conn.sendCmd(66, args) + cln.conn.sendCmd(xpcID[cmdWriteCharacteristic], args) return nil } - return cln.conn.sendReq(66, args).err() + return cln.conn.sendReq(xpcID[cmdWriteCharacteristic], args).err() } // ReadDescriptor reads a characteristic descriptor from a server. [Vol 3, Part G, 4.12.1] func (cln *Client) ReadDescriptor(d *ble.Descriptor) ([]byte, error) { - rsp := cln.conn.sendReq(77, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdReadDescriptor], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgDescriptorHandle": d.Handle, }) @@ -196,7 +196,7 @@ func (cln *Client) ReadDescriptor(d *ble.Descriptor) ([]byte, error) { // WriteDescriptor writes a characteristic descriptor to a server. [Vol 3, Part G, 4.12.3] func (cln *Client) WriteDescriptor(d *ble.Descriptor, b []byte) error { - rsp := cln.conn.sendReq(78, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdWriteDescriptor], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgDescriptorHandle": d.Handle, "kCBMsgArgData": b, @@ -206,7 +206,7 @@ func (cln *Client) WriteDescriptor(d *ble.Descriptor, b []byte) error { // ReadRSSI retrieves the current RSSI value of remote peripheral. [Vol 2, Part E, 7.5.4] func (cln *Client) ReadRSSI() int { - rsp := cln.conn.sendReq(44, xpc.Dict{"kCBMsgArgDeviceUUID": cln.id}) + rsp := cln.conn.sendReq(xpcID[cmdReadRSSI], xpc.Dict{"kCBMsgArgDeviceUUID": cln.id}) if rsp.err() != nil { return 0 } @@ -226,7 +226,7 @@ func (cln *Client) Subscribe(c *ble.Characteristic, ind bool, fn ble.Notificatio cln.conn.Lock() defer cln.conn.Unlock() cln.conn.subs[c.Handle] = &sub{fn: fn, char: c} - rsp := cln.conn.sendReq(68, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdSubscribeCharacteristic], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgCharacteristicHandle": c.Handle, "kCBMsgArgCharacteristicValueHandle": c.ValueHandle, @@ -242,7 +242,7 @@ func (cln *Client) Subscribe(c *ble.Characteristic, ind bool, fn ble.Notificatio // Unsubscribe unsubscribes to indication (if ind is set true), or notification // of a specified characteristic value. [Vol 3, Part G, 4.10 & 4.11] func (cln *Client) Unsubscribe(c *ble.Characteristic, ind bool) error { - rsp := cln.conn.sendReq(68, xpc.Dict{ + rsp := cln.conn.sendReq(xpcID[cmdSubscribeCharacteristic], xpc.Dict{ "kCBMsgArgDeviceUUID": cln.id, "kCBMsgArgCharacteristicHandle": c.Handle, "kCBMsgArgCharacteristicValueHandle": c.ValueHandle, @@ -267,7 +267,7 @@ func (cln *Client) ClearSubscriptions() error { // CancelConnection disconnects the connection. func (cln *Client) CancelConnection() error { - rsp := cln.conn.sendReq(32, xpc.Dict{"kCBMsgArgDeviceUUID": cln.id}) + rsp := cln.conn.sendReq(xpcID[cmdDisconnect], xpc.Dict{"kCBMsgArgDeviceUUID": cln.id}) return rsp.err() } diff --git a/darwin/conn.go b/darwin/conn.go index 0318291..266a71d 100644 --- a/darwin/conn.go +++ b/darwin/conn.go @@ -102,7 +102,7 @@ func (c *conn) subscribed(char *ble.Characteristic) { return } send := func(b []byte) (int, error) { - c.dev.sendCmd(c.dev.pm, 15, xpc.Dict{ + c.dev.sendCmd(c.dev.pm, xpcID[cmdSubscribed], xpc.Dict{ "kCBMsgArgUUIDs": [][]byte{}, "kCBMsgArgAttributeID": h, "kCBMsgArgData": b, diff --git a/darwin/device.go b/darwin/device.go index fb8b6e8..69a3d43 100644 --- a/darwin/device.go +++ b/darwin/device.go @@ -15,35 +15,6 @@ import ( "sync" ) -const ( - evtStateChanged = 4 - evtStateChangedPreHighSierra = 6 - evtAdvertisingStarted = 16 - evtAdvertisingStopped = 17 - evtServiceAdded = 18 - evtReadRequest = 19 - evtWriteRequest = 20 - evtSubscribe = 21 - evtUnubscribe = 22 - evtConfirmation = 23 - evtPeripheralDiscovered = 37 - evtPeripheralConnected = 38 - evtPeripheralDisconnected = 40 - evtATTMTU = 53 - evtRSSIRead = 55 - evtServiceDiscovered = 56 - evtIncludedServicesDiscovered = 63 - evtCharacteristicsDiscovered = 64 - evtCharacteristicRead = 71 - evtCharacteristicWritten = 72 - evtNotificationValueSet = 74 - evtDescriptorsDiscovered = 76 - evtDescriptorRead = 79 - evtDescriptorWritten = 80 - evtSleveConnectionComplete = 81 - evtMasterConnectionComplete = 82 -) - // Device is either a Peripheral or Central device. type Device struct { pm xpc.XPC // peripheralManager @@ -78,8 +49,16 @@ func NewDevice(opts ...Option) (*Device, error) { return nil, err } - d.pm = xpc.XpcConnect("com.apple.blued", d) - d.cm = xpc.XpcConnect("com.apple.blued", d) + initXpcIDs() + if darwinOSVersion < 17 { + // yosemite + d.pm = xpc.XpcConnect("com.apple.blued", d) + d.cm = xpc.XpcConnect("com.apple.blued", d) + } else { + // high sierra + d.pm = xpc.XpcConnect("com.apple.bluetoothd", d) + d.cm = xpc.XpcConnect("com.apple.bluetoothd", d) + } return d, errors.Wrap(d.Init(), "can't init") } @@ -95,7 +74,7 @@ func (d *Device) Option(opts ...Option) error { // Init ... func (d *Device) Init() error { - rsp := d.sendReq(d.cm, 1, xpc.Dict{ + rsp := d.sendReq(d.cm, xpcID[cmdInit], xpc.Dict{ "kCBMsgArgName": fmt.Sprintf("gopher-%v", time.Now().Unix()), "kCBMsgArgOptions": xpc.Dict{ "kCBInitOptionShowPowerAlert": 1, @@ -107,7 +86,7 @@ func (d *Device) Init() error { return fmt.Errorf("state: %s", s) } - rsp = d.sendReq(d.pm, 1, xpc.Dict{ + rsp = d.sendReq(d.pm, xpcID[cmdInit], xpc.Dict{ "kCBMsgArgName": fmt.Sprintf("gopher-%v", time.Now().Unix()), "kCBMsgArgOptions": xpc.Dict{ "kCBInitOptionShowPowerAlert": 1, @@ -123,7 +102,7 @@ func (d *Device) Init() error { // Advertise advertises the given Advertisement func (d *Device) Advertise(ctx context.Context, adv ble.Advertisement) error { - if err := d.sendReq(d.pm, 8, xpc.Dict{ + if err := d.sendReq(d.pm, xpcID[cmdAdvertiseStart], xpc.Dict{ "kCBAdvDataLocalName": adv.LocalName(), "kCBAdvDataServiceUUIDs": adv.Services(), "kCBAdvDataAppleMfgData": adv.ManufacturerData(), @@ -140,7 +119,7 @@ func (d *Device) Advertise(ctx context.Context, adv ble.Advertisement) error { func (d *Device) AdvertiseMfgData(ctx context.Context, id uint16, md []byte) error { l := len(md) b := []byte{byte(l + 3), 0xFF, uint8(id), uint8(id >> 8)} - if err := d.sendReq(d.pm, 8, xpc.Dict{ + if err := d.sendReq(d.pm, xpcID[cmdAdvertiseStart], xpc.Dict{ "kCBAdvDataAppleMfgData": append(b, md...), }).err(); err != nil { return errors.Wrap(err, "can't advertise") @@ -156,7 +135,7 @@ func (d *Device) AdvertiseServiceData16(ctx context.Context, id uint16, b []byte 0x03, 0x03, uint8(id), uint8(id >> 8), byte(l + 3), 0x16, uint8(id), uint8(id >> 8), } - if err := d.sendReq(d.pm, 8, xpc.Dict{ + if err := d.sendReq(d.pm, xpcID[cmdAdvertiseStart], xpc.Dict{ "kCBAdvDataAppleMfgData": append(prefix, b...), }).err(); err != nil { return errors.Wrap(err, "can't advertise") @@ -167,7 +146,7 @@ func (d *Device) AdvertiseServiceData16(ctx context.Context, id uint16, b []byte // AdvertiseNameAndServices advertises name and specifid service UUIDs. func (d *Device) AdvertiseNameAndServices(ctx context.Context, name string, ss ...ble.UUID) error { - if err := d.sendReq(d.pm, 8, xpc.Dict{ + if err := d.sendReq(d.pm, xpcID[cmdAdvertiseStart], xpc.Dict{ "kCBAdvDataLocalName": name, "kCBAdvDataServiceUUIDs": uuidSlice(ss)}, ).err(); err != nil { @@ -187,7 +166,7 @@ func (d *Device) AdvertiseIBeaconData(ctx context.Context, md []byte) error { ibeaconCode := []byte{0x02, 0x15} return d.AdvertiseMfgData(ctx, 0x004C, append(ibeaconCode, md...)) } - if err := d.sendReq(d.pm, 8, xpc.Dict{"kCBAdvDataAppleBeaconKey": md}).err(); err != nil { + if err := d.sendReq(d.pm, xpcID[cmdAdvertiseStart], xpc.Dict{"kCBAdvDataAppleBeaconKey": md}).err(); err != nil { return err } <-ctx.Done() @@ -206,13 +185,13 @@ func (d *Device) AdvertiseIBeacon(ctx context.Context, u ble.UUID, major, minor // stopAdvertising stops advertising. func (d *Device) stopAdvertising() error { - return errors.Wrap(d.sendReq(d.pm, 9, nil).err(), "can't stop advertising") + return errors.Wrap(d.sendReq(d.pm, xpcID[cmdAdvertiseStop], nil).err(), "can't stop advertising") } // Scan ... func (d *Device) Scan(ctx context.Context, allowDup bool, h ble.AdvHandler) error { d.advHandler = h - if err := d.sendCmd(d.cm, 29, xpc.Dict{ + if err := d.sendCmd(d.cm, xpcID[cmdScanningStart], xpc.Dict{ // "kCBMsgArgUUIDs": uuidSlice(ss), "kCBMsgArgOptions": xpc.Dict{ "kCBScanOptionAllowDuplicates": map[bool]int{true: 1, false: 0}[allowDup], @@ -229,12 +208,12 @@ func (d *Device) Scan(ctx context.Context, allowDup bool, h ble.AdvHandler) erro // stopAdvertising stops advertising. func (d *Device) stopScanning() error { - return errors.Wrap(d.sendCmd(d.cm, 30, nil), "can't stop scanning") + return errors.Wrap(d.sendCmd(d.cm, xpcID[cmdScanningStop], nil), "can't stop scanning") } // RemoveAllServices removes all services of device's func (d *Device) RemoveAllServices() error { - return d.sendCmd(d.pm, 12, nil) + return d.sendCmd(d.pm, xpcID[cmdServicesRemove], nil) } // AddService adds a service to device's database. @@ -335,7 +314,7 @@ func (d *Device) AddService(s *ble.Service) error { } xs["kCBMsgArgCharacteristics"] = xcs - return d.sendReq(d.pm, 10, xs).err() + return d.sendReq(d.pm, xpcID[cmdServicesAdd], xs).err() } // SetServices ... @@ -353,7 +332,7 @@ func (d *Device) SetServices(ss []*ble.Service) error { // Dial ... func (d *Device) Dial(ctx context.Context, a ble.Addr) (ble.Client, error) { - d.sendCmd(d.cm, 31, xpc.Dict{ + d.sendCmd(d.cm, xpcID[cmdConnect], xpc.Dict{ "kCBMsgArgDeviceUUID": xpc.MakeUUID(a.String()), "kCBMsgArgOptions": xpc.Dict{ "kCBConnectOptionNotifyOnDisconnection": 1, @@ -385,30 +364,29 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { switch m.id() { case // Device event - evtStateChanged, - evtStateChangedPreHighSierra, - evtAdvertisingStarted, - evtAdvertisingStopped, - evtServiceAdded: + xpcID[evtStateChanged], + xpcID[evtAdvertisingStarted], + xpcID[evtAdvertisingStopped], + xpcID[evtServiceAdded]: d.rspc <- args - case evtPeripheralDiscovered: + case xpcID[evtPeripheralDiscovered]: if d.advHandler == nil { break } a := &adv{args: m.args(), ad: args.advertisementData()} go d.advHandler(a) - case evtConfirmation: + case xpcID[evtConfirmation]: // log.Printf("confirmed: %d", args.attributeID()) - case evtATTMTU: + case xpcID[evtATTMTU]: d.conn(args).SetTxMTU(args.attMTU()) - case evtSleveConnectionComplete: + case xpcID[evtSlaveConnectionComplete]: // remote peripheral is connected. fallthrough - case evtMasterConnectionComplete: + case xpcID[evtMasterConnectionComplete]: // remote central is connected. // Could be LEConnectionComplete or LEConnectionUpdateComplete. @@ -417,7 +395,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { c.connLatency = args.connectionLatency() c.supervisionTimeout = args.supervisionTimeout() - case evtReadRequest: + case xpcID[evtReadRequest]: aid := args.attributeID() char := d.chars[aid] v := char.Value @@ -430,14 +408,14 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { v = buf.Bytes() } - d.sendCmd(d.pm, 13, xpc.Dict{ + d.sendCmd(d.pm, xpcID[cmdSendData], xpc.Dict{ "kCBMsgArgAttributeID": aid, "kCBMsgArgData": v, "kCBMsgArgTransactionID": args.transactionID(), "kCBMsgArgResult": 0, }) - case evtWriteRequest: + case xpcID[evtWriteRequest]: for _, xxw := range args.attWrites() { xw := msg(xxw.(xpc.Dict)) aid := xw.attributeID() @@ -447,7 +425,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { if xw.ignoreResponse() == 1 { continue } - d.sendCmd(d.pm, 13, xpc.Dict{ + d.sendCmd(d.pm, xpcID[cmdSendData], xpc.Dict{ "kCBMsgArgAttributeID": aid, "kCBMsgArgData": nil, "kCBMsgArgTransactionID": args.transactionID(), @@ -455,18 +433,18 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { }) } - case evtSubscribe: + case xpcID[evtSubscribe]: // characteristic is subscribed by remote central. d.conn(args).subscribed(d.chars[args.attributeID()]) - case evtUnubscribe: + case xpcID[evtUnubscribe]: // characteristic is unsubscribed by remote central. d.conn(args).unsubscribed(d.chars[args.attributeID()]) - case evtPeripheralConnected: + case xpcID[evtPeripheralConnected]: d.chConn <- d.conn(args) - case evtPeripheralDisconnected: + case xpcID[evtPeripheralDisconnected]: c := d.conn(args) select { case c.rspc <- m: @@ -479,7 +457,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { d.connLock.Unlock() close(c.done) - case evtCharacteristicRead: + case xpcID[evtCharacteristicRead]: // Notification c := d.conn(args) if args.isNotification() != 0 { @@ -495,15 +473,15 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { c.rspc <- m case // Peripheral events - evtRSSIRead, - evtServiceDiscovered, - evtIncludedServicesDiscovered, - evtCharacteristicsDiscovered, - evtCharacteristicWritten, - evtNotificationValueSet, - evtDescriptorsDiscovered, - evtDescriptorRead, - evtDescriptorWritten: + xpcID[evtRSSIRead], + xpcID[evtServiceDiscovered], + xpcID[evtIncludedServicesDiscovered], + xpcID[evtCharacteristicsDiscovered], + xpcID[evtCharacteristicWritten], + xpcID[evtNotificationValueSet], + xpcID[evtDescriptorsDiscovered], + xpcID[evtDescriptorRead], + xpcID[evtDescriptorWritten]: d.conn(args).rspc <- m diff --git a/darwin/xpcid.go b/darwin/xpcid.go new file mode 100644 index 0000000..59812be --- /dev/null +++ b/darwin/xpcid.go @@ -0,0 +1,170 @@ +package darwin + +import ( + "fmt" + "os/exec" + "strconv" + "strings" +) + +const ( + evtDescriptorWritten = 80 + evtSlaveConnectionComplete = 81 + evtMasterConnectionComplete = 82 +) + +var darwinOSVersion int + +func getDarwinReleaseVersion() int { + version, err := exec.Command("uname", "-r").Output() + if err != nil { + fmt.Println(err) + return 0 + } + v, _ := strconv.Atoi(strings.Split(version, ".")[0]) + return v +} + +// xpc command IDs are OS X version specific, so we will use a map +// to be able to handle arbitrary versions +const ( + cmdInit = iota + cmdAdvertiseStart + cmdAdvertiseStop + cmdScanningStart + cmdScanningStop + cmdServicesAdd + cmdServicesRemove + cmdSendData + cmdSubscribed + cmdConnect + cmdDisconnect + cmdReadRSSI + cmdDiscoverServices + cmdDiscoverIncludedServices + cmdDiscoverCharacteristics + cmdReadCharacteristic + cmdWriteCharacteristic + cmdSubscribeCharacteristic + cmdDiscoverDescriptors + cmdReadDescriptor + cmdWriteDescriptor + evtStateChanged + evtAdvertisingStarted + evtAdvertisingStopped + evtServiceAdded + evtReadRequest + evtWriteRequest + evtSubscribe + evtUnsubscribe + evtConfirmation + evtPeripheralDiscovered + evtPeripheralConnected + evtPeripheralDisconnected + evtATTMTU + evtRSSIRead + evtServiceDiscovered + evtIncludedServicesDiscovered + evtCharacteristicsDiscovered + evtCharacteristicRead + evtCharacteristicWritten + evtNotificationValueSet + evtDescriptorsDiscovered + evtDescriptorRead + evtDescriptorWritten + evtSlaveConnectionComplete + evtMasterConnectionComplete +) + +// XpcIDs is the map of the commands for the current version of OS X +var xpcID map[int]int + +func initXpcIDs() { + darwinOSVersion = getDarwinReleaseVersion() + + xpcID := make(map[int]int) + + xpcID[cmdInit] = 1 + xpcID[cmdAdvertiseStart] = 8 + xpcID[cmdAdvertiseStop] = 9 + xpcID[cmdServicesAdd] = 10 + xpcID[cmdServicesRemove] = 12 + + if darwinOSVersion < 17 { + // yosemite + xpcID[cmdSendData] = 13 + xpcID[cmdSubscribed] = 15 + xpcID[cmdScanningStart] = 29 + xpcID[cmdScanningStop] = 30 + xpcID[cmdConnect] = 31 + xpcID[cmdDisconnect] = 32 + xpcID[cmdReadRSSI] = 44 + xpcID[cmdDiscoverServices] = 45 + xpcID[cmdDiscoverIncludedServices] = 60 + xpcID[cmdDiscoverCharacteristics] = 62 + xpcID[cmdReadCharacteristic] = 65 + xpcID[cmdWriteCharacteristic] = 66 + xpcID[cmdSubscribeCharacteristic] = 68 + xpcID[cmdDiscoverDescriptors] = 70 + xpcID[cmdReadDescriptor] = 77 + xpcID[cmdWriteDescriptor] = 78 + + xpcID[evtStateChanged] = 4 + xpcID[evtAdvertisingStarted] = 16 + xpcID[evtAdvertisingStopped] = 17 + xpcID[evtServiceAdded] = 18 + xpcID[evtReadRequest] = 19 + xpcID[evtWriteRequest] = 20 + xpcID[evtSubscribe] = 21 + xpcID[evtUnsubscribe] = 22 + xpcID[evtConfirmation] = 23 + xpcID[evtPeripheralDiscovered] = 37 + xpcID[evtPeripheralConnected] = 38 + xpcID[evtPeripheralDisconnected] = 40 + xpcID[evtATTMTU] = 53 + xpcID[evtRSSIRead] = 55 + xpcID[evtServiceDiscovered] = 56 + xpcID[evtIncludedServicesDiscovered] = 63 + xpcID[evtCharacteristicsDiscovered] = 64 + xpcID[evtCharacteristicRead] = 71 + xpcID[evtCharacteristicWritten] = 72 + xpcID[evtNotificationValueSet] = 74 + xpcID[evtDescriptorsDiscovered] = 76 + xpcID[evtDescriptorRead] = 79 + xpcID[evtDescriptorWritten] = 80 + xpcID[evtSlaveConnectionComplete] = 81 + xpcID[evtMasterConnectionComplete] = 21 + } else { + // high sierra + xpcID[cmdSendData] = 13 // TODO: find out the correct value for this + xpcID[cmdScanningStart] = 44 + xpcID[cmdScanningStop] = 45 + xpcID[cmdConnect] = 46 + xpcID[cmdDisconnect] = 47 + xpcID[cmdReadRSSI] = 61 + xpcID[cmdDiscoverServices] = 62 + xpcID[cmdDiscoverIncludedServices] = 74 + xpcID[cmdDiscoverCharacteristics] = 75 + xpcID[cmdReadCharacteristic] = 78 + xpcID[cmdWriteCharacteristic] = 79 + xpcID[cmdSubscribeCharacteristic] = 81 + xpcID[cmdDiscoverDescriptors] = 82 + xpcID[cmdReadDescriptor] = 88 + xpcID[cmdWriteDescriptor] = 89 + + xpcID[evtPeripheralDiscovered] = 48 + xpcID[evtPeripheralConnected] = 49 + xpcID[evtPeripheralDisconnected] = 50 + xpcID[evtRSSIRead] = 71 + xpcID[evtServiceDiscovered] = 72 + xpcID[evtCharacteristicsDiscovered] = 77 + xpcID[evtCharacteristicRead] = 83 + xpcID[evtCharacteristicWritten] = 84 + xpcID[evtNotificationValueSet] = 86 + xpcID[evtDescriptorsDiscovered] = 87 + xpcID[evtDescriptorRead] = 90 + xpcID[evtDescriptorWritten] = 91 + + xpcID[evtIncludedServicesDiscovered] = 87 + } +} From 99ada741715204572cd2cc95f96408676d2313bb Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 14:21:58 +0200 Subject: [PATCH 2/8] Correct key error Signed-off-by: deadprogram --- darwin/device.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/device.go b/darwin/device.go index 69a3d43..285e197 100644 --- a/darwin/device.go +++ b/darwin/device.go @@ -437,7 +437,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) { // characteristic is subscribed by remote central. d.conn(args).subscribed(d.chars[args.attributeID()]) - case xpcID[evtUnubscribe]: + case xpcID[evtUnsubscribe]: // characteristic is unsubscribed by remote central. d.conn(args).unsubscribed(d.chars[args.attributeID()]) From fafd81e109c06a7838eaaf097aab22c36b549831 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 14:25:53 +0200 Subject: [PATCH 3/8] Corrections to function for getting OS X version Signed-off-by: deadprogram --- darwin/xpcid.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/darwin/xpcid.go b/darwin/xpcid.go index 59812be..daf0421 100644 --- a/darwin/xpcid.go +++ b/darwin/xpcid.go @@ -16,13 +16,15 @@ const ( var darwinOSVersion int func getDarwinReleaseVersion() int { - version, err := exec.Command("uname", "-r").Output() + v, err := exec.Command("uname", "-r").Output() if err != nil { fmt.Println(err) return 0 } - v, _ := strconv.Atoi(strings.Split(version, ".")[0]) - return v + var version string + version = v + result, _ := strconv.Atoi(strings.Split(version, ".")[0]) + return result } // xpc command IDs are OS X version specific, so we will use a map From 93d021f141295286f1f3ea928eb1d34def834f2f Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 14:41:50 +0200 Subject: [PATCH 4/8] Correct state change ID for Yosemite Signed-off-by: deadprogram --- darwin/xpcid.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/darwin/xpcid.go b/darwin/xpcid.go index daf0421..897cfa9 100644 --- a/darwin/xpcid.go +++ b/darwin/xpcid.go @@ -7,12 +7,6 @@ import ( "strings" ) -const ( - evtDescriptorWritten = 80 - evtSlaveConnectionComplete = 81 - evtMasterConnectionComplete = 82 -) - var darwinOSVersion int func getDarwinReleaseVersion() int { @@ -21,9 +15,8 @@ func getDarwinReleaseVersion() int { fmt.Println(err) return 0 } - var version string - version = v - result, _ := strconv.Atoi(strings.Split(version, ".")[0]) + + result, _ := strconv.Atoi(strings.Split(string(v), ".")[0]) return result } @@ -111,7 +104,7 @@ func initXpcIDs() { xpcID[cmdReadDescriptor] = 77 xpcID[cmdWriteDescriptor] = 78 - xpcID[evtStateChanged] = 4 + xpcID[evtStateChanged] = 6 xpcID[evtAdvertisingStarted] = 16 xpcID[evtAdvertisingStopped] = 17 xpcID[evtServiceAdded] = 18 From 79129bc3d8a0f3b286c30ab3f715c48b04d5c205 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 16:17:00 +0200 Subject: [PATCH 5/8] Use xpc function to retrieve version Signed-off-by: deadprogram --- darwin/device.go | 5 ++++- darwin/xpcid.go | 24 +++++------------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/darwin/device.go b/darwin/device.go index 285e197..af83b6f 100644 --- a/darwin/device.go +++ b/darwin/device.go @@ -50,7 +50,10 @@ func NewDevice(opts ...Option) (*Device, error) { } initXpcIDs() - if darwinOSVersion < 17 { + var utsname xpc.Utsname + xpc.Uname(&utsname) + + if utsname.Release < "17." { // yosemite d.pm = xpc.XpcConnect("com.apple.blued", d) d.cm = xpc.XpcConnect("com.apple.blued", d) diff --git a/darwin/xpcid.go b/darwin/xpcid.go index 897cfa9..27b499f 100644 --- a/darwin/xpcid.go +++ b/darwin/xpcid.go @@ -1,25 +1,9 @@ package darwin import ( - "fmt" - "os/exec" - "strconv" - "strings" + "github.com/raff/goble/xpc" ) -var darwinOSVersion int - -func getDarwinReleaseVersion() int { - v, err := exec.Command("uname", "-r").Output() - if err != nil { - fmt.Println(err) - return 0 - } - - result, _ := strconv.Atoi(strings.Split(string(v), ".")[0]) - return result -} - // xpc command IDs are OS X version specific, so we will use a map // to be able to handle arbitrary versions const ( @@ -75,7 +59,8 @@ const ( var xpcID map[int]int func initXpcIDs() { - darwinOSVersion = getDarwinReleaseVersion() + var utsname xpc.Utsname + xpc.Uname(&utsname) xpcID := make(map[int]int) @@ -85,7 +70,7 @@ func initXpcIDs() { xpcID[cmdServicesAdd] = 10 xpcID[cmdServicesRemove] = 12 - if darwinOSVersion < 17 { + if utsname.Release < "17." { // yosemite xpcID[cmdSendData] = 13 xpcID[cmdSubscribed] = 15 @@ -147,6 +132,7 @@ func initXpcIDs() { xpcID[cmdReadDescriptor] = 88 xpcID[cmdWriteDescriptor] = 89 + xpcID[evtStateChanged] = 4 xpcID[evtPeripheralDiscovered] = 48 xpcID[evtPeripheralConnected] = 49 xpcID[evtPeripheralDisconnected] = 50 From cfb84dbbdc98026c0b6bef100c827f27ac926022 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 16:24:03 +0200 Subject: [PATCH 6/8] Use correctc xpc id for master connection complete Signed-off-by: deadprogram --- darwin/xpcid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/xpcid.go b/darwin/xpcid.go index 27b499f..51aa64f 100644 --- a/darwin/xpcid.go +++ b/darwin/xpcid.go @@ -113,7 +113,7 @@ func initXpcIDs() { xpcID[evtDescriptorRead] = 79 xpcID[evtDescriptorWritten] = 80 xpcID[evtSlaveConnectionComplete] = 81 - xpcID[evtMasterConnectionComplete] = 21 + xpcID[evtMasterConnectionComplete] = 82 } else { // high sierra xpcID[cmdSendData] = 13 // TODO: find out the correct value for this From b2e7d481c12c9e736e81095b3096e3ac64364836 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 16:49:32 +0200 Subject: [PATCH 7/8] Need to populate the correct map Signed-off-by: deadprogram --- darwin/device.go | 8 ++++---- darwin/xpcid.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/darwin/device.go b/darwin/device.go index af83b6f..ef1504b 100644 --- a/darwin/device.go +++ b/darwin/device.go @@ -38,6 +38,10 @@ type Device struct { // NewDevice returns a BLE device. func NewDevice(opts ...Option) (*Device, error) { + initXpcIDs() + var utsname xpc.Utsname + xpc.Uname(&utsname) + d := &Device{ rspc: make(chan msg), conns: make(map[string]*conn), @@ -49,10 +53,6 @@ func NewDevice(opts ...Option) (*Device, error) { return nil, err } - initXpcIDs() - var utsname xpc.Utsname - xpc.Uname(&utsname) - if utsname.Release < "17." { // yosemite d.pm = xpc.XpcConnect("com.apple.blued", d) diff --git a/darwin/xpcid.go b/darwin/xpcid.go index 51aa64f..8cb5f0f 100644 --- a/darwin/xpcid.go +++ b/darwin/xpcid.go @@ -62,7 +62,7 @@ func initXpcIDs() { var utsname xpc.Utsname xpc.Uname(&utsname) - xpcID := make(map[int]int) + xpcID = make(map[int]int) xpcID[cmdInit] = 1 xpcID[cmdAdvertiseStart] = 8 From bb999a2ce6f58c4027820f08d646cdedd452c8cf Mon Sep 17 00:00:00 2001 From: deadprogram Date: Sat, 28 Oct 2017 17:48:05 +0200 Subject: [PATCH 8/8] Mark which XPC ids still might need to be identified for High Sierra Signed-off-by: deadprogram --- darwin/xpcid.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/darwin/xpcid.go b/darwin/xpcid.go index 8cb5f0f..4ca59d8 100644 --- a/darwin/xpcid.go +++ b/darwin/xpcid.go @@ -146,6 +146,19 @@ func initXpcIDs() { xpcID[evtDescriptorRead] = 90 xpcID[evtDescriptorWritten] = 91 + // TODO: find out the correct values for the following items. + // They have all just been copied from Yosemite values + xpcID[evtAdvertisingStarted] = 16 + xpcID[evtAdvertisingStopped] = 17 + xpcID[evtServiceAdded] = 18 + xpcID[evtReadRequest] = 19 + xpcID[evtWriteRequest] = 20 + xpcID[evtSubscribe] = 21 + xpcID[evtUnsubscribe] = 22 + xpcID[evtConfirmation] = 23 + xpcID[evtATTMTU] = 53 + xpcID[evtSlaveConnectionComplete] = 81 + xpcID[evtMasterConnectionComplete] = 82 xpcID[evtIncludedServicesDiscovered] = 87 } }