1 | //! Blocking SPI master mode traits. |
2 | //! |
3 | //! # Bus vs Device |
4 | //! |
5 | //! SPI allows sharing a single bus between many SPI devices. The SCK, MOSI and MISO lines are |
6 | //! wired in parallel to all the devices, and each device gets a dedicated chip-select (CS) line from the MCU, like this: |
7 | //! |
8 | #![doc = include_str!("spi-shared-bus.svg" )] |
9 | //! |
10 | //! CS is usually active-low. When CS is high (not asserted), SPI devices ignore all incoming data, and |
11 | //! don't drive MISO. When CS is low (asserted), the device is active: reacts to incoming data on MOSI and |
12 | //! drives MISO with the response data. By asserting one CS or another, the MCU can choose to which |
13 | //! SPI device it "talks" to on the (possibly shared) bus. |
14 | //! |
15 | //! This bus sharing is common when having multiple SPI devices in the same board, since it uses fewer MCU |
16 | //! pins (`n+3` instead of `4*n`), and fewer MCU SPI peripherals (`1` instead of `n`). |
17 | //! |
18 | //! However, it poses a challenge when building portable drivers for SPI devices. The driver needs to |
19 | //! be able to talk to its device on the bus, while not interfering with other drivers talking to other |
20 | //! devices. |
21 | //! |
22 | //! To solve this, `embedded-hal` has two kinds of SPI traits: **SPI bus** and **SPI device**. |
23 | //! |
24 | //! ## Bus |
25 | //! |
26 | //! The [`SpiBus`] trait represents **exclusive ownership** over the whole SPI bus. This is usually the entire |
27 | //! SPI MCU peripheral, plus the SCK, MOSI and MISO pins. |
28 | //! |
29 | //! Owning an instance of an SPI bus guarantees exclusive access, this is, we have the guarantee no other |
30 | //! piece of code will try to use the bus while we own it. |
31 | //! |
32 | //! ## Device |
33 | //! |
34 | //! The [`SpiDevice`] trait represents **ownership over a single SPI device selected by a CS pin** in a (possibly shared) bus. This is typically: |
35 | //! |
36 | //! - Exclusive ownership of the **CS pin**. |
37 | //! - Access to the **underlying SPI bus**. If shared, it'll be behind some kind of lock/mutex. |
38 | //! |
39 | //! An [`SpiDevice`] allows initiating [transactions](SpiDevice::transaction) against the target device on the bus. A transaction |
40 | //! consists of asserting CS, then doing one or more transfers, then deasserting CS. For the entire duration of the transaction, the [`SpiDevice`] |
41 | //! implementation will ensure no other transaction can be opened on the same bus. This is the key that allows correct sharing of the bus. |
42 | //! |
43 | //! # For driver authors |
44 | //! |
45 | //! When implementing a driver, it's crucial to pick the right trait, to ensure correct operation |
46 | //! with maximum interoperability. Here are some guidelines depending on the device you're implementing a driver for: |
47 | //! |
48 | //! If your device **has a CS pin**, use [`SpiDevice`]. Do not manually |
49 | //! manage the CS pin, the [`SpiDevice`] implementation will do it for you. |
50 | //! By using [`SpiDevice`], your driver will cooperate nicely with other drivers for other devices in the same shared SPI bus. |
51 | //! |
52 | //! ``` |
53 | //! # use embedded_hal::spi::{SpiBus, SpiDevice, Operation}; |
54 | //! pub struct MyDriver<SPI> { |
55 | //! spi: SPI, |
56 | //! } |
57 | //! |
58 | //! impl<SPI> MyDriver<SPI> |
59 | //! where |
60 | //! SPI: SpiDevice, |
61 | //! { |
62 | //! pub fn new(spi: SPI) -> Self { |
63 | //! Self { spi } |
64 | //! } |
65 | //! |
66 | //! pub fn read_foo(&mut self) -> Result<[u8; 2], MyError<SPI::Error>> { |
67 | //! let mut buf = [0; 2]; |
68 | //! |
69 | //! // `transaction` asserts and deasserts CS for us. No need to do it manually! |
70 | //! self.spi.transaction(&mut [ |
71 | //! Operation::Write(&[0x90]), |
72 | //! Operation::Read(&mut buf), |
73 | //! ]).map_err(MyError::Spi)?; |
74 | //! |
75 | //! Ok(buf) |
76 | //! } |
77 | //! } |
78 | //! |
79 | //! #[derive(Copy, Clone, Debug)] |
80 | //! enum MyError<SPI> { |
81 | //! Spi(SPI), |
82 | //! // Add other errors for your driver here. |
83 | //! } |
84 | //! ``` |
85 | //! |
86 | //! If your device **does not have a CS pin**, use [`SpiBus`]. This will ensure |
87 | //! your driver has exclusive access to the bus, so no other drivers can interfere. It's not possible to safely share |
88 | //! a bus without CS pins. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation. |
89 | //! |
90 | //! ``` |
91 | //! # use embedded_hal::spi::SpiBus; |
92 | //! pub struct MyDriver<SPI> { |
93 | //! spi: SPI, |
94 | //! } |
95 | //! |
96 | //! impl<SPI> MyDriver<SPI> |
97 | //! where |
98 | //! SPI: SpiBus, |
99 | //! { |
100 | //! pub fn new(spi: SPI) -> Self { |
101 | //! Self { spi } |
102 | //! } |
103 | //! |
104 | //! pub fn read_foo(&mut self) -> Result<[u8; 2], MyError<SPI::Error>> { |
105 | //! let mut buf = [0; 2]; |
106 | //! self.spi.write(&[0x90]).map_err(MyError::Spi)?; |
107 | //! self.spi.read(&mut buf).map_err(MyError::Spi)?; |
108 | //! Ok(buf) |
109 | //! } |
110 | //! } |
111 | //! |
112 | //! #[derive(Copy, Clone, Debug)] |
113 | //! enum MyError<SPI> { |
114 | //! Spi(SPI), |
115 | //! // Add other errors for your driver here. |
116 | //! } |
117 | //! ``` |
118 | //! |
119 | //! If you're (ab)using SPI to **implement other protocols** by bitbanging (WS2812B, onewire, generating arbitrary waveforms...), use [`SpiBus`]. |
120 | //! SPI bus sharing doesn't make sense at all in this case. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation. |
121 | //! |
122 | //! # For HAL authors |
123 | //! |
124 | //! HALs **must** implement [`SpiBus`]. Users can combine the bus together with the CS pin (which should |
125 | //! implement [`OutputPin`](crate::digital::OutputPin)) using HAL-independent [`SpiDevice`] implementations such as the ones in [`embedded-hal-bus`](https://crates.io/crates/embedded-hal-bus). |
126 | //! |
127 | //! HALs may additionally implement [`SpiDevice`] to **take advantage of hardware CS management**, which may provide some performance |
128 | //! benefits. (There's no point in a HAL implementing [`SpiDevice`] if the CS management is software-only, this task is better left to |
129 | //! the HAL-independent implementations). |
130 | //! |
131 | //! HALs **must not** add infrastructure for sharing at the [`SpiBus`] level. User code owning a [`SpiBus`] must have the guarantee |
132 | //! of exclusive access. |
133 | //! |
134 | //! # Flushing |
135 | //! |
136 | //! To improve performance, [`SpiBus`] implementations are allowed to return before the operation is finished, i.e. when the bus is still not |
137 | //! idle. This allows pipelining SPI transfers with CPU work. |
138 | //! |
139 | //! When calling another method when a previous operation is still in progress, implementations can either wait for the previous operation |
140 | //! to finish, or enqueue the new one, but they must not return a "busy" error. Users must be able to do multiple method calls in a row |
141 | //! and have them executed "as if" they were done sequentially, without having to check for "busy" errors. |
142 | //! |
143 | //! When using a [`SpiBus`], call [`flush`](SpiBus::flush) to wait for operations to actually finish. Examples of situations |
144 | //! where this is needed are: |
145 | //! - To synchronize SPI activity and GPIO activity, for example before deasserting a CS pin. |
146 | //! - Before deinitializing the hardware SPI peripheral. |
147 | //! |
148 | //! When using a [`SpiDevice`], you can still call [`flush`](SpiBus::flush) on the bus within a transaction. |
149 | //! It's very rarely needed, because [`transaction`](SpiDevice::transaction) already flushes for you |
150 | //! before deasserting CS. For example, you may need it to synchronize with GPIOs other than CS, such as DCX pins |
151 | //! sometimes found in SPI displays. |
152 | //! |
153 | //! For example, for [`write`](SpiBus::write) operations, it is common for hardware SPI peripherals to have a small |
154 | //! FIFO buffer, usually 1-4 bytes. Software writes data to the FIFO, and the peripheral sends it on MOSI at its own pace, |
155 | //! at the specified SPI frequency. It is allowed for an implementation of [`write`](SpiBus::write) to return as soon |
156 | //! as all the data has been written to the FIFO, before it is actually sent. Calling [`flush`](SpiBus::flush) would |
157 | //! wait until all the bits have actually been sent, the FIFO is empty, and the bus is idle. |
158 | //! |
159 | //! This still applies to other operations such as [`read`](SpiBus::read) or [`transfer`](SpiBus::transfer). It is less obvious |
160 | //! why, because these methods can't return before receiving all the read data. However it's still technically possible |
161 | //! for them to return before the bus is idle. For example, assuming SPI mode 0, the last bit is sampled on the first (rising) edge |
162 | //! of SCK, at which point a method could return, but the second (falling) SCK edge still has to happen before the bus is idle. |
163 | //! |
164 | //! # CS-to-clock delays |
165 | //! |
166 | //! Many chips require a minimum delay between asserting CS and the first SCK edge, and the last SCK edge and deasserting CS. |
167 | //! Drivers should *NOT* use [`Operation::DelayNs`] for this, they should instead document that the user should configure the |
168 | //! delays when creating the `SpiDevice` instance, same as they have to configure the SPI frequency and mode. This has a few advantages: |
169 | //! |
170 | //! - Allows implementations that use hardware-managed CS to program the delay in hardware |
171 | //! - Allows the end user more flexibility. For example, they can choose to not configure any delay if their MCU is slow |
172 | //! enough to "naturally" do the delay (very common if the delay is in the order of nanoseconds). |
173 | |
174 | use core::fmt::Debug; |
175 | |
176 | #[cfg (feature = "defmt-03" )] |
177 | use crate::defmt; |
178 | |
179 | /// Clock polarity. |
180 | #[derive (Clone, Copy, Debug, PartialEq, Eq)] |
181 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
182 | pub enum Polarity { |
183 | /// Clock signal low when idle. |
184 | IdleLow, |
185 | /// Clock signal high when idle. |
186 | IdleHigh, |
187 | } |
188 | |
189 | /// Clock phase. |
190 | #[derive (Clone, Copy, Debug, PartialEq, Eq)] |
191 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
192 | pub enum Phase { |
193 | /// Data in "captured" on the first clock transition. |
194 | CaptureOnFirstTransition, |
195 | /// Data in "captured" on the second clock transition. |
196 | CaptureOnSecondTransition, |
197 | } |
198 | |
199 | /// SPI mode. |
200 | #[derive (Clone, Copy, Debug, PartialEq, Eq)] |
201 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
202 | pub struct Mode { |
203 | /// Clock polarity. |
204 | pub polarity: Polarity, |
205 | /// Clock phase. |
206 | pub phase: Phase, |
207 | } |
208 | |
209 | /// Helper for CPOL = 0, CPHA = 0. |
210 | pub const MODE_0: Mode = Mode { |
211 | polarity: Polarity::IdleLow, |
212 | phase: Phase::CaptureOnFirstTransition, |
213 | }; |
214 | |
215 | /// Helper for CPOL = 0, CPHA = 1. |
216 | pub const MODE_1: Mode = Mode { |
217 | polarity: Polarity::IdleLow, |
218 | phase: Phase::CaptureOnSecondTransition, |
219 | }; |
220 | |
221 | /// Helper for CPOL = 1, CPHA = 0. |
222 | pub const MODE_2: Mode = Mode { |
223 | polarity: Polarity::IdleHigh, |
224 | phase: Phase::CaptureOnFirstTransition, |
225 | }; |
226 | |
227 | /// Helper for CPOL = 1, CPHA = 1. |
228 | pub const MODE_3: Mode = Mode { |
229 | polarity: Polarity::IdleHigh, |
230 | phase: Phase::CaptureOnSecondTransition, |
231 | }; |
232 | |
233 | /// SPI error. |
234 | pub trait Error: Debug { |
235 | /// Convert error to a generic SPI error kind. |
236 | /// |
237 | /// By using this method, SPI errors freely defined by HAL implementations |
238 | /// can be converted to a set of generic SPI errors upon which generic |
239 | /// code can act. |
240 | fn kind(&self) -> ErrorKind; |
241 | } |
242 | |
243 | impl Error for core::convert::Infallible { |
244 | #[inline ] |
245 | fn kind(&self) -> ErrorKind { |
246 | match *self {} |
247 | } |
248 | } |
249 | |
250 | /// SPI error kind. |
251 | /// |
252 | /// This represents a common set of SPI operation errors. HAL implementations are |
253 | /// free to define more specific or additional error types. However, by providing |
254 | /// a mapping to these common SPI errors, generic code can still react to them. |
255 | #[derive (Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
256 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
257 | #[non_exhaustive ] |
258 | pub enum ErrorKind { |
259 | /// The peripheral receive buffer was overrun. |
260 | Overrun, |
261 | /// Multiple devices on the SPI bus are trying to drive the slave select pin, e.g. in a multi-master setup. |
262 | ModeFault, |
263 | /// Received data does not conform to the peripheral configuration. |
264 | FrameFormat, |
265 | /// An error occurred while asserting or deasserting the Chip Select pin. |
266 | ChipSelectFault, |
267 | /// A different error occurred. The original error may contain more information. |
268 | Other, |
269 | } |
270 | |
271 | impl Error for ErrorKind { |
272 | #[inline ] |
273 | fn kind(&self) -> ErrorKind { |
274 | *self |
275 | } |
276 | } |
277 | |
278 | impl core::fmt::Display for ErrorKind { |
279 | #[inline ] |
280 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
281 | match self { |
282 | Self::Overrun => write!(f, "The peripheral receive buffer was overrun" ), |
283 | Self::ModeFault => write!( |
284 | f, |
285 | "Multiple devices on the SPI bus are trying to drive the slave select pin" |
286 | ), |
287 | Self::FrameFormat => write!( |
288 | f, |
289 | "Received data does not conform to the peripheral configuration" |
290 | ), |
291 | Self::ChipSelectFault => write!( |
292 | f, |
293 | "An error occurred while asserting or deasserting the Chip Select pin" |
294 | ), |
295 | Self::Other => write!( |
296 | f, |
297 | "A different error occurred. The original error may contain more information" |
298 | ), |
299 | } |
300 | } |
301 | } |
302 | |
303 | /// SPI error type trait. |
304 | /// |
305 | /// This just defines the error type, to be used by the other SPI traits. |
306 | pub trait ErrorType { |
307 | /// Error type. |
308 | type Error: Error; |
309 | } |
310 | |
311 | impl<T: ErrorType + ?Sized> ErrorType for &mut T { |
312 | type Error = T::Error; |
313 | } |
314 | |
315 | /// SPI transaction operation. |
316 | /// |
317 | /// This allows composition of SPI operations into a single bus transaction. |
318 | #[derive (Debug, PartialEq, Eq)] |
319 | #[cfg_attr (feature = "defmt-03" , derive(defmt::Format))] |
320 | pub enum Operation<'a, Word: 'static> { |
321 | /// Read data into the provided buffer. |
322 | /// |
323 | /// Equivalent to [`SpiBus::read`]. |
324 | Read(&'a mut [Word]), |
325 | /// Write data from the provided buffer, discarding read data. |
326 | /// |
327 | /// Equivalent to [`SpiBus::write`]. |
328 | Write(&'a [Word]), |
329 | /// Read data into the first buffer, while writing data from the second buffer. |
330 | /// |
331 | /// Equivalent to [`SpiBus::transfer`]. |
332 | Transfer(&'a mut [Word], &'a [Word]), |
333 | /// Write data out while reading data into the provided buffer. |
334 | /// |
335 | /// Equivalent to [`SpiBus::transfer_in_place`]. |
336 | TransferInPlace(&'a mut [Word]), |
337 | /// Delay for at least the specified number of nanoseconds. |
338 | DelayNs(u32), |
339 | } |
340 | |
341 | /// SPI device trait. |
342 | /// |
343 | /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected |
344 | /// with a CS (Chip Select) pin. |
345 | /// |
346 | /// See the [module-level documentation](self) for important usage information. |
347 | pub trait SpiDevice<Word: Copy + 'static = u8>: ErrorType { |
348 | /// Perform a transaction against the device. |
349 | /// |
350 | /// - Locks the bus |
351 | /// - Asserts the CS (Chip Select) pin. |
352 | /// - Performs all the operations. |
353 | /// - [Flushes](SpiBus::flush) the bus. |
354 | /// - Deasserts the CS pin. |
355 | /// - Unlocks the bus. |
356 | /// |
357 | /// The locking mechanism is implementation-defined. The only requirement is it must prevent two |
358 | /// transactions from executing concurrently against the same bus. Examples of implementations are: |
359 | /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. |
360 | /// |
361 | /// On bus errors the implementation should try to deassert CS. |
362 | /// If an error occurs while deasserting CS the bus error should take priority as the return value. |
363 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error>; |
364 | |
365 | /// Do a read within a transaction. |
366 | /// |
367 | /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Read(buf)])`. |
368 | /// |
369 | /// See also: [`SpiDevice::transaction`], [`SpiBus::read`] |
370 | #[inline ] |
371 | fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { |
372 | self.transaction(&mut [Operation::Read(buf)]) |
373 | } |
374 | |
375 | /// Do a write within a transaction. |
376 | /// |
377 | /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Write(buf)])`. |
378 | /// |
379 | /// See also: [`SpiDevice::transaction`], [`SpiBus::write`] |
380 | #[inline ] |
381 | fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { |
382 | self.transaction(&mut [Operation::Write(buf)]) |
383 | } |
384 | |
385 | /// Do a transfer within a transaction. |
386 | /// |
387 | /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)]`. |
388 | /// |
389 | /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] |
390 | #[inline ] |
391 | fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { |
392 | self.transaction(&mut [Operation::Transfer(read, write)]) |
393 | } |
394 | |
395 | /// Do an in-place transfer within a transaction. |
396 | /// |
397 | /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)]`. |
398 | /// |
399 | /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] |
400 | #[inline ] |
401 | fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { |
402 | self.transaction(&mut [Operation::TransferInPlace(buf)]) |
403 | } |
404 | } |
405 | |
406 | impl<Word: Copy + 'static, T: SpiDevice<Word> + ?Sized> SpiDevice<Word> for &mut T { |
407 | #[inline ] |
408 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { |
409 | T::transaction(self, operations) |
410 | } |
411 | |
412 | #[inline ] |
413 | fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { |
414 | T::read(self, buf) |
415 | } |
416 | |
417 | #[inline ] |
418 | fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { |
419 | T::write(self, buf) |
420 | } |
421 | |
422 | #[inline ] |
423 | fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { |
424 | T::transfer(self, read, write) |
425 | } |
426 | |
427 | #[inline ] |
428 | fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { |
429 | T::transfer_in_place(self, buf) |
430 | } |
431 | } |
432 | |
433 | /// SPI bus. |
434 | /// |
435 | /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. |
436 | /// |
437 | /// See the [module-level documentation](self) for important information on SPI Bus vs Device traits. |
438 | pub trait SpiBus<Word: Copy + 'static = u8>: ErrorType { |
439 | /// Read `words` from the slave. |
440 | /// |
441 | /// The word value sent on MOSI during reading is implementation-defined, |
442 | /// typically `0x00`, `0xFF`, or configurable. |
443 | /// |
444 | /// Implementations are allowed to return before the operation is |
445 | /// complete. See the [module-level documentation](self) for details. |
446 | fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; |
447 | |
448 | /// Write `words` to the slave, ignoring all the incoming words. |
449 | /// |
450 | /// Implementations are allowed to return before the operation is |
451 | /// complete. See the [module-level documentation](self) for details. |
452 | fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; |
453 | |
454 | /// Write and read simultaneously. `write` is written to the slave on MOSI and |
455 | /// words received on MISO are stored in `read`. |
456 | /// |
457 | /// It is allowed for `read` and `write` to have different lengths, even zero length. |
458 | /// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter, |
459 | /// incoming words after `read` has been filled will be discarded. If `write` is shorter, |
460 | /// the value of words sent in MOSI after all `write` has been sent is implementation-defined, |
461 | /// typically `0x00`, `0xFF`, or configurable. |
462 | /// |
463 | /// Implementations are allowed to return before the operation is |
464 | /// complete. See the [module-level documentation](self) for details. |
465 | fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; |
466 | |
467 | /// Write and read simultaneously. The contents of `words` are |
468 | /// written to the slave, and the received words are stored into the same |
469 | /// `words` buffer, overwriting it. |
470 | /// |
471 | /// Implementations are allowed to return before the operation is |
472 | /// complete. See the [module-level documentation](self) for details. |
473 | fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; |
474 | |
475 | /// Wait until all operations have completed and the bus is idle. |
476 | /// |
477 | /// See the [module-level documentation](self) for important usage information. |
478 | fn flush(&mut self) -> Result<(), Self::Error>; |
479 | } |
480 | |
481 | impl<T: SpiBus<Word> + ?Sized, Word: Copy + 'static> SpiBus<Word> for &mut T { |
482 | #[inline ] |
483 | fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { |
484 | T::read(self, words) |
485 | } |
486 | |
487 | #[inline ] |
488 | fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { |
489 | T::write(self, words) |
490 | } |
491 | |
492 | #[inline ] |
493 | fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { |
494 | T::transfer(self, read, write) |
495 | } |
496 | |
497 | #[inline ] |
498 | fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { |
499 | T::transfer_in_place(self, words) |
500 | } |
501 | |
502 | #[inline ] |
503 | fn flush(&mut self) -> Result<(), Self::Error> { |
504 | T::flush(self) |
505 | } |
506 | } |
507 | |