Skip to content

Commit

Permalink
Merge pull request #69 from rigado/test/hciDebug
Browse files Browse the repository at this point in the history
Set suggested max TX data length
  • Loading branch information
estutzenberger authored Nov 2, 2022
2 parents 14d525c + 936a973 commit a1fdaee
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 40 deletions.
11 changes: 7 additions & 4 deletions examples/basic/advertiser/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,26 @@ import (
"context"
"flag"
"fmt"
"github.com/rigado/ble/linux"
"log"
"time"

"github.com/rigado/ble"
"github.com/rigado/ble/examples/lib/dev"
"github.com/pkg/errors"
"github.com/rigado/ble"
)

var (
device = flag.String("device", "default", "implementation of ble")
du = flag.Duration("du", 5*time.Second, "advertising duration, 0 for indefinitely")
name = flag.String("name", "Cascade", "name of the peripheral device")
)

func main() {
flag.Parse()

d, err := dev.NewDevice("default")
opt := ble.OptTransportHCISocket(0)

d, err := linux.NewDeviceWithNameAndHandler("", nil, opt)
if err != nil {
log.Fatalf("can't new device : %s", err)
}
Expand All @@ -29,7 +32,7 @@ func main() {
// Advertise for specified durantion, or until interrupted by user.
fmt.Printf("Advertising for %s...\n", *du)
ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), *du))
chkErr(ble.AdvertiseNameAndServices(ctx, "Gopher"))
chkErr(ble.AdvertiseNameAndServices(ctx, *name, ble.BatteryUUID, ble.DeviceInfoUUID))
}

func chkErr(err error) {
Expand Down
12 changes: 10 additions & 2 deletions linux/adv/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package adv

import (
"encoding/binary"
"fmt"

"github.com/pkg/errors"
"errors"
"github.com/rigado/ble"
"github.com/rigado/ble/parser"
)
Expand Down Expand Up @@ -68,7 +69,14 @@ func NewRawPacket(bytes ...[]byte) (*Packet, error) {

//decode the bytes
m, err := parser.Parse(b)
err = errors.Wrapf(err, "pdu decode")
if err != nil {
if !errors.Is(err, parser.EmptyOrNilPdu) {
err = fmt.Errorf("pdu decode: %w", err)
} else {
err = nil
}
}

switch {
case err == nil:
// ok
Expand Down
47 changes: 24 additions & 23 deletions linux/att/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package att
import (
"encoding/binary"
"errors"

"fmt"
"io"
"time"
Expand Down Expand Up @@ -60,7 +61,7 @@ func (c *Client) WithServer(db *DB) *Client {
var err error
c.server, err = NewServer(db, c.l2c, c.Logger)
if err != nil {
c.Errorf("client: failed to create server")
c.Errorf("failed to create server")
}

return c
Expand Down Expand Up @@ -511,7 +512,7 @@ func (c *Client) sendCmd(b []byte) error {
}

func (c *Client) sendReq(b []byte) (rsp []byte, err error) {
c.Debugf("client: req % X", b)
c.Debugf("req: %x", b)
if _, err := c.l2c.Write(b); err != nil {
return nil, fmt.Errorf("send ATT request failed: %w", err)
}
Expand All @@ -526,7 +527,7 @@ func (c *Client) sendReq(b []byte) (rsp []byte, err error) {
// returns an ErrReqNotSupp response, and continue to wait
// the response to our request.
errRsp := newErrorResponse(rsp[0], 0x0000, ble.ErrReqNotSupp)
c.Debugf("client: rsp % X", b)
c.Debugf("rsp: %x", b)
_, err := c.l2c.Write(errRsp)
if err != nil {
return nil, fmt.Errorf("unexpected ATT response received: %w", err)
Expand Down Expand Up @@ -559,14 +560,14 @@ func (c *Client) asyncReqLoop() {
// keep trying?
select {
case <-c.done:
c.Debug("[BLE ATT]: exited client async loop: done")
c.Debug("exited async loop: done")
return
case <-c.connClosed:
c.Debug("[BLE ATT]: exited client async loop: conn closed")
c.Debug("exited async loop: conn closed")
return
default:
if c.l2c == nil {
c.Debug("[BLE ATT] exited client async loop: l2c nil")
c.Debug("exited async loop: l2c nil")
return
}
//ok
Expand All @@ -579,7 +580,7 @@ func (c *Client) asyncReqLoop() {
}
err := c.sendResp(rsp)
if err != nil {
c.Errorf("client: failed to send async att response for: %X", in[0])
c.Errorf("failed to send async att response for: %X", in[0])
}
}
}
Expand Down Expand Up @@ -613,14 +614,14 @@ func (c *Client) Loop() {
// keep trying?
select {
case <-c.done:
c.Debug("exited client loop: done")
c.Debug("exited async loop: done")
return
case <-c.connClosed:
c.Debug("exited client async loop: conn closed")
c.Debug("exited async loop: conn closed")
return
default:
if c.l2c == nil {
c.Debug("exited client loop: l2c nil")
c.Debug("exited async loop: l2c nil")
return
}
//ok
Expand All @@ -630,14 +631,14 @@ func (c *Client) Loop() {
// keep trying?
select {
case <-c.done:
c.Debug("exited client loop: done")
c.Debug("exited async loop: done")
return
case <-c.connClosed:
c.Debug("exited client async loop: conn closed")
c.Debug("exited async loop: conn closed")
return
default:
if c.l2c == nil {
c.Debug("exited client loop: l2c nil")
c.Debug("exited async loop: l2c nil")
return
} else if err != nil {
if errors.Is(err, io.ErrClosedPipe) {
Expand All @@ -657,28 +658,28 @@ func (c *Client) Loop() {

b := make([]byte, n)
copy(b, c.rxBuf)
c.Debugf("client: data rx % X", b)
c.Debugf("rx: %x", b)

//all incoming requests are even numbered
//which means the last bit should be 0
if b[0]&0x01 == 0x00 {
select {
case <-c.done:
c.Info("exited client loop: closed after async req rx")
c.Info("exited async loop: closed after async req rx")
return
case <-c.connClosed:
c.Debug("exited client async loop: conn closed")
c.Debug("exited async loop: conn closed")
return
case c.inc <- b:
continue
default:
c.Errorf("client: failed to enqueue request for", fmt.Sprintf("%x", b[0]))
c.Errorf("failed to enqueue request for %x", b[0])
continue
}
}

if (b[0] != HandleValueNotificationCode) && (b[0] != HandleValueIndicationCode) {
c.Debugf("client: rsp % X", c.rxBuf[:n])
c.Debugf("a rx: %x", c.rxBuf[:n])
select {
case <-c.done:
c.Info("exited client loop: closed after rsp rx")
Expand All @@ -692,24 +693,24 @@ func (c *Client) Loop() {
}

// Deliver the full request to upper layer.
c.Debugf("client: notif % X", b)
c.Debugf("notif: %x", b)
select {
case <-c.done:
c.Info("exited client loop: closed after rx")
c.Info("exited async loop: closed after rx")
return
case <-c.connClosed:
c.Debug("exited client async loop: conn closed")
c.Debug("exited async loop: conn closed")
return
case ch <- asyncWork{handle: c.handler.HandleNotification, data: b}:
// ok
default:
// If this really happens, especially on a slow machine, enlarge the channel buffer.
c.Error("client: req - can't enqueue incoming notification.")
c.Error("can't enqueue incoming notification.")
}

// Always write aknowledgement for an indication, even it was an invalid request.
if b[0] == HandleValueIndicationCode {
c.Debugf("client: req % X", b)
c.Debugf("write confirmation for indication")
_, _ = c.l2c.Write(confirmation)
}
}
Expand Down
9 changes: 7 additions & 2 deletions linux/gatt/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type sub struct {

// NewClient returns a GATT Client.
func NewClient(conn ble.Conn, cache ble.GattCache, done chan bool, l ble.Logger) (*Client, error) {
cl := l.ChildLogger(map[string]interface{}{"client": hex.EncodeToString(conn.RemoteAddr().Bytes())})
cl := l.ChildLogger(map[string]interface{}{"gatt": hex.EncodeToString(conn.RemoteAddr().Bytes())})
p := &Client{
subs: make(map[uint16]*sub),
conn: conn,
Expand Down Expand Up @@ -456,7 +456,12 @@ func (p *Client) HandleNotification(req []byte) {
case sub.nHandler != nil:
sub.nHandler(sub.id, nd)
default:
p.Warnf("no handler, dropping data vh 0x%x, indication %v, id %v, %x", vh, indication, sub.id, nd)
select {
case <-p.conn.Disconnected():
//ok
default:
p.Warnf("no handler, dropping data vh 0x%x, indication %v, id %v, %x", vh, indication, sub.id, nd)
}
}
sub.id++
}
Expand Down
31 changes: 31 additions & 0 deletions linux/hci/cmd/cmd_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -1560,3 +1560,34 @@ type LERemoteConnectionParameterRequestNegativeReplyRP struct {
func (c *LERemoteConnectionParameterRequestNegativeReplyRP) Unmarshal(b []byte) error {
return unmarshal(c, b)
}

// LEWriteSuggestedDefaultDataLength implements LE Write Suggested Default Data Length (0x08|0x0024) [Vol 2, Part E, 7.8.35]
type LEWriteSuggestedDefaultDataLength struct {
SuggestedMaxTxOctets uint16
SuggestedMaxTxTime uint16
}

func (c *LEWriteSuggestedDefaultDataLength) String() string {
return "LE Write Suggested Default Data Length (0x08|0x0024)"
}

// OpCode returns the opcode of the command.
func (c *LEWriteSuggestedDefaultDataLength) OpCode() int { return 0x08<<10 | 0x0024 }

// Len returns the length of the command.
func (c *LEWriteSuggestedDefaultDataLength) Len() int { return 4 }

// Marshal serializes the command parameters into binary form.
func (c *LEWriteSuggestedDefaultDataLength) Marshal(b []byte) error {
return marshal(c, b)
}

// LEWriteSuggestedDefaultDataLengthRP returns the return parameter of LE Write Suggested Default Data Length
type LEWriteSuggestedDefaultDataLengthRP struct {
Status uint8
}

// Unmarshal de-serializes the binary data and stores the result in the receiver.
func (c *LEWriteSuggestedDefaultDataLengthRP) Unmarshal(b []byte) error {
return unmarshal(c, b)
}
25 changes: 21 additions & 4 deletions linux/hci/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type Conn struct {
encryptionEnabled bool

smp SmpManager
encInfo ble.EncryptionChangedInfo
encChanged chan ble.EncryptionChangedInfo
ble.Logger
}
Expand Down Expand Up @@ -145,8 +146,23 @@ func (c *Conn) Pair(authData ble.AuthData, to time.Duration) error {

func (c *Conn) StartEncryption(ch chan ble.EncryptionChangedInfo) error {
if c.encryptionEnabled {
//we already have the encryption changed info, send it to the channel if possible
if ch != nil {
go func(conn *Conn, c chan ble.EncryptionChangedInfo) {
select {
case ch <- conn.encInfo:
//ok
case <-time.After(1 * time.Second):
conn.Errorf("encryptionChanged: failed to send encryption update to channel: %v", conn.encInfo)
}
}(c, ch)
return nil
}

//if a nil channel is passed in, then return the already enabled error
return ble.ErrEncryptionAlreadyEnabled
}

c.encChanged = ch
err := c.smp.StartEncryption()
if err != nil {
Expand Down Expand Up @@ -352,6 +368,7 @@ func (c *Conn) writePDU(pdu []byte) (int, error) {
default:
}

c.Debugf("tx: %x", pkt.Bytes())
if _, err := c.hci.skt.Write(pkt.Bytes()); err != nil {
return sent, err
}
Expand All @@ -377,7 +394,7 @@ func (c *Conn) recombine() error {
}

p := pdu(pkt.data())
c.Debugf("recombine: pdu in - % X", pkt.data())
c.Debugf("recombine: pdu in - %x", pkt.data())
// Currently, check for LE-U only. For channels that we don't recognizes,
// re-combine them anyway, and discard them later when we dispatch the PDU
// according to CID.
Expand Down Expand Up @@ -435,13 +452,13 @@ func (c *Conn) handleEncryptionChanged(status uint8, enabled uint8) {

c.encryptionEnabled = enabled == 0x01

info := ble.EncryptionChangedInfo{Status: int(status), Err: err, Enabled: c.encryptionEnabled}
c.encInfo = ble.EncryptionChangedInfo{Status: int(status), Err: err, Enabled: c.encryptionEnabled}
if c.encChanged != nil {
select {
case c.encChanged <- info:
case c.encChanged <- c.encInfo:
return
default:
c.Errorf("encryptionChanged: failed to send encryption update to channel: %v", info)
c.Errorf("encryptionChanged: failed to send encryption update to channel: %v", c.encInfo)
}
} else {
c.Infof("encryptionChanged: status %v", status)
Expand Down
7 changes: 6 additions & 1 deletion linux/hci/hci.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ func (h *HCI) init() error {
WriteLEHostSupportRP := cmd.WriteLEHostSupportRP{}
h.Send(&cmd.WriteLEHostSupport{LESupportedHost: 1, SimultaneousLEHost: 0}, &WriteLEHostSupportRP)

WriteDefaultDataLengthRP := cmd.LEWriteSuggestedDefaultDataLengthRP{}
h.Send(&cmd.LEWriteSuggestedDefaultDataLength{SuggestedMaxTxOctets: 251, SuggestedMaxTxTime: 2120}, &WriteDefaultDataLengthRP)

return h.err
}

Expand Down Expand Up @@ -400,6 +403,7 @@ func (h *HCI) send(c Command) ([]byte, error) {
h.sent[oc] = p //use oc here due to swap to 0xff for vendor events
h.muSent.Unlock()

h.Debugf("tx op: %v - %v", c.OpCode(), hex.EncodeToString(b))
if !h.isOpen() {
return nil, fmt.Errorf("hci closed")
} else if n, err := h.skt.Write(b[:4+c.Len()]); err != nil {
Expand Down Expand Up @@ -525,6 +529,7 @@ func (h *HCI) handlePkt(b []byte) error {
t, b := b[0], b[1:]
switch t {
case pktTypeACLData:
h.Debugf("hci rx acl: %v", hex.EncodeToString(b))
return h.handleACL(b)
case pktTypeEvent:
return h.handleEvt(b)
Expand Down Expand Up @@ -929,7 +934,7 @@ func (h *HCI) handleEncryptionKeyRefreshComplete(b []byte) error {

func (h *HCI) handleNumberOfCompletedPackets(b []byte) error {
e := evt.NumberOfCompletedPackets(b)
h.Debugf("numberOfCompletedPackets: % X", b)
h.Debugf("numberOfCompletedPackets: %v", hex.EncodeToString(b))
h.muConns.Lock()
defer h.muConns.Unlock()
for i := 0; i < int(e.NumberOfHandles()); i++ {
Expand Down
Loading

0 comments on commit a1fdaee

Please sign in to comment.