// PrepareWrite requests the server to prepare to write the value of an attribute. // The server will respond to this request with a Prepare Write Response, so that // the Client can verify that the value was received correctly. // [Vol 3, Part F, 3.4.6.1 & 3.4.6.2] func (c *Client) PrepareWrite(handle uint16, offset uint16, value []byte) (uint16, uint16, []byte, error) { if len(value) > c.l2c.TxMTU()-5 { return 0, 0, nil, ErrInvalidArgument } // Acquire and reuse the txBuf, and release it after usage. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() req := PrepareWriteRequest(txBuf[:5+len(value)]) req.SetAttributeOpcode() req.SetAttributeHandle(handle) req.SetValueOffset(offset) b, err := c.sendReq(req) if err != nil { return 0, 0, nil, err } // Convert and validate the response. rsp := PrepareWriteResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return 0, 0, nil, ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) != 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): fallthrough case len(rsp) < 5: return 0, 0, nil, ErrInvalidResponse } return rsp.AttributeHandle(), rsp.ValueOffset(), rsp.PartAttributeValue(), nil }
// ExecuteWrite requests the server to write or cancel the write of all the prepared // values currently held in the prepare queue from this Client. This request shall be // handled by the server as an atomic operation. [Vol 3, Part F, 3.4.6.3 & 3.4.6.4] func (c *Client) ExecuteWrite(flags uint8) error { // Acquire and reuse the txBuf, and release it after usage. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() req := ExecuteWriteRequest(txBuf[:1]) req.SetAttributeOpcode() req.SetFlags(flags) b, err := c.sendReq(req) if err != nil { return err } // Convert and validate the response. rsp := ExecuteWriteResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) == 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): return ErrInvalidResponse } return nil }
// Write requests the server to write the value of an attribute and acknowledge that // this has been achieved in a Write Response. [Vol 3, Part F, 3.4.5.1 & 3.4.5.2] func (c *Client) Write(handle uint16, value []byte) error { if len(value) > c.l2c.TxMTU()-3 { return ErrInvalidArgument } // Acquire and reuse the txBuf, and release it after usage. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() req := WriteRequest(txBuf[:3+len(value)]) req.SetAttributeOpcode() req.SetAttributeHandle(handle) req.SetAttributeValue(value) b, err := c.sendReq(req) if err != nil { return err } // Convert and validate the response. rsp := WriteResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) != 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): return ErrInvalidResponse } return nil }
// ReadBlob requests the server to read part of the value of an attribute at a // given offset and return a specific part of the value in a Read Blob Response. // [Vol 3, Part F, 3.4.4.5 & 3.4.4.6] func (c *Client) ReadBlob(handle, offset uint16) ([]byte, error) { // Acquire and reuse the txBuf, and release it after usage. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() req := ReadBlobRequest(txBuf[:5]) req.SetAttributeOpcode() req.SetAttributeHandle(handle) req.SetValueOffset(offset) b, err := c.sendReq(req) if err != nil { return nil, err } // Convert and validate the response. rsp := ReadBlobResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return nil, ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) != 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): fallthrough case len(rsp) < 1: return nil, ErrInvalidResponse } return rsp.PartAttributeValue(), nil }
// ReadByType obtains the values of attributes where the attribute type is known // but the handle is not known. [Vol 3, Part F, 3.4.4.1 & 3.4.4.2] func (c *Client) ReadByType(starth, endh uint16, uuid ble.UUID) (int, []byte, error) { if starth > endh || (len(uuid) != 2 && len(uuid) != 16) { return 0, nil, ErrInvalidArgument } // Acquire and reuse the txBuf, and release it after usage. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() req := ReadByTypeRequest(txBuf[:5+len(uuid)]) req.SetAttributeOpcode() req.SetStartingHandle(starth) req.SetEndingHandle(endh) req.SetAttributeType(uuid) b, err := c.sendReq(req) if err != nil { return 0, nil, err } // Convert and validate the response. rsp := ReadByTypeResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return 0, nil, ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) != 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): fallthrough case len(rsp) < 4 || len(rsp.AttributeDataList())%int(rsp.Length()) != 0: return 0, nil, ErrInvalidResponse } return int(rsp.Length()), rsp.AttributeDataList(), nil }
// ExchangeMTU informs the server of the client’s maximum receive MTU size and // request the server to respond with its maximum receive MTU size. [Vol 3, Part F, 3.4.2.1] func (c *Client) ExchangeMTU(clientRxMTU int) (serverRxMTU int, err error) { if clientRxMTU < ble.DefaultMTU || clientRxMTU > ble.MaxMTU { return 0, ErrInvalidArgument } // Acquire and reuse the txBuf, and release it after usage. // The same txBuf, or a newly allocate one, if the txMTU is changed, // will be released back to the channel. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() // Let L2CAP know the MTU we can handle. c.l2c.SetRxMTU(clientRxMTU) req := ExchangeMTURequest(txBuf[:3]) req.SetAttributeOpcode() req.SetClientRxMTU(uint16(clientRxMTU)) b, err := c.sendReq(req) if err != nil { return 0, err } // Convert and validate the response. rsp := ExchangeMTUResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return 0, ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) != 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): fallthrough case len(rsp) != 3: return 0, ErrInvalidResponse } txMTU := int(rsp.ServerRxMTU()) if len(txBuf) != txMTU { // Let L2CAP know the MTU that the remote device can handle. c.l2c.SetTxMTU(txMTU) // Put a re-allocated txBuf back to the channel. // The txBuf has been captured in deferred function. txBuf = make([]byte, txMTU, txMTU) } return txMTU, nil }
// FindInformation obtains the mapping of attribute handles with their associated types. // This allows a Client to discover the list of attributes and their types on a server. // [Vol 3, Part F, 3.4.3.1 & 3.4.3.2] func (c *Client) FindInformation(starth, endh uint16) (fmt int, data []byte, err error) { if starth == 0 || starth > endh { return 0x00, nil, ErrInvalidArgument } // Acquire and reuse the txBuf, and release it after usage. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() req := FindInformationRequest(txBuf[:5]) req.SetAttributeOpcode() req.SetStartingHandle(starth) req.SetEndingHandle(endh) b, err := c.sendReq(req) if err != nil { return 0x00, nil, err } // Convert and validate the response. rsp := FindInformationResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return 0x00, nil, ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) != 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): fallthrough case len(rsp) < 6: fallthrough case rsp.Format() == 0x01 && ((len(rsp)-2)%4) != 0: fallthrough case rsp.Format() == 0x02 && ((len(rsp)-2)%18) != 0: return 0x00, nil, ErrInvalidResponse } return int(rsp.Format()), rsp.InformationData(), nil }
// ReadMultiple requests the server to read two or more values of a set of // attributes and return their values in a Read Multiple Response. // Only values that have a known fixed size can be read, with the exception of // the last value that can have a variable length. The knowledge of whether // attributes have a known fixed size is defined in a higher layer specification. // [Vol 3, Part F, 3.4.4.7 & 3.4.4.8] func (c *Client) ReadMultiple(handles []uint16) ([]byte, error) { // Should request to read two or more values. if len(handles) < 2 || len(handles)*2 > c.l2c.TxMTU()-1 { return nil, ErrInvalidArgument } // Acquire and reuse the txBuf, and release it after usage. txBuf := <-c.chTxBuf defer func() { c.chTxBuf <- txBuf }() req := ReadMultipleRequest(txBuf[:1+len(handles)*2]) req.SetAttributeOpcode() p := req.SetOfHandles() for _, h := range handles { binary.LittleEndian.PutUint16(p, h) p = p[2:] } b, err := c.sendReq(req) if err != nil { return nil, err } // Convert and validate the response. rsp := ReadMultipleResponse(b) switch { case rsp[0] == ErrorResponseCode && len(rsp) == 5: return nil, ble.ATTError(rsp[4]) case rsp[0] == ErrorResponseCode && len(rsp) != 5: fallthrough case rsp[0] != rsp.AttributeOpcode(): fallthrough case len(rsp) < 1: return nil, ErrInvalidResponse } return rsp.SetOfValues(), nil }
func (m msg) err() error { if code := m.result(); code != 0 { return ble.ATTError(code) } return nil }