From 04bd03276d3fa33d405004fc1a45e058442ab8f3 Mon Sep 17 00:00:00 2001 From: Kent Chen Date: Mon, 16 Oct 2017 16:40:39 +0800 Subject: [PATCH] att server: support "Prepare Write Request" and "Execute Write Request" --- linux/att/README.md | 4 +- linux/att/server.go | 100 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/linux/att/README.md b/linux/att/README.md index 25024f87..e5d8c3d2 100644 --- a/linux/att/README.md +++ b/linux/att/README.md @@ -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] diff --git a/linux/att/server.go b/linux/att/server.go index 21006ac7..174b0eb0 100644 --- a/linux/att/server.go +++ b/linux/att/server.go @@ -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. @@ -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: @@ -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) } @@ -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) @@ -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()] @@ -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()] @@ -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() @@ -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. @@ -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 @@ -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 @@ -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: @@ -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: