| package att |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "fmt" |
| "io" |
| "time" |
| |
| "github.com/go-ble/ble" |
| ) |
| |
| type conn struct { |
| ble.Conn |
| svr *Server |
| cccs map[uint16]uint16 |
| nn map[uint16]ble.Notifier |
| in map[uint16]ble.Notifier |
| } |
| |
| // Server implements an ATT (Attribute Protocol) server. |
| type Server struct { |
| conn *conn |
| db *DB |
| |
| // Refer to [Vol 3, Part F, 3.3.2 & 3.3.3] for the requirement of |
| // sequential request-response protocol, and transactions. |
| rxMTU int |
| txBuf []byte |
| chNotBuf chan []byte |
| chIndBuf chan []byte |
| chConfirm chan bool |
| |
| dummyRspWriter ble.ResponseWriter |
| |
| // Store a write handler for defer execute once receiving ExecuteWriteRequest |
| prepareWriteRequestAttr *attr |
| prepareWriteRequestData bytes.Buffer |
| } |
| |
| // NewServer returns an ATT (Attribute Protocol) server. |
| func NewServer(db *DB, l2c ble.Conn) (*Server, error) { |
| mtu := l2c.RxMTU() |
| if mtu < ble.DefaultMTU || mtu > ble.MaxMTU { |
| return nil, fmt.Errorf("invalid MTU") |
| } |
| // Although the rxBuf is initialized with the capacity of rxMTU, it is |
| // not discovered, and only the default ATT_MTU (23 bytes) of it shall |
| // be used until remote central request ExchangeMTU. |
| s := &Server{ |
| conn: &conn{ |
| Conn: l2c, |
| cccs: make(map[uint16]uint16), |
| in: make(map[uint16]ble.Notifier), |
| nn: make(map[uint16]ble.Notifier), |
| }, |
| db: db, |
| |
| rxMTU: mtu, |
| txBuf: make([]byte, ble.DefaultMTU, ble.DefaultMTU), |
| chNotBuf: make(chan []byte, 1), |
| chIndBuf: make(chan []byte, 1), |
| chConfirm: make(chan bool), |
| |
| dummyRspWriter: ble.NewResponseWriter(nil), |
| } |
| s.conn.svr = s |
| s.chNotBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU) |
| s.chIndBuf <- make([]byte, ble.DefaultMTU, ble.DefaultMTU) |
| return s, nil |
| } |
| |
| // notify sends notification to remote central. |
| func (s *Server) notify(h uint16, data []byte) (int, error) { |
| // Acquire and reuse notifyBuffer. Release it after usage. |
| nBuf := <-s.chNotBuf |
| defer func() { s.chNotBuf <- nBuf }() |
| |
| rsp := HandleValueNotification(nBuf) |
| rsp.SetAttributeOpcode() |
| rsp.SetAttributeHandle(h) |
| buf := bytes.NewBuffer(rsp.AttributeValue()) |
| buf.Reset() |
| if len(data) > buf.Cap() { |
| data = data[:buf.Cap()] |
| } |
| buf.Write(data) |
| return s.conn.Write(rsp[:3+buf.Len()]) |
| } |
| |
| // indicate sends indication to remote central. |
| func (s *Server) indicate(h uint16, data []byte) (int, error) { |
| // Acquire and reuse indicateBuffer. Release it after usage. |
| iBuf := <-s.chIndBuf |
| defer func() { s.chIndBuf <- iBuf }() |
| |
| rsp := HandleValueIndication(iBuf) |
| rsp.SetAttributeOpcode() |
| rsp.SetAttributeHandle(h) |
| buf := bytes.NewBuffer(rsp.AttributeValue()) |
| buf.Reset() |
| if len(data) > buf.Cap() { |
| data = data[:buf.Cap()] |
| } |
| buf.Write(data) |
| n, err := s.conn.Write(rsp[:3+buf.Len()]) |
| if err != nil { |
| return n, err |
| } |
| select { |
| case _, ok := <-s.chConfirm: |
| if !ok { |
| return 0, io.ErrClosedPipe |
| } |
| return n, nil |
| case <-time.After(time.Second * 30): |
| return 0, ErrSeqProtoTimeout |
| } |
| } |
| |
| // Loop accepts incoming ATT request, and respond response. |
| func (s *Server) Loop() { |
| type sbuf struct { |
| buf []byte |
| len int |
| } |
| pool := make(chan *sbuf, 2) |
| pool <- &sbuf{buf: make([]byte, s.rxMTU)} |
| pool <- &sbuf{buf: make([]byte, s.rxMTU)} |
| |
| seq := make(chan *sbuf) |
| go func() { |
| b := <-pool |
| for { |
| n, err := s.conn.Read(b.buf) |
| if n == 0 || err != nil { |
| close(seq) |
| close(s.chConfirm) |
| _ = s.conn.Close() |
| return |
| } |
| if b.buf[0] == HandleValueConfirmationCode { |
| select { |
| case s.chConfirm <- true: |
| default: |
| logger.Error("server", "received a spurious confirmation", nil) |
| } |
| continue |
| } |
| b.len = n |
| seq <- b // Send the current request for handling |
| b = <-pool // Swap the buffer for next incoming request. |
| } |
| }() |
| for req := range seq { |
| if rsp := s.handleRequest(req.buf[:req.len]); rsp != nil { |
| if len(rsp) != 0 { |
| s.conn.Write(rsp) |
| } |
| } |
| pool <- req |
| } |
| for h, ccc := range s.conn.cccs { |
| if ccc != 0 { |
| logger.Info("cleanup", ble.ContextKeyCCC, fmt.Sprintf("0x%02X", ccc)) |
| } |
| if ccc&cccIndicate != 0 { |
| s.conn.in[h].Close() |
| } |
| if ccc&cccNotify != 0 { |
| s.conn.nn[h].Close() |
| } |
| } |
| } |
| |
| func (s *Server) handleRequest(b []byte) []byte { |
| var resp []byte |
| logger.Debug("server", "req", fmt.Sprintf("% X", b)) |
| switch reqType := b[0]; reqType { |
| case ExchangeMTURequestCode: |
| resp = s.handleExchangeMTURequest(b) |
| case FindInformationRequestCode: |
| resp = s.handleFindInformationRequest(b) |
| case FindByTypeValueRequestCode: |
| resp = s.handleFindByTypeValueRequest(b) |
| case ReadByTypeRequestCode: |
| resp = s.handleReadByTypeRequest(b) |
| case ReadRequestCode: |
| resp = s.handleReadRequest(b) |
| case ReadBlobRequestCode: |
| resp = s.handleReadBlobRequest(b) |
| case ReadByGroupTypeRequestCode: |
| resp = s.handleReadByGroupRequest(b) |
| case WriteRequestCode: |
| resp = s.handleWriteRequest(b) |
| case WriteCommandCode: |
| s.handleWriteCommand(b) |
| case PrepareWriteRequestCode: |
| resp = s.handlePrepareWriteRequest(b) |
| case ExecuteWriteRequestCode: |
| resp = s.handleExecuteWriteRequest(b) |
| case ReadMultipleRequestCode, |
| SignedWriteCommandCode: |
| fallthrough |
| default: |
| resp = newErrorResponse(reqType, 0x0000, ble.ErrReqNotSupp) |
| } |
| logger.Debug("server", "rsp", fmt.Sprintf("% X", resp)) |
| return resp |
| } |
| |
| // handle MTU Exchange request. [Vol 3, Part F, 3.4.2] |
| func (s *Server) handleExchangeMTURequest(r ExchangeMTURequest) []byte { |
| // Validate the request. |
| switch { |
| case len(r) != 3: |
| fallthrough |
| case r.ClientRxMTU() < 23: |
| return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) |
| } |
| |
| txMTU := int(r.ClientRxMTU()) |
| s.conn.SetTxMTU(txMTU) |
| |
| if txMTU != len(s.txBuf) { |
| // Apply the txMTU afer this response has been sent and before |
| // any other attribute protocol PDU is sent. |
| defer func() { |
| s.txBuf = make([]byte, txMTU, txMTU) |
| <-s.chNotBuf |
| s.chNotBuf <- make([]byte, txMTU, txMTU) |
| <-s.chIndBuf |
| s.chIndBuf <- make([]byte, txMTU, txMTU) |
| }() |
| } |
| |
| rsp := ExchangeMTUResponse(s.txBuf) |
| rsp.SetAttributeOpcode() |
| rsp.SetServerRxMTU(uint16(s.rxMTU)) |
| return rsp[:3] |
| } |
| |
| // handle Find Information request. [Vol 3, Part F, 3.4.3.1 & 3.4.3.2] |
| func (s *Server) handleFindInformationRequest(r FindInformationRequest) []byte { |
| // Validate the request. |
| switch { |
| case len(r) != 5: |
| return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) |
| case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) |
| } |
| |
| rsp := FindInformationResponse(s.txBuf) |
| rsp.SetAttributeOpcode() |
| rsp.SetFormat(0x00) |
| buf := bytes.NewBuffer(rsp.InformationData()) |
| buf.Reset() |
| |
| // Each response shall contain Types of the same format. |
| for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { |
| if rsp.Format() == 0 { |
| rsp.SetFormat(0x01) |
| if a.typ.Len() == 16 { |
| rsp.SetFormat(0x02) |
| } |
| } |
| if rsp.Format() == 0x01 && a.typ.Len() != 2 { |
| break |
| } |
| if rsp.Format() == 0x02 && a.typ.Len() != 16 { |
| break |
| } |
| |
| if buf.Len()+2+a.typ.Len() > buf.Cap() { |
| break |
| } |
| binary.Write(buf, binary.LittleEndian, a.h) |
| binary.Write(buf, binary.LittleEndian, a.typ) |
| } |
| |
| // Nothing has been found. |
| if rsp.Format() == 0 { |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) |
| } |
| return rsp[:2+buf.Len()] |
| } |
| |
| // handle Find By Type Value request. [Vol 3, Part F, 3.4.3.3 & 3.4.3.4] |
| func (s *Server) handleFindByTypeValueRequest(r FindByTypeValueRequest) []byte { |
| // Validate the request. |
| switch { |
| case len(r) < 7: |
| return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) |
| case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) |
| } |
| |
| rsp := FindByTypeValueResponse(s.txBuf) |
| rsp.SetAttributeOpcode() |
| buf := bytes.NewBuffer(rsp.HandleInformationList()) |
| buf.Reset() |
| |
| for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { |
| v, starth, endh := a.v, a.h, a.endh |
| if !a.typ.Equal(ble.UUID16(r.AttributeType())) { |
| continue |
| } |
| if v == nil { |
| // The value shall not exceed ATT_MTU - 7 bytes. |
| // 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, r, ble.NewResponseWriter(buf2)) |
| if e != ble.ErrSuccess || buf2.Len() > len(s.txBuf)-7 { |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) |
| } |
| endh = a.h |
| } |
| if !(ble.UUID(v).Equal(ble.UUID(r.AttributeValue()))) { |
| continue |
| } |
| |
| if buf.Len()+4 > buf.Cap() { |
| break |
| } |
| binary.Write(buf, binary.LittleEndian, starth) |
| binary.Write(buf, binary.LittleEndian, endh) |
| } |
| if buf.Len() == 0 { |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) |
| } |
| |
| return rsp[:1+buf.Len()] |
| } |
| |
| // handle Read By Type request. [Vol 3, Part F, 3.4.4.1 & 3.4.4.2] |
| func (s *Server) handleReadByTypeRequest(r ReadByTypeRequest) []byte { |
| // Validate the request. |
| switch { |
| case len(r) != 7 && len(r) != 21: |
| return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) |
| case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) |
| } |
| |
| rsp := ReadByTypeResponse(s.txBuf) |
| rsp.SetAttributeOpcode() |
| buf := bytes.NewBuffer(rsp.AttributeDataList()) |
| buf.Reset() |
| |
| // handle length (2 bytes) + value length. |
| // Each response shall only contains values with the same size. |
| dlen := 0 |
| for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { |
| if !a.typ.Equal(ble.UUID(r.AttributeType())) { |
| continue |
| } |
| v := a.v |
| if v == nil { |
| buf2 := bytes.NewBuffer(make([]byte, 0, len(s.txBuf)-2)) |
| 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) |
| } |
| // Otherwise, skip to the next one. |
| break |
| } |
| v = buf2.Bytes() |
| } |
| if dlen == 0 { |
| // Found the first value. |
| dlen = 2 + len(v) |
| if dlen > 255 { |
| dlen = 255 |
| } |
| if dlen > buf.Cap() { |
| dlen = buf.Cap() |
| } |
| rsp.SetLength(uint8(dlen)) |
| } else if 2+len(v) != dlen { |
| break |
| } |
| |
| if buf.Len()+dlen > buf.Cap() { |
| break |
| } |
| binary.Write(buf, binary.LittleEndian, a.h) |
| binary.Write(buf, binary.LittleEndian, v[:dlen-2]) |
| } |
| if dlen == 0 { |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) |
| } |
| return rsp[:2+buf.Len()] |
| } |
| |
| // handle Read request. [Vol 3, Part F, 3.4.4.3 & 3.4.4.4] |
| func (s *Server) handleReadRequest(r ReadRequest) []byte { |
| // Validate the request. |
| switch { |
| case len(r) != 3: |
| return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) |
| } |
| |
| rsp := ReadResponse(s.txBuf) |
| rsp.SetAttributeOpcode() |
| buf := bytes.NewBuffer(rsp.AttributeValue()) |
| buf.Reset() |
| |
| a, ok := s.db.at(r.AttributeHandle()) |
| if !ok { |
| return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle) |
| } |
| |
| // Simple case. Read-only, no-authorization, no-authentication. |
| if a.v != nil { |
| binary.Write(buf, binary.LittleEndian, a.v) |
| return rsp[:1+buf.Len()] |
| } |
| |
| // Pass the request to upper layer with the ResponseWriter, which caps |
| // the buffer to a valid length of payload. |
| if e := handleATT(a, s, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess { |
| return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e) |
| } |
| return rsp[:1+buf.Len()] |
| } |
| |
| // handle Read Blob request. [Vol 3, Part F, 3.4.4.5 & 3.4.4.6] |
| func (s *Server) handleReadBlobRequest(r ReadBlobRequest) []byte { |
| // Validate the request. |
| switch { |
| case len(r) != 5: |
| return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) |
| } |
| |
| a, ok := s.db.at(r.AttributeHandle()) |
| if !ok { |
| return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), ble.ErrInvalidHandle) |
| } |
| |
| rsp := ReadBlobResponse(s.txBuf) |
| rsp.SetAttributeOpcode() |
| buf := bytes.NewBuffer(rsp.PartAttributeValue()) |
| buf.Reset() |
| |
| // Simple case. Read-only, no-authorization, no-authentication. |
| if a.v != nil { |
| binary.Write(buf, binary.LittleEndian, a.v) |
| return rsp[:1+buf.Len()] |
| } |
| |
| // Pass the request to upper layer with the ResponseWriter, which caps |
| // the buffer to a valid length of payload. |
| if e := handleATT(a, s, r, ble.NewResponseWriter(buf)); e != ble.ErrSuccess { |
| return newErrorResponse(r.AttributeOpcode(), r.AttributeHandle(), e) |
| } |
| return rsp[:1+buf.Len()] |
| } |
| |
| // handle Read Blob request. [Vol 3, Part F, 3.4.4.9 & 3.4.4.10] |
| func (s *Server) handleReadByGroupRequest(r ReadByGroupTypeRequest) []byte { |
| // Validate the request. |
| switch { |
| case len(r) != 7 && len(r) != 21: |
| return newErrorResponse(r.AttributeOpcode(), 0x0000, ble.ErrInvalidPDU) |
| case r.StartingHandle() == 0 || r.StartingHandle() > r.EndingHandle(): |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrInvalidHandle) |
| } |
| |
| rsp := ReadByGroupTypeResponse(s.txBuf) |
| rsp.SetAttributeOpcode() |
| buf := bytes.NewBuffer(rsp.AttributeDataList()) |
| buf.Reset() |
| |
| dlen := 0 |
| for _, a := range s.db.subrange(r.StartingHandle(), r.EndingHandle()) { |
| v := a.v |
| if v == nil { |
| buf2 := bytes.NewBuffer(make([]byte, buf.Cap()-buf.Len()-4)) |
| if e := handleATT(a, s, r, ble.NewResponseWriter(buf2)); e != ble.ErrSuccess { |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), e) |
| } |
| v = buf2.Bytes() |
| } |
| if dlen == 0 { |
| dlen = 4 + len(v) |
| if dlen > 255 { |
| dlen = 255 |
| } |
| if dlen > buf.Cap() { |
| dlen = buf.Cap() |
| } |
| rsp.SetLength(uint8(dlen)) |
| } else if 4+len(v) != dlen { |
| break |
| } |
| |
| if buf.Len()+dlen > buf.Cap() { |
| break |
| } |
| binary.Write(buf, binary.LittleEndian, a.h) |
| binary.Write(buf, binary.LittleEndian, a.endh) |
| binary.Write(buf, binary.LittleEndian, v[:dlen-4]) |
| } |
| if dlen == 0 { |
| return newErrorResponse(r.AttributeOpcode(), r.StartingHandle(), ble.ErrAttrNotFound) |
| } |
| return rsp[:2+buf.Len()] |
| } |
| |
| // handle Write request. [Vol 3, Part F, 3.4.5.1 & 3.4.5.2] |
| func (s *Server) handleWriteRequest(r WriteRequest) []byte { |
| // 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) |
| } |
| 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. |
| switch { |
| case len(r) <= 3: |
| return nil |
| } |
| |
| a, ok := s.db.at(r.AttributeHandle()) |
| if !ok { |
| return nil |
| } |
| |
| // We don't support write to static value. Pass the request to upper layer. |
| if a == nil { |
| return nil |
| } |
| if e := handleATT(a, s, r, s.dummyRspWriter); e != ble.ErrSuccess { |
| return nil |
| } |
| return nil |
| } |
| |
| func newErrorResponse(op byte, h uint16, s ble.ATTError) []byte { |
| r := ErrorResponse(make([]byte, 5)) |
| r.SetAttributeOpcode() |
| r.SetRequestOpcodeInError(op) |
| r.SetAttributeInError(h) |
| r.SetErrorCode(uint8(s)) |
| return r |
| } |
| |
| 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 |
| case ReadRequestCode: |
| if a.rh == nil { |
| return ble.ErrReadNotPerm |
| } |
| a.rh.ServeRead(ble.NewRequest(conn, data, offset), rsp) |
| case ReadBlobRequestCode: |
| if a.rh == nil { |
| return ble.ErrReadNotPerm |
| } |
| 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: |
| if a.wh == nil { |
| return ble.ErrWriteNotPerm |
| } |
| data = WriteRequest(req).AttributeValue() |
| a.wh.ServeWrite(ble.NewRequest(conn, data, offset), rsp) |
| // case SignedWriteCommandCode: |
| // case ReadByGroupTypeRequestCode: |
| // case ReadMultipleRequestCode: |
| default: |
| return ble.ErrReqNotSupp |
| } |
| |
| return rsp.Status() |
| } |