blob: 1a03465f2d02dcf90b6455ad4bf5eff0e7101bd5 [file] [log] [blame]
package linux
import (
"fmt"
"io"
"log"
"github.com/paypal/gatt/linux/cmd"
)
type aclData struct {
attr uint16
flags uint8
dlen uint16
b []byte
}
func (a *aclData) unmarshal(b []byte) error {
if len(b) < 4 {
return fmt.Errorf("malformed acl packet")
}
attr := uint16(b[0]) | (uint16(b[1]&0x0f) << 8)
flags := b[1] >> 4
dlen := uint16(b[2]) | (uint16(b[3]) << 8)
if len(b) != 4+int(dlen) {
return fmt.Errorf("malformed acl packet")
}
*a = aclData{attr: attr, flags: flags, dlen: dlen, b: b[4:]}
return nil
}
type conn struct {
hci *HCI
attr uint16
aclc chan *aclData
}
func newConn(hci *HCI, hh uint16) *conn {
return &conn{
hci: hci,
attr: hh,
aclc: make(chan *aclData),
}
}
func (c *conn) updateConnection() (int, error) {
b := []byte{
0x12, // Code (Connection Param Update)
0x02, // ID
0x08, 0x00, // DataLength
0x08, 0x00, // IntervalMin
0x18, 0x00, // IntervalMax
0x00, 0x00, // SlaveLatency
0xC8, 0x00} // TimeoutMultiplier
return c.write(0x05, b)
}
// write writes the l2cap payload to the controller.
// It first prepend the l2cap header (4-bytes), and diassemble the payload
// if it is larger than the HCI LE buffer size that the conntroller can support.
func (c *conn) write(cid int, b []byte) (int, error) {
flag := uint8(0) // ACL data continuation flag
tlen := len(b) // Total length of the l2cap payload
// log.Printf("W: [ % X ]", b)
w := append(
[]byte{
0, // packet type
0, 0, // attr
0, 0, // dlen
uint8(tlen), uint8(tlen >> 8), // l2cap header
uint8(cid), uint8(cid >> 8), // l2cap header
}, b...)
n := 4 + tlen // l2cap header + l2cap payload
for n > 0 {
dlen := n
if dlen > c.hci.bufSize {
dlen = c.hci.bufSize
}
w[0] = 0x02 // packetTypeACL
w[1] = uint8(c.attr)
w[2] = uint8(c.attr>>8) | flag
w[3] = uint8(dlen)
w[4] = uint8(dlen >> 8)
// make sure we don't send more buffers than the controller can handdle
c.hci.bufCnt <- struct{}{}
c.hci.d.Write(w[:5+dlen])
w = w[dlen:] // advance the pointer to the next segment, if any.
flag = 0x10 // the rest of iterations attr continued segments, if any.
n -= dlen
}
return len(b), nil
}
func (c *conn) Read(b []byte) (int, error) {
a, ok := <-c.aclc
if !ok {
return 0, io.EOF
}
tlen := int(uint16(a.b[0]) | uint16(a.b[1])<<8)
if tlen > len(b) {
return 0, io.ErrShortBuffer
}
d := a.b[4:] // skip l2cap header
copy(b, d)
n := len(d)
// Keep receiving and reassemble continued l2cap segments
for n != tlen {
if a, ok = <-c.aclc; !ok || (a.flags&0x1) == 0 {
return n, io.ErrUnexpectedEOF
}
copy(b[n:], a.b)
n += len(a.b)
}
// log.Printf("R: [ % X ]", b[:n])
return n, nil
}
func (c *conn) Write(b []byte) (int, error) {
return c.write(0x04, b)
}
// Close disconnects the connection by sending HCI disconnect command to the device.
func (c *conn) Close() error {
h := c.hci
hh := c.attr
h.connsmu.Lock()
defer h.connsmu.Unlock()
_, found := h.conns[hh]
if !found {
// log.Printf("l2conn: 0x%04x already disconnected", hh)
return nil
}
if err, _ := h.c.Send(cmd.Disconnect{ConnectionHandle: hh, Reason: 0x13}); err != nil {
return fmt.Errorf("l2conn: failed to disconnect, %s", err)
}
return nil
}
// Signal Packets
// 0x00 Reserved Any
// 0x01 Command reject 0x0001 and 0x0005
// 0x02 Connection request 0x0001
// 0x03 Connection response 0x0001
// 0x04 Configure request 0x0001
// 0x05 Configure response 0x0001
// 0x06 Disconnection request 0x0001 and 0x0005
// 0x07 Disconnection response 0x0001 and 0x0005
// 0x08 Echo request 0x0001
// 0x09 Echo response 0x0001
// 0x0A Information request 0x0001
// 0x0B Information response 0x0001
// 0x0C Create Channel request 0x0001
// 0x0D Create Channel response 0x0001
// 0x0E Move Channel request 0x0001
// 0x0F Move Channel response 0x0001
// 0x10 Move Channel Confirmation 0x0001
// 0x11 Move Channel Confirmation response 0x0001
// 0x12 Connection Parameter Update request 0x0005
// 0x13 Connection Parameter Update response 0x0005
// 0x14 LE Credit Based Connection request 0x0005
// 0x15 LE Credit Based Connection response 0x0005
// 0x16 LE Flow Control Credit 0x0005
func (c *conn) handleSignal(a *aclData) error {
log.Printf("ignore l2cap signal:[ % X ]", a.b)
// FIXME: handle LE signaling channel (CID: 5)
return nil
}