Unverified Commit c88cfeb1 authored by Kevin's avatar Kevin Committed by GitHub
Browse files

Cleanup XPC and add some errors (#4)

* XPC cleanup

* Go Meta Linter

* Update travis with linux

* Fix unintroduced interface

* Fix travis.yml

* Fix travis.yml

* Fix travis.yml

* Add GOOS to Travis

* Damn travis

* Finally?

* Goddamn travis
parent 78821469
language: go
os:
- osx
- linux
go:
- 1.7
......@@ -10,8 +11,15 @@ go:
go_import_path: github.com/go-ble/ble
test:
- go vet ./...
- go test -v ./...
- GOOS=darwin go build -v ./...
- GOOS=linux go build -v ./...
install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then go get ./...; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then GOOS=linux go get && GOOS=linux go get ./linux; fi
script:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then go vet ./...; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then GOOS=linux go vet && GOOS=linux go vet ./linux/...; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then go test ./...; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then GOOS=linux go test && GOOS=linux go test ./linux/...; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then go build -v ./...; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then GOOS=linux go build -v && GOOS=linux go build -v ./linux/...; fi
......@@ -69,5 +69,5 @@ func (a *adv) RSSI() int {
}
func (a *adv) Addr() ble.Addr {
return xpc.UUID(a.args.MustGetUUID("kCBMsgArgDeviceUUID"))
return a.args.MustGetUUID("kCBMsgArgDeviceUUID")
}
......@@ -68,12 +68,15 @@ 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(xpcID[cmdDiscoverServices], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdDiscoverServices, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgUUIDs": uuidSlice(ss),
})
if rsp.err() != nil {
return nil, rsp.err()
if err != nil {
return nil, err
}
if err := rsp.err(); err != nil {
return nil, err
}
svcs := []*ble.Service{}
for _, xss := range rsp.services() {
......@@ -93,14 +96,17 @@ 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(xpcID[cmdDiscoverIncludedServices], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdDiscoverIncludedServices, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgServiceStartHandle": s.Handle,
"kCBMsgArgServiceEndHandle": s.EndHandle,
"kCBMsgArgUUIDs": uuidSlice(ss),
})
if rsp.err() != nil {
return nil, rsp.err()
if err != nil {
return nil, err
}
if err := rsp.err(); err != nil {
return nil, err
}
return nil, ble.ErrNotImplemented
}
......@@ -108,14 +114,17 @@ 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(xpcID[cmdDiscoverCharacteristics], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdDiscoverCharacteristics, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgServiceStartHandle": s.Handle,
"kCBMsgArgServiceEndHandle": s.EndHandle,
"kCBMsgArgUUIDs": uuidSlice(cs),
})
if rsp.err() != nil {
return nil, rsp.err()
if err != nil {
return nil, err
}
if err := rsp.err(); err != nil {
return nil, err
}
for _, xcs := range rsp.characteristics() {
xc := msg(xcs.(xpc.Dict))
......@@ -132,12 +141,18 @@ 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(xpcID[cmdDiscoverDescriptors], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdDiscoverDescriptors, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgCharacteristicHandle": c.Handle,
"kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
"kCBMsgArgUUIDs": uuidSlice(ds),
})
if err != nil {
return nil, err
}
if err := rsp.err(); err != nil {
return nil, err
}
for _, xds := range rsp.descriptors() {
xd := msg(xds.(xpc.Dict))
c.Descriptors = append(c.Descriptors, &ble.Descriptor{
......@@ -150,11 +165,14 @@ 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(xpcID[cmdReadCharacteristic], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdReadCharacteristic, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgCharacteristicHandle": c.Handle,
"kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
})
if err != nil {
return nil, err
}
if rsp.err() != nil {
return nil, rsp.err()
}
......@@ -176,37 +194,49 @@ 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(xpcID[cmdWriteCharacteristic], args)
return nil
return cln.conn.sendCmd(cmdWriteCharacteristic, args)
}
m, err := cln.conn.sendReq(cmdWriteCharacteristic, args)
if err != nil {
return err
}
return cln.conn.sendReq(xpcID[cmdWriteCharacteristic], args).err()
return m.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(xpcID[cmdReadDescriptor], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdReadDescriptor, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgDescriptorHandle": d.Handle,
})
if rsp.err() != nil {
return nil, rsp.err()
if err != nil {
return nil, err
}
if err := rsp.err(); err != nil {
return nil, err
}
return rsp.data(), nil
}
// 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(xpcID[cmdWriteDescriptor], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdWriteDescriptor, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgDescriptorHandle": d.Handle,
"kCBMsgArgData": b,
})
if err != nil {
return err
}
return rsp.err()
}
// 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(xpcID[cmdReadRSSI], xpc.Dict{"kCBMsgArgDeviceUUID": cln.id})
rsp, err := cln.conn.sendReq(cmdReadRSSI, xpc.Dict{"kCBMsgArgDeviceUUID": cln.id})
if err != nil {
return 0
}
if rsp.err() != nil {
return 0
}
......@@ -226,15 +256,19 @@ 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(xpcID[cmdSubscribeCharacteristic], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdSubscribeCharacteristic, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgCharacteristicHandle": c.Handle,
"kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
"kCBMsgArgState": 1,
})
if rsp.err() != nil {
if err != nil {
delete(cln.conn.subs, c.Handle)
return err
}
if err := rsp.err(); err != nil {
delete(cln.conn.subs, c.Handle)
return rsp.err()
return err
}
return nil
}
......@@ -242,14 +276,17 @@ 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(xpcID[cmdSubscribeCharacteristic], xpc.Dict{
rsp, err := cln.conn.sendReq(cmdSubscribeCharacteristic, xpc.Dict{
"kCBMsgArgDeviceUUID": cln.id,
"kCBMsgArgCharacteristicHandle": c.Handle,
"kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
"kCBMsgArgState": 0,
})
if rsp.err() != nil {
return rsp.err()
if err != nil {
return err
}
if err := rsp.err(); err != nil {
return err
}
cln.conn.Lock()
defer cln.conn.Unlock()
......@@ -260,14 +297,19 @@ func (cln *Client) Unsubscribe(c *ble.Characteristic, ind bool) error {
// ClearSubscriptions clears all subscriptions to notifications and indications.
func (cln *Client) ClearSubscriptions() error {
for _, s := range cln.conn.subs {
cln.Unsubscribe(s.char, false)
if err := cln.Unsubscribe(s.char, false); err != nil {
return err
}
}
return nil
}
// CancelConnection disconnects the connection.
func (cln *Client) CancelConnection() error {
rsp := cln.conn.sendReq(xpcID[cmdDisconnect], xpc.Dict{"kCBMsgArgDeviceUUID": cln.id})
rsp, err := cln.conn.sendReq(cmdDisconnect, xpc.Dict{"kCBMsgArgDeviceUUID": cln.id})
if err != nil {
return err
}
return rsp.err()
}
......
......@@ -2,6 +2,7 @@ package darwin
import (
"context"
"log"
"sync"
"github.com/go-ble/ble"
......@@ -27,7 +28,6 @@ type conn struct {
sync.RWMutex
dev *Device
role int
ctx context.Context
rxMTU int
txMTU int
......@@ -102,12 +102,12 @@ func (c *conn) subscribed(char *ble.Characteristic) {
return
}
send := func(b []byte) (int, error) {
c.dev.sendCmd(c.dev.pm, xpcID[cmdSubscribed], xpc.Dict{
err := c.dev.sendCmd(c.dev.pm, cmdSubscribed, xpc.Dict{
"kCBMsgArgUUIDs": [][]byte{},
"kCBMsgArgAttributeID": h,
"kCBMsgArgData": b,
})
return len(b), nil
return len(b), err
}
n := ble.NewNotifier(send)
c.notifiers[h] = n
......@@ -118,17 +118,22 @@ func (c *conn) subscribed(char *ble.Characteristic) {
// server (peripheral)
func (c *conn) unsubscribed(char *ble.Characteristic) {
if n, found := c.notifiers[char.Handle]; found {
n.Close()
if err := n.Close(); err != nil {
log.Printf("failed to clone notifier: %v", err)
}
delete(c.notifiers, char.Handle)
}
}
func (c *conn) sendReq(id int, args xpc.Dict) msg {
c.dev.sendCmd(c.dev.cm, id, args)
func (c *conn) sendReq(id int, args xpc.Dict) (msg, error) {
err := c.dev.sendCmd(c.dev.cm, id, args)
if err != nil {
return msg{}, err
}
m := <-c.rspc
return msg(m.args())
return msg(m.args()), nil
}
func (c *conn) sendCmd(id int, args xpc.Dict) {
c.dev.sendCmd(c.dev.pm, id, args)
func (c *conn) sendCmd(id int, args xpc.Dict) error {
return c.dev.sendCmd(c.dev.pm, id, args)
}
......@@ -38,9 +38,10 @@ type Device struct {
// NewDevice returns a BLE device.
func NewDevice(opts ...Option) (*Device, error) {
initXpcIDs()
var utsname xpc.Utsname
xpc.Uname(&utsname)
err := initXpcIDs()
if err != nil {
return nil, err
}
d := &Device{
rspc: make(chan msg),
......@@ -53,15 +54,8 @@ func NewDevice(opts ...Option) (*Device, error) {
return nil, err
}
if utsname.Release < "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)
}
d.pm = xpc.XpcConnect(serviceID, d)
d.cm = xpc.XpcConnect(serviceID, d)
return d, errors.Wrap(d.Init(), "can't init")
}
......@@ -77,25 +71,31 @@ func (d *Device) Option(opts ...Option) error {
// Init ...
func (d *Device) Init() error {
rsp := d.sendReq(d.cm, xpcID[cmdInit], xpc.Dict{
rsp, err := d.sendReq(d.cm, cmdInit, xpc.Dict{
"kCBMsgArgName": fmt.Sprintf("gopher-%v", time.Now().Unix()),
"kCBMsgArgOptions": xpc.Dict{
"kCBInitOptionShowPowerAlert": 1,
},
"kCBMsgArgType": 0,
})
if err != nil {
return err
}
s := State(rsp.state())
if s != StatePoweredOn {
return fmt.Errorf("state: %s", s)
}
rsp = d.sendReq(d.pm, xpcID[cmdInit], xpc.Dict{
rsp, err = d.sendReq(d.pm, cmdInit, xpc.Dict{
"kCBMsgArgName": fmt.Sprintf("gopher-%v", time.Now().Unix()),
"kCBMsgArgOptions": xpc.Dict{
"kCBInitOptionShowPowerAlert": 1,
},
"kCBMsgArgType": 1,
})
if err != nil {
return err
}
s = State(rsp.state())
if s != StatePoweredOn {
return fmt.Errorf("state: %s", s)
......@@ -105,15 +105,19 @@ 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, xpcID[cmdAdvertiseStart], xpc.Dict{
rsp, err := d.sendReq(d.pm, cmdAdvertiseStart, xpc.Dict{
"kCBAdvDataLocalName": adv.LocalName(),
"kCBAdvDataServiceUUIDs": adv.Services(),
"kCBAdvDataAppleMfgData": adv.ManufacturerData(),
}).err(); err != nil {
})
if err != nil {
return err
}
if err := rsp.err(); err != nil {
return err
}
<-ctx.Done()
d.stopAdvertising()
_ = d.stopAdvertising()
return ctx.Err()
}
......@@ -122,9 +126,13 @@ 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, xpcID[cmdAdvertiseStart], xpc.Dict{
rsp, err := d.sendReq(d.pm, cmdAdvertiseStart, xpc.Dict{
"kCBAdvDataAppleMfgData": append(b, md...),
}).err(); err != nil {
})
if err != nil {
return err
}
if err := rsp.err(); err != nil {
return errors.Wrap(err, "can't advertise")
}
<-ctx.Done()
......@@ -138,9 +146,13 @@ 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, xpcID[cmdAdvertiseStart], xpc.Dict{
rsp, err := d.sendReq(d.pm, cmdAdvertiseStart, xpc.Dict{
"kCBAdvDataAppleMfgData": append(prefix, b...),
}).err(); err != nil {
})
if err != nil {
return err
}
if err := rsp.err(); err != nil {
return errors.Wrap(err, "can't advertise")
}
<-ctx.Done()
......@@ -149,27 +161,38 @@ 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, xpcID[cmdAdvertiseStart], xpc.Dict{
rsp, err := d.sendReq(d.pm, cmdAdvertiseStart, xpc.Dict{
"kCBAdvDataLocalName": name,
"kCBAdvDataServiceUUIDs": uuidSlice(ss)},
).err(); err != nil {
)
if err != nil {
return err
}
if err := rsp.err(); err != nil {
return err
}
<-ctx.Done()
d.stopAdvertising()
_ = d.stopAdvertising()
return ctx.Err()
}
// AdvertiseIBeaconData advertises iBeacon packet with specified manufacturer data.
func (d *Device) AdvertiseIBeaconData(ctx context.Context, md []byte) error {
var utsname xpc.Utsname
xpc.Uname(&utsname)
err := xpc.Uname(&utsname)
if err != nil {
return err
}
if utsname.Release >= "14." {
ibeaconCode := []byte{0x02, 0x15}
return d.AdvertiseMfgData(ctx, 0x004C, append(ibeaconCode, md...))
}
if err := d.sendReq(d.pm, xpcID[cmdAdvertiseStart], xpc.Dict{"kCBAdvDataAppleBeaconKey": md}).err(); err != nil {
rsp, err := d.sendReq(d.pm, cmdAdvertiseStart, xpc.Dict{"kCBAdvDataAppleBeaconKey": md})
if err != nil {
return err
}
if err := rsp.err(); err != nil {
return err
}
<-ctx.Done()
......@@ -188,13 +211,20 @@ 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, xpcID[cmdAdvertiseStop], nil).err(), "can't stop advertising")
rsp, err := d.sendReq(d.pm, cmdAdvertiseStop, nil)
if err != nil {
return errors.Wrap(err, "can't send stop advertising")
}
if err := rsp.err(); err != nil {
return errors.Wrap(err, "can't stop advertising")
}
return nil
}
// Scan ...
func (d *Device) Scan(ctx context.Context, allowDup bool, h ble.AdvHandler) error {
d.advHandler = h
if err := d.sendCmd(d.cm, xpcID[cmdScanningStart], xpc.Dict{
if err := d.sendCmd(d.cm, cmdScanningStart, xpc.Dict{
// "kCBMsgArgUUIDs": uuidSlice(ss),
"kCBMsgArgOptions": xpc.Dict{
"kCBScanOptionAllowDuplicates": map[bool]int{true: 1, false: 0}[allowDup],
......@@ -211,12 +241,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, xpcID[cmdScanningStop], nil), "can't stop scanning")
return errors.Wrap(d.sendCmd(d.cm, cmdScanningStop, nil), "can't stop scanning")
}
// RemoveAllServices removes all services of device's
func (d *Device) RemoveAllServices() error {
return d.sendCmd(d.pm, xpcID[cmdServicesRemove], nil)
return d.sendCmd(d.pm, cmdServicesRemove, nil)
}
// AddService adds a service to device's database.
......@@ -317,7 +347,11 @@ func (d *Device) AddService(s *ble.Service) error {
}
xs["kCBMsgArgCharacteristics"] = xcs
return d.sendReq(d.pm, xpcID[cmdServicesAdd], xs).err()
rsp, err := d.sendReq(d.pm, cmdServicesAdd, xs)
if err != nil {
return err
}
return rsp.err()
}
// SetServices ...
......@@ -335,12 +369,15 @@ 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, xpcID[cmdConnect], xpc.Dict{
err := d.sendCmd(d.cm, cmdConnect, xpc.Dict{
"kCBMsgArgDeviceUUID": xpc.MakeUUID(a.String()),
"kCBMsgArgOptions": xpc.Dict{
"kCBConnectOptionNotifyOnDisconnection": 1,
},
})
if err != nil {
return nil, err
}
select {
case <-ctx.Done():
return nil, ctx.Err()
......@@ -367,29 +404,29 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
switch m.id() {
case // Device event
xpcID[evtStateChanged],
xpcID[evtAdvertisingStarted],
xpcID[evtAdvertisingStopped],
xpcID[evtServiceAdded]:
evtStateChanged,
evtAdvertisingStarted,
evtAdvertisingStopped,
evtServiceAdded:
d.rspc <- args
case xpcID[evtPeripheralDiscovered]:
case evtPeripheralDiscovered:
if d.advHandler == nil {
break
}
a := &adv{args: m.args(), ad: args.advertisementData()}
go d.advHandler(a)
case xpcID[evtConfirmation]:
case evtConfirmation:
// log.Printf("confirmed: %d", args.attributeID())
case xpcID[evtATTMTU]:
case evtATTMTU:
d.conn(args).SetTxMTU(args.attMTU())
case xpcID[evtSlaveConnectionComplete]:
case evtSlaveConnectionComplete:
// remote peripheral is connected.
fallthrough
case xpcID[evtMasterConnectionComplete]:
case evtMasterConnectionComplete:
// remote central is connected.
// Could be LEConnectionComplete or LEConnectionUpdateComplete.
......@@ -398,7 +435,7 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
c.connLatency = args.connectionLatency()
c.supervisionTimeout = args.supervisionTimeout()
case xpcID[evtReadRequest]:
case evtReadRequest:
aid := args.attributeID()
char := d.chars[aid]
v := char.Value
......@@ -411,14 +448,17 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
v = buf.Bytes()
}
d.sendCmd(d.pm, xpcID[cmdSendData], xpc.Dict{
err := d.sendCmd(d.pm, cmdSendData, xpc.Dict{
"kCBMsgArgAttributeID": aid,
"kCBMsgArgData": v,
"kCBMsgArgTransactionID": args.transactionID(),
"kCBMsgArgResult": 0,
})
case xpcID[evtWriteRequest]:
if err != nil {
log.Printf("error: %v", err)
return
}
case evtWriteRequest:
for _, xxw := range args.attWrites() {
xw := msg(xxw.(xpc.Dict))
aid := xw.attributeID()
......@@ -428,26 +468,30 @@ func (d *Device) HandleXpcEvent(event xpc.Dict, err error) {
if xw.ignoreResponse() == 1 {
continue
}
d.sendCmd(d.pm, xpcID[cmdSendData], xpc.Dict{