Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Att Server: support "Prepare Write Request" and "Execute Write Request" #60

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions linux/att/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ This package implement Attribute Protocol (ATT) [Vol 3, Part F]
- [x] Write Request [3.4.5.1 & 3.4.5.2]
- [x] Write Command [3.4.5.3]
- [ ] Signed Write Command [3.4.5.4]
- [ ] Prepare Write Request [3.4.6.1 & 3.4.6.2]
- [ ] Execute Write Request [3.4.6.3]
- [x] Prepare Write Request [3.4.6.1 & 3.4.6.2]
- [x] Execute Write Request [3.4.6.3]
- [x] Handle Value Notification [3.4.7.1]
- [x] Handle Value Indication [3.4.7.2 & 3.4.7.3]

Expand Down
100 changes: 88 additions & 12 deletions linux/att/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ type Server struct {
chConfirm chan bool

dummyRspWriter ble.ResponseWriter

// Store a write handler for defer execute once receving ExecuteWriteRequest
prepareWriteRequestAttr *attr
prepareWriteRequestData bytes.Buffer
}

// NewServer returns an ATT (Attribute Protocol) server.
Expand Down Expand Up @@ -191,9 +195,11 @@ func (s *Server) handleRequest(b []byte) []byte {
resp = s.handleWriteRequest(b)
case WriteCommandCode:
s.handleWriteCommand(b)
case PrepareWriteRequestCode:
resp = s.handlePrepareWriteRequest(b)
case ExecuteWriteRequestCode:
resp = s.handleExecuteWriteRequest(b)
case ReadMultipleRequestCode,
PrepareWriteRequestCode,
ExecuteWriteRequestCode,
SignedWriteCommandCode:
fallthrough
default:
Expand Down Expand Up @@ -304,7 +310,7 @@ func (s *Server) handleFindByTypeValueRequest(r FindByTypeValueRequest) []byte {
// Since ResponseWriter caps the value at the capacity,
// we allocate one extra byte, and the written length.
buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-7+1))
e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2))
e := handleATT(a, s, r, ble.NewResponseWriter(buf2))
if e != ble.ErrSuccess || buf2.Len() > len(s.txBuf)-7 {
return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle)
}
Expand Down Expand Up @@ -352,7 +358,7 @@ func (s *Server) handleReadByTypeRequest(r ReadByTypeRequest) []byte {
v := a.v
if v == nil {
buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-2))
if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess {
if e := handleATT(a, s, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess {
// Return if the first value read cause an error.
if dlen == 0 {
return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e)
Expand Down Expand Up @@ -414,7 +420,7 @@ func (s *Server) handleReadRequest(r ReadRequest) []byte {

// Pass the request to upper layer with the ResponseWriter, which caps
// the buffer to a valid length of payload.
if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess {
if e := handleATT(a, s, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess {
return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e)
}
return rsp[:1+buf.Len()]
Expand Down Expand Up @@ -446,7 +452,7 @@ func (s *Server) handleReadBlobRequest(r ReadBlobRequest) []byte {

// Pass the request to upper layer with the ResponseWriter, which caps
// the buffer to a valid length of payload.
if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess {
if e := handleATT(a, s, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess {
return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e)
}
return rsp[:1+buf.Len()]
Expand All @@ -472,7 +478,7 @@ func (s *Server) handleReadByGroupRequest(r ReadByGroupTypeRequest) []byte {
v := a.v
if v == nil {
buf2 := bytes.NewBuffer(make([]byte, buf.Cap()-buf.Len()-4))
if e := handleATT(a, s.conn, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess {
if e := handleATT(a, s, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess {
return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e)
}
v = buf2.Bytes()
Expand Down Expand Up @@ -520,12 +526,62 @@ func (s *Server) handleWriteRequest(r WriteRequest) []byte {
if a == nil {
return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrWriteNotPerm)
}
if e := handleATT(a, s.conn, r, ble.NewResponseWriter(nil)); e != ble.ErrSuccess {
if e := handleATT(a, s, r, ble.NewResponseWriter(nil)); e != ble.ErrSuccess {
return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e)
}
return []byte{WriteResponseCode}
}

func (s *Server) handlePrepareWriteRequest(r PrepareWriteRequest) []byte {
logger.Debug("handlePrepareWriteRequest ->", "r.AttributeHandle", r.AttributeHandle())
// Validate the request.
switch {
case len(r) < 3:
return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
}

a, ok := s.db.at(r.AttributeHandle())
if !ok {
return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle)
}

// We don't support write to static value. Pass the request to upper layer.
if a == nil {
return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrWriteNotPerm)
}

if e := handleATT(a, s, r, ble.NewResponseWriter(nil)); e != ble.ErrSuccess {
return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e)
}

// Convert and validate the response.
rsp := PrepareWriteResponse(r)
rsp.SetAttributeOpcode()
return rsp
}

func (s *Server) handleExecuteWriteRequest(r ExecuteWriteRequest) []byte {
// Validate the request.
switch {
case len(r) < 2:
return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU)
}

switch r.Flags() {
case 0:
// 0x00 – Cancel all prepared writes
s.prepareWriteRequestAttr = nil
case 1:
// 0x01 – Immediately write all pending prepared values
a := s.prepareWriteRequestAttr
if e := handleATT(a, s, r, ble.NewResponseWriter(nil)); e != ble.ErrSuccess {
return newErrorResponse(r.AttributeOpcode(), 0, e)
}
}

return []byte{ExecuteWriteResponseCode}
}

// handle Write command. [Vol 3, Part F, 3.4.5.3]
func (s *Server) handleWriteCommand(r WriteCommand) []byte {
// Validate the request.
Expand All @@ -543,7 +599,7 @@ func (s *Server) handleWriteCommand(r WriteCommand) []byte {
if a == nil {
return nil
}
if e := handleATT(a, s.conn, r, s.dummyRspWriter); e != ble.ErrSuccess {
if e := handleATT(a, s, r, s.dummyRspWriter); e != ble.ErrSuccess {
return nil
}
return nil
Expand All @@ -558,10 +614,11 @@ func newErrorResponse(op byte, h uint16, s ble.ATTError) []byte {
return r
}

func handleATT(a *attr, conn ble.Conn, req []byte, rsp ble.ResponseWriter) ble.ATTError {
func handleATT(a *attr, s *Server, req []byte, rsp ble.ResponseWriter) ble.ATTError {
rsp.SetStatus(ble.ErrSuccess)
var offset int
var data []byte
conn := s.conn
switch req[0] {
case ReadByTypeRequestCode:
fallthrough
Expand All @@ -576,6 +633,27 @@ func handleATT(a *attr, conn ble.Conn, req []byte, rsp ble.ResponseWriter) ble.A
}
offset = int(ReadBlobRequest(req).ValueOffset())
a.rh.ServeRead(ble.NewRequest(conn, data, offset), rsp)
case PrepareWriteRequestCode:
if a.wh == nil {
return ble.ErrWriteNotPerm
}
data = PrepareWriteRequest(req).PartAttributeValue()
logger.Debug("handleATT", "PartAttributeValue",
fmt.Sprintf("data: %x, offset: %d, %p\n", data, int(PrepareWriteRequest(req).ValueOffset()), s.prepareWriteRequestAttr))

if s.prepareWriteRequestAttr == nil {
s.prepareWriteRequestAttr = a
s.prepareWriteRequestData.Reset()
}
s.prepareWriteRequestData.Write(data)

case ExecuteWriteRequestCode:
if a.wh == nil {
return ble.ErrWriteNotPerm
}
data = s.prepareWriteRequestData.Bytes()
a.wh.ServeWrite(ble.NewRequest(conn, data, offset), rsp)
s.prepareWriteRequestAttr = nil
case WriteRequestCode:
fallthrough
case WriteCommandCode:
Expand All @@ -584,8 +662,6 @@ func handleATT(a *attr, conn ble.Conn, req []byte, rsp ble.ResponseWriter) ble.A
}
data = WriteRequest(req).AttributeValue()
a.wh.ServeWrite(ble.NewRequest(conn, data, offset), rsp)
// case PrepareWriteRequestCode:
// case ExecuteWriteRequestCode:
// case SignedWriteCommandCode:
// case ReadByGroupTypeRequestCode:
// case ReadMultipleRequestCode:
Expand Down