1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::BLEConnDesc;
use alloc::{boxed::Box, vec::Vec};
use bitflags::bitflags;
use core::{cell::UnsafeCell, ffi::c_void};
use esp_idf_svc::sys as esp_idf_sys;
use esp_idf_sys::{ble_uuid_any_t, ble_uuid_cmp};

use crate::{
  utilities::{
    ble_npl_hw_enter_critical, ble_npl_hw_exit_critical, mutex::Mutex, os_mbuf_append,
    voidp_to_ref, BleUuid,
  },
  AttValue, OnWriteDescriptorArgs,
};

bitflags! {
  #[repr(transparent)]
  pub struct DescriptorProperties: u8 {
    const READ = esp_idf_sys::BLE_ATT_F_READ as _;
    const READ_ENC = esp_idf_sys::BLE_ATT_F_READ_ENC as _;
    const READ_AUTHEN = esp_idf_sys::BLE_ATT_F_READ_AUTHEN as _;
    const READ_AUTHOR = esp_idf_sys::BLE_ATT_F_READ_AUTHOR  as _;
    const WRITE = esp_idf_sys::BLE_ATT_F_WRITE  as _;
    const WRITE_ENC = esp_idf_sys::BLE_ATT_F_WRITE_ENC as _;
    const WRITE_AUTHEN = esp_idf_sys::BLE_ATT_F_WRITE_AUTHEN  as _;
    const WRITE_AUTHOR = esp_idf_sys::BLE_ATT_F_WRITE_AUTHOR  as _;
  }
}

#[allow(clippy::type_complexity)]
pub struct BLEDescriptor {
  pub(crate) uuid: ble_uuid_any_t,
  pub(crate) properties: DescriptorProperties,
  value: AttValue,
  on_read: Option<Box<dyn FnMut(&mut AttValue, &BLEConnDesc) + Send + Sync>>,
  on_write: Option<Box<dyn FnMut(&mut OnWriteDescriptorArgs) + Send + Sync>>,
}

impl BLEDescriptor {
  pub(super) fn new(uuid: BleUuid, properties: DescriptorProperties) -> Self {
    Self {
      uuid: ble_uuid_any_t::from(uuid),
      properties,
      value: AttValue::new(),
      on_read: None,
      on_write: None,
    }
  }

  pub fn set_value(&mut self, value: &[u8]) -> &mut Self {
    self.value.set_value(value);
    self
  }

  pub fn set_from<T: Sized>(&mut self, value: &T) -> &mut Self {
    self.value.set_from(value);
    self
  }

  pub fn value_mut(&mut self) -> &mut AttValue {
    &mut self.value
  }

  pub fn on_read(
    &mut self,
    callback: impl FnMut(&mut AttValue, &BLEConnDesc) + Send + Sync + 'static,
  ) -> &mut Self {
    self.on_read = Some(Box::new(callback));
    self
  }

  pub fn on_write(
    &mut self,
    callback: impl FnMut(&mut OnWriteDescriptorArgs) + Send + Sync + 'static,
  ) -> &mut Self {
    self.on_write = Some(Box::new(callback));
    self
  }

  pub(super) extern "C" fn handle_gap_event(
    conn_handle: u16,
    _attr_handle: u16,
    ctxt: *mut esp_idf_sys::ble_gatt_access_ctxt,
    arg: *mut c_void,
  ) -> i32 {
    let ctxt = unsafe { &*ctxt };

    let mutex = unsafe { voidp_to_ref::<Mutex<Self>>(arg) };
    let mut descriptor = mutex.lock();

    if unsafe { ble_uuid_cmp((*ctxt.__bindgen_anon_1.chr).uuid, &descriptor.uuid.u) != 0 } {
      return esp_idf_sys::BLE_ATT_ERR_UNLIKELY as _;
    }

    match ctxt.op as _ {
      esp_idf_sys::BLE_GATT_ACCESS_OP_READ_DSC => {
        let desc = crate::utilities::ble_gap_conn_find(conn_handle).unwrap();

        unsafe {
          if (*(ctxt.om)).om_pkthdr_len > 8 || descriptor.value.len() <= (desc.mtu() - 3) as _ {
            let descriptor = UnsafeCell::new(&mut descriptor);
            if let Some(callback) = &mut (*descriptor.get()).on_read {
              callback(&mut (*descriptor.get()).value, &desc);
            }
          }
        }

        ble_npl_hw_enter_critical();
        let value = descriptor.value.value();
        let rc = os_mbuf_append(ctxt.om, value);
        ble_npl_hw_exit_critical();
        if rc == 0 {
          0
        } else {
          esp_idf_sys::BLE_ATT_ERR_INSUFFICIENT_RES as _
        }
      }
      esp_idf_sys::BLE_GATT_ACCESS_OP_WRITE_DSC => {
        let mut buf = Vec::with_capacity(esp_idf_sys::BLE_ATT_ATTR_MAX_LEN as _);
        let mut om = ctxt.om;
        while !om.is_null() {
          let slice = unsafe { core::slice::from_raw_parts((*om).om_data, (*om).om_len as _) };
          buf.extend_from_slice(slice);
          om = unsafe { (*om).om_next.sle_next };
        }

        unsafe {
          let descriptor = UnsafeCell::new(&mut descriptor);
          if let Some(callback) = &mut (*descriptor.get()).on_write {
            let desc = crate::utilities::ble_gap_conn_find(conn_handle).unwrap();
            let mut arg = OnWriteDescriptorArgs {
              current_data: (*descriptor.get()).value.value(),
              recv_data: &buf,
              desc: &desc,
              reject: false,
              error_code: 0,
            };
            callback(&mut arg);

            if arg.reject {
              return arg.error_code as _;
            }
          }
        }
        descriptor.set_value(&buf);

        0
      }
      _ => esp_idf_sys::BLE_ATT_ERR_UNLIKELY as _,
    }
  }
}