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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! Notification support.
//!
//! # Background
//!
//! The notification system is used as a kind of interrupt-based mechanism when
//! receiving data from the device. Once an endpoint is enabled for notifications,
//! the driver will send a notification to the host when data is received on that
//! endpoint, which in turn invokes a user-defined callback. The notification
//! contains the number of bytes received, the endpoint that data was received on,
//! and an optional context value that can be set by the user.
//!
//! The notification system is ideal for endpoints sending short messages, such as
//! start/stop signals or status updates. It is not intended for endpoints that
//! send large amounts of data, such as a video stream.
//!
//! # Example
//!
//! A simple use case might look like this:
//!
//! ```no_run
//! use std::io::Read;
//! use std::sync::{Arc, Mutex};
//!
//! use d3xx::Device;
//! use d3xx::notification::{Notification, NotificationData};
//!
//! fn callback<'a>(notification: Notification<Arc<Mutex<Device>>>) {
//! match notification.data() {
//! NotificationData::Data { endpoint, size } => {
//! let mut buf = vec![0; *size];
//! let device = notification
//! .context()
//! .unwrap()
//! .lock()
//! .unwrap();
//! device.
//! pipe(*endpoint)
//! .read(&mut buf)
//! .unwrap();
//! println!("Data: {:?}", buf);
//! }
//! NotificationData::Gpio { gpio0, gpio1 } => {
//! println!("GPIO0: {gpio0}, GPIO1: {gpio1}");
//! }
//! }
//! }
//!
//! let device = Device::open("serial number").unwrap();
//! let device = Arc::new(Mutex::new(device));
//! device
//! .lock()
//! .unwrap()
//! .set_notification_callback(callback, Some(device.clone()))
//! .unwrap();
use c_void;
use crate::;
/// Type alias for notification callback functions.
///
/// A notification callback is a function that takes a [`Notification<T>`] as its only argument.
///
/// See [`Notification`] for more information and safety tips.
pub type NotificationCallback<T> = dyn Fn + 'static;
/// Information regarding a notification sent by a device.
///
/// [Notification callbacks](NotificationCallback) are called using a [`Notification`] as their
/// only argument. This struct contains the context provided by the user when setting the
/// callback, as well as the notification data sent by the device.
///
/// # Thread Safety
///
/// Any type can be used as the context as long as it is `Sync`. This constraint is necessary
/// because the callback is not guaranteed to be called on the same thread as the one that set
/// the callback, and because unwinding across the FFI boundary is undefined behavior.
///
/// It is also highly discouraged to panic in the callback, as this will cause the panic to be
/// propagated across the FFI boundary and back to the D3XX library. This is undefined behavior
/// and may cause crashes or a number of other wonderful things. This may be changed in the
/// future ([issue](https://github.com/mtmk-ee/d3xx/issues/4))
/// Notification callback context used internally.
///
/// This struct is used to provide [`trampoline`] with the necessary information to call the
/// user-provided callback. It allows users to set closures as callbacks, as well as use
/// arbitrary types as context. Otherwise, a rigid API using function pointers would be
/// required.
/// Data associated with a [`Notification`].
///
/// Two variants are defined: `Data` and `Gpio`. The `Data` variant is used when
/// data is received on an endpoint, while the `Gpio` variant is used when the
/// state of the GPIO pins changes. Note that to receive either variant the
/// corresponding endpoint or GPIO pins must be enabled for notifications.
/// Set a notification callback.
///
/// Internally this function registers a separate "trampoline" callback with the driver to
/// support different `T` parameters. The trampoline callback is responsible for calling the
/// user-provided callback with the correct types. For this, a struct containing extra
/// information is leaked to provide the trampoline with the necessary information.
///
/// # Warning
///
/// It is unknown whether the D3XX driver releases the leaked memory when the callback is
/// cleared because the documentation does not specify this. For now it is assumed that the
/// memory is released when the callback is cleared/changed. Until this is confirmed, it is
/// recommended to only set the callback smaller number of times, and with a `T` that is small
/// enough to not cause memory issues.
pub
/// Clear the notification callback.
///
/// See the concerns about this in [`set_notification_callback`].
///
/// Note that this function is infallible, and it is unclear why due to incorrect
/// documentation. On one hand the documentation says that the foreign function returns
/// `FT_STATUS`, but on the other hand the header/bindings indicate that nothing is returned.
pub unsafe
/// Trampoline callback used to call the user-provided callback.
///
/// This function expects that the `callback_context` is a pointer to an [`InternalContext`] with
/// the same `T` parameter.
///
/// # Panics
///
/// This function will never panic, but the user-provided callback may panic. If this happens,
/// the panic will be caught and printed to stderr. It is not possible to propagate the panic
/// across the FFI boundary.
///
/// # Safety
///
/// This function is unsafe because it dereferences raw pointers and casts
/// between different types. To avoid undefined behavior, the caller must ensure
/// the following:
///
/// - `callback_context` is a valid pointer to an [`InternalContext<T>`] and `T` is correct.
/// - `callback_info` matches the corresponding `callback_type`.
///
/// Additionally, care should be taken to avoid panicking in the callback function as unwinding
/// across the FFI boundary is undefined behavior.
unsafe extern "C"
/// Casts the callback info to the correct [`NotificationData`] variant.
///
/// # Safety
///
/// This function is unsafe because it dereferences raw pointers and casts
/// between different types. To avoid undefined behavior, the caller must ensure
/// that the `callback_info` is a valid object of the correct notification type.
unsafe