use super::{AttUuid, Handle, RawHandleRange};
use crate::{bytes::*, utils::HexSlice, Error};
use core::convert::TryInto;
enum_with_unknown! {
#[derive(Copy, Clone, Debug)]
pub enum ErrorCode(u8) {
InvalidHandle = 0x01,
ReadNotPermitted = 0x02,
WriteNotPermitted = 0x03,
InvalidPdu = 0x04,
InsufficientAuthentication = 0x05,
RequestNotSupported = 0x06,
InvalidOffset = 0x07,
InsufficientAuthorization = 0x08,
PrepareQueueFull = 0x09,
AttributeNotFound = 0x0A,
AttributeNotLong = 0x0B,
InsufficientEncryptionKeySize = 0x0C,
InvalidAttributeValueLength = 0x0D,
UnlikelyError = 0x0E,
InsufficientEncryption = 0x0F,
UnsupportedGroupType = 0x10,
InsufficientResources = 0x11,
}
}
#[derive(Debug)]
pub struct AttError {
code: ErrorCode,
handle: Handle,
}
impl AttError {
pub fn new(code: ErrorCode, handle: Handle) -> Self {
Self { code, handle }
}
pub fn attribute_not_found() -> Self {
Self::new(ErrorCode::AttributeNotFound, Handle::NULL)
}
pub fn error_code(&self) -> ErrorCode {
self.code
}
pub fn handle(&self) -> Handle {
self.handle
}
}
#[derive(Debug)]
pub struct ByTypeAttData<'a> {
handle: Handle,
value: HexSlice<&'a [u8]>,
}
impl<'a> ByTypeAttData<'a> {
pub fn new(att_mtu: u8, handle: Handle, mut value: &'a [u8]) -> Self {
let max_val_len = usize::from(att_mtu - 2);
if value.len() > max_val_len {
value = &value[..max_val_len];
}
Self {
handle,
value: HexSlice(value),
}
}
pub fn encoded_size(&self) -> u8 {
2 + self.value.as_ref().len() as u8
}
}
impl<'a> FromBytes<'a> for ByTypeAttData<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
Ok(ByTypeAttData {
handle: Handle::from_bytes(bytes)?,
value: HexSlice(bytes.read_rest()),
})
}
}
impl<'a> ToBytes for ByTypeAttData<'a> {
fn to_bytes(&self, writer: &mut ByteWriter<'_>) -> Result<(), Error> {
writer.write_u16_le(self.handle.as_u16())?;
writer.write_slice_truncate(self.value.as_ref());
Ok(())
}
}
#[derive(Debug, Copy, Clone)]
pub struct ByGroupAttData<'a> {
handle: Handle,
group_end_handle: Handle,
value: HexSlice<&'a [u8]>,
}
impl<'a> ByGroupAttData<'a> {
pub fn new(att_mtu: u8, handle: Handle, group_end_handle: Handle, mut value: &'a [u8]) -> Self {
let max_val_len = usize::from(att_mtu - 2 - 2);
if value.len() > max_val_len {
value = &value[..max_val_len];
}
Self {
handle,
group_end_handle,
value: HexSlice(value),
}
}
pub fn encoded_size(&self) -> u8 {
2 + 2 + self.value.as_ref().len() as u8
}
}
impl<'a> FromBytes<'a> for ByGroupAttData<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
Ok(ByGroupAttData {
handle: Handle::from_bytes(bytes)?,
group_end_handle: Handle::from_bytes(bytes)?,
value: HexSlice(bytes.read_rest()),
})
}
}
impl<'a> ToBytes for ByGroupAttData<'a> {
fn to_bytes(&self, writer: &mut ByteWriter<'_>) -> Result<(), Error> {
writer.write_u16_le(self.handle.as_u16())?;
writer.write_u16_le(self.group_end_handle.as_u16())?;
writer.write_slice_truncate(self.value.as_ref());
Ok(())
}
}
enum_with_unknown! {
#[derive(Debug, Copy, Clone)]
pub enum Opcode(u8) {
ErrorRsp = 0x01,
ExchangeMtuReq = 0x02,
ExchangeMtuRsp = 0x03,
FindInformationReq = 0x04,
FindInformationRsp = 0x05,
FindByTypeValueReq = 0x06,
FindByTypeValueRsp = 0x07,
ReadByTypeReq = 0x08,
ReadByTypeRsp = 0x09,
ReadReq = 0x0A,
ReadRsp = 0x0B,
ReadBlobReq = 0x0C,
ReadBlobRsp = 0x0D,
ReadMultipleReq = 0x0E,
ReadMultipleRsp = 0x0F,
ReadByGroupReq = 0x10,
ReadByGroupRsp = 0x11,
WriteReq = 0x12,
WriteRsp = 0x13,
WriteCommand = 0x52,
SignedWriteCommand = 0xD2,
PrepareWriteReq = 0x16,
PrepareWriteRsp = 0x17,
ExecuteWriteReq = 0x18,
ExecuteWriteRsp = 0x19,
HandleValueNotification = 0x1B,
HandleValueIndication = 0x1D,
HandleValueConfirmation = 0x1E,
}
}
impl Opcode {
pub fn raw(&self) -> u8 {
u8::from(*self)
}
pub fn is_authenticated(&self) -> bool {
self.raw() & 0x80 != 0
}
pub fn is_command(&self) -> bool {
self.raw() & 0x40 != 0
}
}
#[derive(Debug)]
pub enum AttPdu<'a> {
ErrorRsp {
opcode: Opcode,
handle: Handle,
error_code: ErrorCode,
},
ExchangeMtuReq {
mtu: u16,
},
ExchangeMtuRsp {
mtu: u16,
},
FindInformationReq {
handle_range: RawHandleRange,
},
FindInformationRsp {
format: u8,
data: HexSlice<&'a [u8]>,
},
FindByTypeValueReq {
handle_range: RawHandleRange,
attribute_type: u16,
attribute_value: HexSlice<&'a [u8]>,
},
FindByTypeValueRsp {
handles_information_list: HexSlice<&'a [u8]>,
},
ReadByTypeReq {
handle_range: RawHandleRange,
attribute_type: AttUuid,
},
ReadByTypeRsp {
length: u8,
data_list: HexSlice<&'a [u8]>,
},
ReadReq {
handle: Handle,
},
ReadRsp {
value: HexSlice<&'a [u8]>,
},
ReadBlobReq {
handle: Handle,
offset: u16,
},
ReadBlobRsp {
value: HexSlice<&'a [u8]>,
},
ReadMultipleReq {
handles: HexSlice<&'a [u8]>,
},
ReadMultipleRsp {
values: HexSlice<&'a [u8]>,
},
ReadByGroupReq {
handle_range: RawHandleRange,
group_type: AttUuid,
},
ReadByGroupRsp {
length: u8,
data_list: HexSlice<&'a [u8]>,
},
WriteReq {
handle: Handle,
value: HexSlice<&'a [u8]>,
},
WriteRsp,
WriteCommand {
handle: Handle,
value: HexSlice<&'a [u8]>,
},
SignedWriteCommand {
handle: Handle,
value: HexSlice<&'a [u8]>,
signature: HexSlice<&'a [u8; 12]>,
},
PrepareWriteReq {
handle: Handle,
offset: u16,
value: HexSlice<&'a [u8]>,
},
PrepareWriteRsp {
handle: Handle,
offset: u16,
value: HexSlice<&'a [u8]>,
},
ExecuteWriteReq {
flags: u8,
},
ExecuteWriteRsp,
HandleValueNotification {
handle: Handle,
value: HexSlice<&'a [u8]>,
},
HandleValueIndication {
handle: Handle,
value: HexSlice<&'a [u8]>,
},
HandleValueConfirmation,
Unknown {
opcode: Opcode,
params: HexSlice<&'a [u8]>,
},
}
impl<'a> FromBytes<'a> for AttPdu<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
let opcode = Opcode::from(bytes.read_u8()?);
Ok(match opcode {
Opcode::ErrorRsp => AttPdu::ErrorRsp {
opcode: Opcode::from(bytes.read_u8()?),
handle: Handle::from_bytes(bytes)?,
error_code: ErrorCode::from(bytes.read_u8()?),
},
Opcode::ExchangeMtuReq => AttPdu::ExchangeMtuReq {
mtu: bytes.read_u16_le()?,
},
Opcode::ExchangeMtuRsp => AttPdu::ExchangeMtuRsp {
mtu: bytes.read_u16_le()?,
},
Opcode::FindInformationReq => AttPdu::FindInformationReq {
handle_range: RawHandleRange::from_bytes(bytes)?,
},
Opcode::FindInformationRsp => AttPdu::FindInformationRsp {
format: bytes.read_u8()?,
data: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::FindByTypeValueReq => AttPdu::FindByTypeValueReq {
handle_range: RawHandleRange::from_bytes(bytes)?,
attribute_type: bytes.read_u16_le()?,
attribute_value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::FindByTypeValueRsp => AttPdu::FindByTypeValueRsp {
handles_information_list: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::ReadByTypeReq => AttPdu::ReadByTypeReq {
handle_range: RawHandleRange::from_bytes(bytes)?,
attribute_type: AttUuid::from_bytes(bytes)?,
},
Opcode::ReadByTypeRsp => AttPdu::ReadByTypeRsp {
length: bytes.read_u8()?,
data_list: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::ReadReq => AttPdu::ReadReq {
handle: Handle::from_bytes(bytes)?,
},
Opcode::ReadRsp => AttPdu::ReadRsp {
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::ReadBlobReq => AttPdu::ReadBlobReq {
handle: Handle::from_bytes(bytes)?,
offset: bytes.read_u16_le()?,
},
Opcode::ReadBlobRsp => AttPdu::ReadBlobRsp {
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::ReadMultipleReq => AttPdu::ReadMultipleReq {
handles: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::ReadMultipleRsp => AttPdu::ReadMultipleRsp {
values: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::ReadByGroupReq => AttPdu::ReadByGroupReq {
handle_range: RawHandleRange::from_bytes(bytes)?,
group_type: AttUuid::from_bytes(bytes)?,
},
Opcode::ReadByGroupRsp => AttPdu::ReadByGroupRsp {
length: bytes.read_u8()?,
data_list: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::WriteReq => AttPdu::WriteReq {
handle: Handle::from_bytes(bytes)?,
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::WriteRsp => AttPdu::WriteRsp {},
Opcode::WriteCommand => AttPdu::WriteCommand {
handle: Handle::from_bytes(bytes)?,
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::SignedWriteCommand => AttPdu::SignedWriteCommand {
handle: Handle::from_bytes(bytes)?,
value: HexSlice(bytes.read_slice(bytes.bytes_left() - 12)?),
signature: HexSlice(bytes.read_slice(12)?.try_into().unwrap()),
},
Opcode::PrepareWriteReq => AttPdu::PrepareWriteReq {
handle: Handle::from_bytes(bytes)?,
offset: bytes.read_u16_le()?,
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::PrepareWriteRsp => AttPdu::PrepareWriteRsp {
handle: Handle::from_bytes(bytes)?,
offset: bytes.read_u16_le()?,
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::ExecuteWriteReq => AttPdu::ExecuteWriteReq {
flags: bytes.read_u8()?,
},
Opcode::ExecuteWriteRsp => AttPdu::ExecuteWriteRsp {},
Opcode::HandleValueNotification => AttPdu::HandleValueNotification {
handle: Handle::from_bytes(bytes)?,
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::HandleValueIndication => AttPdu::HandleValueIndication {
handle: Handle::from_bytes(bytes)?,
value: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
Opcode::HandleValueConfirmation => AttPdu::HandleValueConfirmation {},
Opcode::Unknown(_) => AttPdu::Unknown {
opcode,
params: HexSlice(bytes.read_slice(bytes.bytes_left())?),
},
})
}
}
impl<'a> ToBytes for AttPdu<'a> {
fn to_bytes(&self, writer: &mut ByteWriter<'_>) -> Result<(), Error> {
writer.write_u8(self.opcode().into())?;
match *self {
AttPdu::ErrorRsp {
opcode,
handle,
error_code,
} => {
writer.write_u8(opcode.into())?;
writer.write_u16_le(handle.as_u16())?;
writer.write_u8(error_code.into())?;
}
AttPdu::ExchangeMtuReq { mtu } => {
writer.write_u16_le(mtu)?;
}
AttPdu::ExchangeMtuRsp { mtu } => {
writer.write_u16_le(mtu)?;
}
AttPdu::FindInformationReq { handle_range } => {
handle_range.to_bytes(writer)?;
}
AttPdu::FindInformationRsp { format, data } => {
writer.write_u8(format)?;
writer.write_slice(data.as_ref())?;
}
AttPdu::FindByTypeValueReq {
handle_range,
attribute_type,
attribute_value,
} => {
handle_range.to_bytes(writer)?;
writer.write_u16_le(attribute_type)?;
writer.write_slice(attribute_value.as_ref())?;
}
AttPdu::FindByTypeValueRsp {
handles_information_list,
} => {
writer.write_slice(handles_information_list.as_ref())?;
}
AttPdu::ReadByTypeReq {
handle_range,
attribute_type,
} => {
handle_range.to_bytes(writer)?;
attribute_type.to_bytes(writer)?;
}
AttPdu::ReadByTypeRsp { length, data_list } => {
writer.write_u8(length)?;
writer.write_slice(data_list.as_ref())?;
}
AttPdu::ReadReq { handle } => {
handle.to_bytes(writer)?;
}
AttPdu::ReadRsp { value } => {
writer.write_slice(value.as_ref())?;
}
AttPdu::ReadBlobReq { handle, offset } => {
handle.to_bytes(writer)?;
writer.write_u16_le(offset)?;
}
AttPdu::ReadBlobRsp { value } => {
writer.write_slice(value.as_ref())?;
}
AttPdu::ReadMultipleReq { handles } => {
writer.write_slice(handles.as_ref())?;
}
AttPdu::ReadMultipleRsp { values } => {
writer.write_slice(values.as_ref())?;
}
AttPdu::ReadByGroupReq {
handle_range,
group_type,
} => {
handle_range.to_bytes(writer)?;
group_type.to_bytes(writer)?;
}
AttPdu::ReadByGroupRsp { length, data_list } => {
writer.write_u8(length)?;
writer.write_slice(data_list.as_ref())?;
}
AttPdu::WriteReq { handle, value } => {
handle.to_bytes(writer)?;
writer.write_slice(value.as_ref())?;
}
AttPdu::WriteRsp => {}
AttPdu::WriteCommand { handle, value } => {
handle.to_bytes(writer)?;
writer.write_slice(value.as_ref())?;
}
AttPdu::SignedWriteCommand {
handle,
value,
signature,
} => {
handle.to_bytes(writer)?;
writer.write_slice(value.as_ref())?;
writer.write_slice(*signature.as_ref())?;
}
AttPdu::PrepareWriteReq {
handle,
offset,
value,
} => {
handle.to_bytes(writer)?;
writer.write_u16_le(offset)?;
writer.write_slice(value.as_ref())?;
}
AttPdu::PrepareWriteRsp {
handle,
offset,
value,
} => {
handle.to_bytes(writer)?;
writer.write_u16_le(offset)?;
writer.write_slice(value.as_ref())?;
}
AttPdu::ExecuteWriteReq { flags } => {
writer.write_u8(flags)?;
}
AttPdu::ExecuteWriteRsp => {}
AttPdu::HandleValueNotification { handle, value } => {
handle.to_bytes(writer)?;
writer.write_slice_truncate(value.as_ref());
}
AttPdu::HandleValueIndication { handle, value } => {
handle.to_bytes(writer)?;
writer.write_slice_truncate(value.as_ref());
}
AttPdu::HandleValueConfirmation => {}
AttPdu::Unknown { opcode: _, params } => {
writer.write_slice(params.as_ref())?;
}
}
Ok(())
}
}
impl AttPdu<'_> {
pub fn opcode(&self) -> Opcode {
match self {
AttPdu::ErrorRsp { .. } => Opcode::ErrorRsp,
AttPdu::ExchangeMtuReq { .. } => Opcode::ExchangeMtuReq,
AttPdu::ExchangeMtuRsp { .. } => Opcode::ExchangeMtuRsp,
AttPdu::ReadByTypeReq { .. } => Opcode::ReadByTypeReq,
AttPdu::ReadByTypeRsp { .. } => Opcode::ReadByTypeRsp,
AttPdu::FindInformationReq { .. } => Opcode::FindInformationReq,
AttPdu::FindInformationRsp { .. } => Opcode::FindInformationRsp,
AttPdu::FindByTypeValueReq { .. } => Opcode::FindByTypeValueReq,
AttPdu::FindByTypeValueRsp { .. } => Opcode::FindByTypeValueRsp,
AttPdu::ReadReq { .. } => Opcode::ReadReq,
AttPdu::ReadRsp { .. } => Opcode::ReadRsp,
AttPdu::ReadBlobReq { .. } => Opcode::ReadBlobReq,
AttPdu::ReadBlobRsp { .. } => Opcode::ReadBlobRsp,
AttPdu::ReadMultipleReq { .. } => Opcode::ReadMultipleReq,
AttPdu::ReadMultipleRsp { .. } => Opcode::ReadMultipleRsp,
AttPdu::ReadByGroupReq { .. } => Opcode::ReadByGroupReq,
AttPdu::ReadByGroupRsp { .. } => Opcode::ReadBlobRsp,
AttPdu::WriteReq { .. } => Opcode::WriteReq,
AttPdu::WriteRsp { .. } => Opcode::WriteRsp,
AttPdu::WriteCommand { .. } => Opcode::WriteCommand,
AttPdu::SignedWriteCommand { .. } => Opcode::SignedWriteCommand,
AttPdu::PrepareWriteReq { .. } => Opcode::PrepareWriteReq,
AttPdu::PrepareWriteRsp { .. } => Opcode::PrepareWriteRsp,
AttPdu::ExecuteWriteReq { .. } => Opcode::ExecuteWriteReq,
AttPdu::ExecuteWriteRsp { .. } => Opcode::ExecuteWriteRsp,
AttPdu::HandleValueNotification { .. } => Opcode::HandleValueNotification,
AttPdu::HandleValueIndication { .. } => Opcode::HandleValueIndication,
AttPdu::HandleValueConfirmation { .. } => Opcode::HandleValueConfirmation,
AttPdu::Unknown { opcode, .. } => *opcode,
}
}
}