33//! [`embedded-hal`]: https://docs.rs/embedded-hal
44
55use std:: fmt;
6+ use std:: marker:: PhantomData ;
67use std:: path:: Path ;
78
8- use embedded_hal:: digital:: InputPin ;
9+ use embedded_hal:: digital:: { Error , ErrorType , InputPin , OutputPin , PinState , StatefulOutputPin } ;
10+ use gpiocdev:: {
11+ line:: { Config , Direction , Offset , Value } ,
12+ request:: Request ,
13+ } ;
914#[ cfg( feature = "async-tokio" ) ]
1015use gpiocdev:: {
1116 line:: { EdgeDetection , EdgeKind } ,
1217 tokio:: AsyncRequest ,
1318} ;
14- use gpiocdev:: {
15- line:: { Offset , Value } ,
16- request:: { Config , Request } ,
17- } ;
19+
20+ /// Marker type for a [`CdevPin`] in input mode.
21+ #[ non_exhaustive]
22+ pub struct Input ;
23+
24+ /// Marker type for a [`CdevPin`] in output mode.
25+ #[ non_exhaustive]
26+ pub struct Output ;
1827
1928/// Wrapper around [`gpiocdev::request::Request`] that implements the `embedded-hal` traits.
2029#[ derive( Debug ) ]
21- pub struct CdevPin {
30+ pub struct CdevPin < MODE > {
2231 #[ cfg( not( feature = "async-tokio" ) ) ]
2332 req : Request ,
2433 #[ cfg( feature = "async-tokio" ) ]
2534 req : AsyncRequest ,
2635 line : Offset ,
36+ line_config : Config ,
37+ mode : PhantomData < MODE > ,
2738}
2839
29- impl CdevPin {
30- /// Creates a new pin for the given `line` on the given `chip`.
40+ impl CdevPin < Input > {
41+ /// Creates a new input pin for the given `line` on the given `chip`.
3142 ///
3243 /// ```
3344 /// use linux_embedded_hal::CdevPin;
3445 /// # use linux_embedded_hal::CdevPinError;
3546 ///
3647 /// # fn main() -> Result<(), CdevPinError> {
37- /// let mut pin = CdevPin::new("/dev/gpiochip0", 4)?.into_output_pin()?;
48+ /// let mut pin = CdevPin::new_input("/dev/gpiochip0", 4)?;
49+ /// pin.is_high()?;
50+ /// # }
51+ /// ```
52+ pub fn new_input < P > ( chip : P , line : u32 ) -> Result < Self , CdevPinError >
53+ where
54+ P : AsRef < Path > ,
55+ {
56+ let line_config = Config {
57+ direction : Some ( Direction :: Input ) ,
58+ ..Default :: default ( )
59+ } ;
60+
61+ let req = Request :: builder ( )
62+ . on_chip ( chip. as_ref ( ) )
63+ . from_line_config ( & line_config)
64+ . request ( ) ?;
65+
66+ #[ cfg( feature = "async-tokio" ) ]
67+ let req = AsyncRequest :: new ( req) ;
68+
69+ Ok ( Self {
70+ req,
71+ line,
72+ line_config,
73+ mode : PhantomData ,
74+ } )
75+ }
76+ }
77+
78+ impl CdevPin < Output > {
79+ /// Creates a new output pin for the given `line` on the given `chip`,
80+ /// initialized with the given `initial_state`.
81+ ///
82+ /// ```
83+ /// use linux_embedded_hal::CdevPin;
84+ /// # use linux_embedded_hal::CdevPinError;
85+ ///
86+ /// # fn main() -> Result<(), CdevPinError> {
87+ /// let mut pin = CdevPin::new_output("/dev/gpiochip0", 4)?;
88+ /// pin.is_set_high()?;
3889 /// pin.set_high()?;
3990 /// # }
4091 /// ```
41- pub fn new < P > ( chip : P , line : u32 ) -> Result < Self , CdevPinError >
92+ pub fn new_output < P > ( chip : P , line : u32 , initial_state : PinState ) -> Result < Self , CdevPinError >
4293 where
4394 P : AsRef < Path > ,
4495 {
96+ let line_config = Config {
97+ direction : Some ( Direction :: Output ) ,
98+ active_low : false ,
99+ value : Some ( match initial_state {
100+ PinState :: High => Value :: Active ,
101+ PinState :: Low => Value :: Inactive ,
102+ } ) ,
103+ ..Default :: default ( )
104+ } ;
105+
45106 let req = Request :: builder ( )
46107 . on_chip ( chip. as_ref ( ) )
47- . with_line ( line )
108+ . from_line_config ( & line_config )
48109 . request ( ) ?;
49110
50111 #[ cfg( feature = "async-tokio" ) ]
51112 let req = AsyncRequest :: new ( req) ;
52113
53- Ok ( Self { req, line } )
114+ Ok ( Self {
115+ req,
116+ line,
117+ line_config,
118+ mode : PhantomData ,
119+ } )
54120 }
121+ }
55122
123+ impl < MODE > CdevPin < MODE > {
56124 #[ inline]
57125 fn request ( & self ) -> & Request {
58126 #[ cfg( not( feature = "async-tokio" ) ) ]
@@ -66,58 +134,19 @@ impl CdevPin {
66134 }
67135 }
68136
69- fn config ( & self ) -> Config {
70- self . request ( ) . config ( )
71- }
72-
73- /// Set this pin to input mode
74- pub fn into_input_pin ( self ) -> Result < CdevPin , CdevPinError > {
75- let config = self . config ( ) ;
76- let line_config = config. line_config ( self . line ) . unwrap ( ) ;
77-
78- if line_config. direction == Some ( gpiocdev:: line:: Direction :: Input ) {
79- return Ok ( self ) ;
80- }
81-
82- let mut new_config = config;
83- new_config. as_input ( ) ;
84- self . request ( ) . reconfigure ( & new_config) ?;
85-
86- Ok ( self )
87- }
88-
89- /// Set this pin to output mode
90- pub fn into_output_pin (
91- self ,
92- state : embedded_hal:: digital:: PinState ,
93- ) -> Result < CdevPin , CdevPinError > {
94- let config = self . config ( ) ;
95- let line_config = config. line_config ( self . line ) . unwrap ( ) ;
96- if line_config. direction == Some ( gpiocdev:: line:: Direction :: Output ) {
97- return Ok ( self ) ;
98- }
99- let is_active_low = line_config. active_low ;
100-
101- let mut new_config = config;
102- new_config. as_output ( state_to_value ( state, is_active_low) ) ;
103- self . request ( ) . reconfigure ( & new_config) ?;
104-
105- Ok ( self )
106- }
107- }
108-
109- /// Converts a pin state to the gpio_cdev compatible numeric value, accounting
110- /// for the active_low condition.
111- fn state_to_value ( state : embedded_hal:: digital:: PinState , is_active_low : bool ) -> Value {
112- if is_active_low {
113- match state {
114- embedded_hal:: digital:: PinState :: High => Value :: Inactive ,
115- embedded_hal:: digital:: PinState :: Low => Value :: Active ,
116- }
117- } else {
118- match state {
119- embedded_hal:: digital:: PinState :: High => Value :: Active ,
120- embedded_hal:: digital:: PinState :: Low => Value :: Inactive ,
137+ /// Converts a pin state to a value, depending on
138+ /// whether the pin is configured as active-low.
139+ fn state_to_value ( & self , state : PinState ) -> Value {
140+ if self . line_config . active_low {
141+ match state {
142+ PinState :: High => Value :: Inactive ,
143+ PinState :: Low => Value :: Active ,
144+ }
145+ } else {
146+ match state {
147+ PinState :: High => Value :: Active ,
148+ PinState :: Low => Value :: Inactive ,
149+ }
121150 }
122151 }
123152}
@@ -146,60 +175,65 @@ impl std::error::Error for CdevPinError {
146175 }
147176}
148177
149- impl embedded_hal :: digital :: Error for CdevPinError {
178+ impl Error for CdevPinError {
150179 fn kind ( & self ) -> embedded_hal:: digital:: ErrorKind {
151180 use embedded_hal:: digital:: ErrorKind ;
152181 ErrorKind :: Other
153182 }
154183}
155184
156- impl embedded_hal :: digital :: ErrorType for CdevPin {
185+ impl < MODE > ErrorType for CdevPin < MODE > {
157186 type Error = CdevPinError ;
158187}
159188
160- impl embedded_hal:: digital:: OutputPin for CdevPin {
189+ impl InputPin for CdevPin < Input > {
190+ fn is_low ( & mut self ) -> Result < bool , Self :: Error > {
191+ let low_value = self . state_to_value ( PinState :: Low ) ;
192+ Ok ( self . request ( ) . value ( self . line ) ? == low_value)
193+ }
194+
195+ fn is_high ( & mut self ) -> Result < bool , Self :: Error > {
196+ let high_value = self . state_to_value ( PinState :: High ) ;
197+ Ok ( self . request ( ) . value ( self . line ) ? == high_value)
198+ }
199+ }
200+
201+ impl OutputPin for CdevPin < Output > {
161202 fn set_low ( & mut self ) -> Result < ( ) , Self :: Error > {
162- let line = self . line ;
163- let is_active_low = self . config ( ) . line_config ( line) . unwrap ( ) . active_low ;
164- self . request ( )
165- . set_value (
166- line,
167- state_to_value ( embedded_hal:: digital:: PinState :: Low , is_active_low) ,
168- )
169- . map ( |_| ( ) )
170- . map_err ( CdevPinError :: from)
203+ let new_value = self . state_to_value ( PinState :: Low ) ;
204+
205+ self . request ( ) . set_value ( self . line , new_value) ?;
206+ self . line_config . value = Some ( new_value) ;
207+
208+ Ok ( ( ) )
171209 }
172210
173211 fn set_high ( & mut self ) -> Result < ( ) , Self :: Error > {
174- let line = self . line ;
175- let is_active_low = self . config ( ) . line_config ( line) . unwrap ( ) . active_low ;
176- self . request ( )
177- . set_value (
178- line,
179- state_to_value ( embedded_hal:: digital:: PinState :: High , is_active_low) ,
180- )
181- . map ( |_| ( ) )
182- . map_err ( CdevPinError :: from)
212+ let new_value = self . state_to_value ( PinState :: High ) ;
213+
214+ self . request ( ) . set_value ( self . line , new_value) ?;
215+ self . line_config . value = Some ( new_value) ;
216+
217+ Ok ( ( ) )
183218 }
184219}
185220
186- impl InputPin for CdevPin {
187- fn is_high ( & mut self ) -> Result < bool , Self :: Error > {
188- let line = self . line ;
189- let is_active_low = self . config ( ) . line_config ( line) . unwrap ( ) . active_low ;
190- self . request ( )
191- . value ( line)
192- . map ( |val| val == state_to_value ( embedded_hal:: digital:: PinState :: High , is_active_low) )
193- . map_err ( CdevPinError :: from)
221+ impl StatefulOutputPin for CdevPin < Output > {
222+ #[ inline]
223+ fn is_set_low ( & mut self ) -> Result < bool , Self :: Error > {
224+ let low_value = self . state_to_value ( PinState :: Low ) ;
225+ Ok ( self . line_config . value == Some ( low_value) )
194226 }
195227
196- fn is_low ( & mut self ) -> Result < bool , Self :: Error > {
197- self . is_high ( ) . map ( |val| !val)
228+ #[ inline]
229+ fn is_set_high ( & mut self ) -> Result < bool , Self :: Error > {
230+ let high_value = self . state_to_value ( PinState :: High ) ;
231+ Ok ( self . line_config . value == Some ( high_value) )
198232 }
199233}
200234
201235#[ cfg( feature = "async-tokio" ) ]
202- impl embedded_hal_async:: digital:: Wait for CdevPin {
236+ impl embedded_hal_async:: digital:: Wait for CdevPin < Input > {
203237 async fn wait_for_high ( & mut self ) -> Result < ( ) , Self :: Error > {
204238 if self . is_high ( ) ? {
205239 return Ok ( ( ) ) ;
@@ -217,15 +251,14 @@ impl embedded_hal_async::digital::Wait for CdevPin {
217251 }
218252
219253 async fn wait_for_rising_edge ( & mut self ) -> Result < ( ) , Self :: Error > {
220- let config = self . config ( ) ;
221- let line_config = config. line_config ( self . line ) . unwrap ( ) ;
222254 if !matches ! (
223- line_config. edge_detection,
255+ self . line_config. edge_detection,
224256 Some ( EdgeDetection :: RisingEdge | EdgeDetection :: BothEdges )
225257 ) {
226- let mut new_config = config;
258+ let mut new_config = self . req . as_ref ( ) . config ( ) ;
227259 new_config. with_edge_detection ( EdgeDetection :: RisingEdge ) ;
228- self . request ( ) . reconfigure ( & new_config) ?;
260+ self . req . as_ref ( ) . reconfigure ( & new_config) ?;
261+ self . line_config . edge_detection = Some ( EdgeDetection :: RisingEdge ) ;
229262 }
230263
231264 loop {
@@ -237,15 +270,14 @@ impl embedded_hal_async::digital::Wait for CdevPin {
237270 }
238271
239272 async fn wait_for_falling_edge ( & mut self ) -> Result < ( ) , Self :: Error > {
240- let config = self . config ( ) ;
241- let line_config = config. line_config ( self . line ) . unwrap ( ) ;
242273 if !matches ! (
243- line_config. edge_detection,
274+ self . line_config. edge_detection,
244275 Some ( EdgeDetection :: FallingEdge | EdgeDetection :: BothEdges )
245276 ) {
246- let mut new_config = config;
277+ let mut new_config = self . req . as_ref ( ) . config ( ) ;
247278 new_config. with_edge_detection ( EdgeDetection :: FallingEdge ) ;
248- self . request ( ) . reconfigure ( & new_config) ?;
279+ self . req . as_ref ( ) . reconfigure ( & new_config) ?;
280+ self . line_config . edge_detection = Some ( EdgeDetection :: FallingEdge ) ;
249281 }
250282
251283 loop {
@@ -257,12 +289,14 @@ impl embedded_hal_async::digital::Wait for CdevPin {
257289 }
258290
259291 async fn wait_for_any_edge ( & mut self ) -> Result < ( ) , Self :: Error > {
260- let config = self . config ( ) ;
261- let line_config = config. line_config ( self . line ) . unwrap ( ) ;
262- if line_config. edge_detection != Some ( EdgeDetection :: BothEdges ) {
263- let mut new_config = config;
292+ if !matches ! (
293+ self . line_config. edge_detection,
294+ Some ( EdgeDetection :: BothEdges )
295+ ) {
296+ let mut new_config = self . req . as_ref ( ) . config ( ) ;
264297 new_config. with_edge_detection ( EdgeDetection :: BothEdges ) ;
265- self . request ( ) . reconfigure ( & new_config) ?;
298+ self . req . as_ref ( ) . reconfigure ( & new_config) ?;
299+ self . line_config . edge_detection = Some ( EdgeDetection :: BothEdges ) ;
266300 }
267301
268302 self . req . read_edge_event ( ) . await ?;
0 commit comments