-
Couldn't load subscription status.
- Fork 78
Work on the Attribute module #99
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
Draft
nm17
wants to merge
7
commits into
embassy-rs:main
Choose a base branch
from
nm17:callback-attributes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e210bfc
Document attribute module
nm17 5b7a205
Add more derives in attribute and uuid modules
nm17 aa9c169
Add .gitignore
nm17 60152cc
Fix value typo in code
nm17 af16bf4
cargo fmt
nm17 8dd0af7
Refactor everything related to attributes
nm17 c517772
Refactor away repeating parts of the attribute module
nm17 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # Created by https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,rust-analyzer | ||
| # Edit at https://www.toptal.com/developers/gitignore?templates=rust,visualstudiocode,rust-analyzer | ||
|
|
||
| ### Rust ### | ||
| # Generated by Cargo | ||
| # will have compiled files and executables | ||
| debug/ | ||
| target/ | ||
|
|
||
| # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries | ||
| # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html | ||
| Cargo.lock | ||
|
|
||
| # These are backup files generated by rustfmt | ||
| **/*.rs.bk | ||
|
|
||
| # MSVC Windows builds of rustc generate these, which store debugging information | ||
| *.pdb | ||
|
|
||
| ### rust-analyzer ### | ||
| # Can be generated by other build systems other than cargo (ex: bazelbuild/rust_rules) | ||
| rust-project.json | ||
|
|
||
|
|
||
| ### VisualStudioCode ### | ||
| .vscode/* | ||
| !.vscode/settings.json | ||
| !.vscode/tasks.json | ||
| !.vscode/launch.json | ||
| !.vscode/extensions.json | ||
| !.vscode/*.code-snippets | ||
|
|
||
| # Local History for Visual Studio Code | ||
| .history/ | ||
|
|
||
| # Built Visual Studio Code Extensions | ||
| *.vsix | ||
|
|
||
| ### VisualStudioCode Patch ### | ||
| # Ignore all local history of files | ||
| .history | ||
| .ionide | ||
|
|
||
| # End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode,rust-analyzer | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| use super::Uuid; | ||
|
|
||
| pub const GENERIC_ACCESS_SERVICE_UUID16: Uuid = Uuid::Uuid16(0x1800u16.to_le_bytes()); | ||
| pub const CHARACTERISTIC_DEVICE_NAME_UUID16: Uuid = Uuid::Uuid16(0x2A00u16.to_le_bytes()); | ||
| pub const CHARACTERISTIC_APPEARANCE_UUID16: Uuid = Uuid::Uuid16(0x2A03u16.to_le_bytes()); | ||
|
|
||
| pub const GENERIC_ATTRIBUTE_SERVICE_UUID16: Uuid = Uuid::Uuid16(0x1801u16.to_le_bytes()); | ||
|
|
||
| pub const PRIMARY_SERVICE_UUID16: Uuid = Uuid::Uuid16(0x2800u16.to_le_bytes()); | ||
| pub const SECONDARY_SERVICE_UUID16: Uuid = Uuid::Uuid16(0x2801u16.to_le_bytes()); | ||
| pub const INCLUDE_SERVICE_UUID16: Uuid = Uuid::Uuid16(0x2802u16.to_le_bytes()); | ||
| pub const CHARACTERISTIC_UUID16: Uuid = Uuid::Uuid16(0x2803u16.to_le_bytes()); | ||
| pub const CHARACTERISTIC_CCCD_UUID16: Uuid = Uuid::Uuid16(0x2902u16.to_le_bytes()); | ||
| pub const GENERIC_ATTRIBUTE_UUID16: Uuid = Uuid::Uuid16(0x1801u16.to_le_bytes()); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| use super::CharacteristicProp; | ||
| use super::CharacteristicProps; | ||
| use crate::att::AttErrorCode; | ||
| use crate::cursor::WriteCursor; | ||
| use crate::types::uuid::Uuid; | ||
|
|
||
| /// The underlying data behind an attribute. | ||
| #[derive(Debug, PartialEq, Eq)] | ||
| #[non_exhaustive] | ||
| pub enum AttributeData<'d> { | ||
| /// Service UUID Data | ||
| /// | ||
| /// Serializes to raw bytes of UUID. | ||
| Service { uuid: Uuid }, | ||
| /// Read only data | ||
| /// | ||
| /// Implemented by storing a borrow of a slice. | ||
| /// The slice has to live at least as much as the device. | ||
| ReadOnlyData { | ||
| props: CharacteristicProps, | ||
| value: &'d [u8], | ||
| }, | ||
| /// Read and write data | ||
| /// | ||
| /// Implemented by storing a mutable borrow of a slice. | ||
| /// The slice has to live at least as much as the device. | ||
| Data { | ||
| props: CharacteristicProps, | ||
| value: &'d mut [u8], | ||
| }, | ||
| /// Characteristic declaration | ||
| Declaration { | ||
| props: CharacteristicProps, | ||
| handle: u16, | ||
| uuid: Uuid, | ||
| }, | ||
| /// Client Characteristic Configuration Descriptor | ||
| /// | ||
| /// Ref: BLUETOOTH CORE SPECIFICATION Version 6.0, Vol 3, Part G, Section 3.3.3.3 Client Characteristic Configuration | ||
| Cccd { notifications: bool, indications: bool }, | ||
| } | ||
|
|
||
| impl<'d> AttributeData<'d> { | ||
| pub fn readable(&self) -> bool { | ||
| match self { | ||
| Self::Data { props, value } => props.0 & (CharacteristicProp::Read as u8) != 0, | ||
| _ => true, | ||
| } | ||
| } | ||
|
|
||
| pub fn writable(&self) -> bool { | ||
| match self { | ||
| Self::Data { props, value } => { | ||
| props.0 | ||
| & (CharacteristicProp::Write as u8 | ||
| | CharacteristicProp::WriteWithoutResponse as u8 | ||
| | CharacteristicProp::AuthenticatedWrite as u8) | ||
| != 0 | ||
| } | ||
| Self::Cccd { | ||
| notifications, | ||
| indications, | ||
| } => true, | ||
| _ => false, | ||
| } | ||
| } | ||
|
|
||
| /// Read the attribute value from some kind of a readable attribute data source | ||
| /// | ||
| /// Seek to to the `offset`-nth byte in the source data, fill the response data slice `data` up to the end or lower. | ||
| /// | ||
| /// The data buffer is always sized L2CAP_MTU, minus the 4 bytes for the L2CAP header) | ||
| /// The max stated value of an attribute in the GATT specification is 512 bytes. | ||
| /// | ||
| /// Returns the amount of bytes that have been written into `data`. | ||
| pub fn read(&self, offset: usize, data: &mut [u8]) -> Result<usize, AttErrorCode> { | ||
| if !self.readable() { | ||
| return Err(AttErrorCode::ReadNotPermitted); | ||
| } | ||
| match self { | ||
| Self::ReadOnlyData { props, value } => { | ||
| if offset > value.len() { | ||
| return Ok(0); | ||
| } | ||
| let len = data.len().min(value.len() - offset); | ||
| if len > 0 { | ||
| data[..len].copy_from_slice(&value[offset..offset + len]); | ||
| } | ||
| Ok(len) | ||
| } | ||
| Self::Data { props, value } => { | ||
| if offset > value.len() { | ||
| return Ok(0); | ||
| } | ||
| let len = data.len().min(value.len() - offset); | ||
| if len > 0 { | ||
| data[..len].copy_from_slice(&value[offset..offset + len]); | ||
| } | ||
| Ok(len) | ||
| } | ||
| Self::Service { uuid } => { | ||
| let val = uuid.as_raw(); | ||
| if offset > val.len() { | ||
| return Ok(0); | ||
| } | ||
| let len = data.len().min(val.len() - offset); | ||
| if len > 0 { | ||
| data[..len].copy_from_slice(&val[offset..offset + len]); | ||
| } | ||
| Ok(len) | ||
| } | ||
| Self::Cccd { | ||
| notifications, | ||
| indications, | ||
| } => { | ||
| if offset > 0 { | ||
| return Err(AttErrorCode::InvalidOffset); | ||
| } | ||
| if data.len() < 2 { | ||
| return Err(AttErrorCode::UnlikelyError); | ||
| } | ||
| let mut v = 0; | ||
| if *notifications { | ||
| v |= 0x01; | ||
| } | ||
|
|
||
| if *indications { | ||
| v |= 0x02; | ||
| } | ||
| data[0] = v; | ||
| Ok(2) | ||
| } | ||
| Self::Declaration { props, handle, uuid } => { | ||
| let val = uuid.as_raw(); | ||
| if offset > val.len() + 3 { | ||
| return Ok(0); | ||
| } | ||
| let mut w = WriteCursor::new(data); | ||
| if offset == 0 { | ||
| w.write(props.0)?; | ||
| w.write(*handle)?; | ||
| } else if offset == 1 { | ||
| w.write(*handle)?; | ||
| } else if offset == 2 { | ||
| w.write(handle.to_le_bytes()[1])?; | ||
| } | ||
|
|
||
| let to_write = w.available().min(val.len()); | ||
|
|
||
| if to_write > 0 { | ||
| w.append(&val[..to_write])?; | ||
| } | ||
| Ok(w.len()) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Write into the attribute value at 'offset' data from `data` buffer | ||
| /// | ||
| /// Expect the writes to be fragmented, like with [`AttributeData::read`] | ||
| pub fn write(&mut self, offset: usize, data: &[u8]) -> Result<(), AttErrorCode> { | ||
| let writable = self.writable(); | ||
|
|
||
| match self { | ||
| Self::Data { value, props } => { | ||
| if !writable { | ||
| return Err(AttErrorCode::WriteNotPermitted); | ||
| } | ||
|
|
||
| if offset + data.len() <= value.len() { | ||
| value[offset..offset + data.len()].copy_from_slice(data); | ||
| Ok(()) | ||
| } else { | ||
| Err(AttErrorCode::InvalidOffset) | ||
| } | ||
| } | ||
| Self::Cccd { | ||
| notifications, | ||
| indications, | ||
| } => { | ||
| if offset > 0 { | ||
| return Err(AttErrorCode::InvalidOffset); | ||
| } | ||
|
|
||
| if data.is_empty() { | ||
| return Err(AttErrorCode::UnlikelyError); | ||
| } | ||
|
|
||
| *notifications = data[0] & 0b00000001 != 0; | ||
| *indications = data[0] & 0b00000010 != 0; | ||
| Ok(()) | ||
| } | ||
| _ => Err(AttErrorCode::WriteNotPermitted), | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.