capsules_extra/
mcp230xx.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! SyscallDriver for the Microchip MCP230xx I2C GPIO extenders.
6//!
7//! - <https://www.microchip.com/wwwproducts/en/MCP23008>
8//! - <https://www.microchip.com/wwwproducts/en/MCP23017>
9//!
10//! Paraphrased from the website for the MCP23008:
11//!
12//! > The MCP23008 device provides 8-bit, general purpose, parallel I/O
13//! > expansion for I2C bus applications. The MCP23008 has three address pins
14//! > and consists of multiple 8-bit configuration registers for input, output
15//! > and polarity selection. The system master can enable the I/Os as either
16//! > inputs or outputs by writing the I/O configuration bits. The data for each
17//! > input or output is kept in the corresponding Input or Output register. The
18//! > polarity of the Input Port register can be inverted with the Polarity
19//! > Inversion register. All registers can be read by the system master.
20//!
21//! This driver can support the MCP230xx series GPIO extenders with a
22//! configurable number of banks.
23//!
24//! Usage
25//! -----
26//! This capsule can either be used inside of the kernel or as an input to
27//! the `gpio_async` capsule because it implements the `gpio_async::Port`
28//! trait.
29//!
30//! Example usage:
31//!
32//! ```rust,ignore
33//! # use kernel::static_init;
34//!
35//! // Configure the MCP230xx. Device address 0x20.
36//! let mcp230xx_i2c = static_init!(
37//!     capsules::virtual_i2c::I2CDevice,
38//!     capsules::virtual_i2c::I2CDevice::new(i2c_mux, 0x20));
39//! let mcp230xx_buffer = static_init!([u8; capsules::mcp230xx::BUFFER_LENGTH],
40//!                                    [0; capsules::mcp230xx::BUFFER_LENGTH]);
41//! let mcp230xx = static_init!(
42//!     capsules::mcp230xx::MCP230xx<'static>,
43//!     capsules::mcp230xx::MCP230xx::new(mcp230xx_i2c,
44//!                                       Some(&sam4l::gpio::PA[04]),
45//!                                       None,
46//!                                       mcp230xx_buffer,
47//!                                       8, // How many pins in a bank
48//!                                       1, // How many pin banks on the chip
49//!                                       ));
50//! mcp230xx_i2c.set_client(mcp230xx);
51//! sam4l::gpio::PA[04].set_client(mcp230xx);
52//!
53//! // Create an array of the GPIO extenders so we can pass them to an
54//! // administrative layer that provides a single interface to them all.
55//! let async_gpio_ports = static_init!(
56//!     [&'static capsules::mcp230xx::MCP230xx; 1],
57//!     [mcp230xx]);
58//!
59//! // `gpio_async` is the object that manages all of the extenders.
60//! let gpio_async = static_init!(
61//!     capsules::gpio_async::GPIOAsync<'static, capsules::mcp230xx::MCP230xx<'static>>,
62//!     capsules::gpio_async::GPIOAsync::new(async_gpio_ports));
63//! // Setup the clients correctly.
64//! for port in async_gpio_ports.iter() {
65//!     port.set_client(gpio_async);
66//! }
67//! ```
68//!
69//! Note that if interrupts are not needed, a `None` can be passed in when the
70//! `mcp230xx` object is created.
71
72use core::cell::Cell;
73use kernel::hil;
74use kernel::hil::gpio;
75use kernel::hil::gpio_async;
76use kernel::utilities::cells::{OptionalCell, TakeCell};
77use kernel::ErrorCode;
78
79// Buffer to use for I2C messages
80pub const BUFFER_LENGTH: usize = 7;
81
82#[allow(dead_code)]
83#[derive(Debug)]
84enum Registers {
85    IoDir = 0x00,
86    IPol = 0x01,
87    GpIntEn = 0x02,
88    DefVal = 0x03,
89    IntCon = 0x04,
90    IoCon = 0x05,
91    GpPu = 0x06,
92    IntF = 0x07,
93    IntCap = 0x08,
94    Gpio = 0x09,
95    OLat = 0x0a,
96}
97
98/// States of the I2C protocol with the MCP230xx.
99#[derive(Clone, Copy, Debug, PartialEq)]
100enum State {
101    Idle,
102
103    // Setup input/output
104    SelectIoDir(u8, Direction),
105    ReadIoDir(u8, Direction),
106    SelectIoDirForGpPu(u8, bool),
107    ReadIoDirForGpPu(u8, bool),
108    SetIoDirForGpPu(u8, bool),
109    ReadGpPu(u8, bool),
110    SelectGpio(u8, PinState),
111    ReadGpio(u8, PinState),
112    SelectGpioToggle(u8),
113    ReadGpioToggle(u8),
114    SelectGpioRead(u8),
115    ReadGpioRead(u8),
116    EnableInterruptSettings(u8),
117    ReadInterruptSetup(u8),
118    ReadInterruptValues(u8),
119
120    /// Disable I2C and release buffer
121    Done,
122}
123
124#[derive(Clone, Copy, Debug, PartialEq)]
125enum Direction {
126    Input = 0x01,
127    Output = 0x00,
128}
129
130#[derive(Clone, Copy, Debug, PartialEq)]
131enum PinState {
132    High = 0x01,
133    Low = 0x00,
134}
135
136pub struct MCP230xx<'a, I: hil::i2c::I2CDevice> {
137    i2c: &'a I,
138    state: Cell<State>,
139    bank_size: u8,       // How many GPIO pins per bank (likely 8)
140    number_of_banks: u8, // How many GPIO banks this extender has (likely 1 or 2)
141    buffer: TakeCell<'static, [u8]>,
142    interrupt_pin_a: Option<&'a dyn gpio::InterruptValuePin<'a>>,
143    interrupt_pin_b: Option<&'a dyn gpio::InterruptValuePin<'a>>,
144    interrupts_enabled: Cell<u32>, // Whether the pin interrupt is enabled
145    interrupts_mode: Cell<u32>,    // What interrupt mode the pin is in
146    client: OptionalCell<&'static dyn gpio_async::Client>,
147}
148
149impl<'a, I: hil::i2c::I2CDevice> MCP230xx<'a, I> {
150    pub fn new(
151        i2c: &'a I,
152        interrupt_pin_a: Option<&'a dyn gpio::InterruptValuePin<'a>>,
153        interrupt_pin_b: Option<&'a dyn gpio::InterruptValuePin<'a>>,
154        buffer: &'static mut [u8],
155        bank_size: u8,
156        number_of_banks: u8,
157    ) -> MCP230xx<'a, I> {
158        MCP230xx {
159            i2c,
160            state: Cell::new(State::Idle),
161            bank_size,
162            number_of_banks,
163            buffer: TakeCell::new(buffer),
164            interrupt_pin_a,
165            interrupt_pin_b,
166            interrupts_enabled: Cell::new(0),
167            interrupts_mode: Cell::new(0),
168            client: OptionalCell::empty(),
169        }
170    }
171
172    /// Set the client of this MCP230xx when commands finish or interrupts
173    /// occur. The `identifier` is simply passed back with the callback
174    /// so that the upper layer can keep track of which device triggered.
175    pub fn set_client<C: gpio_async::Client>(&self, client: &'static C) {
176        self.client.set(client);
177    }
178
179    fn enable_host_interrupt(&self) -> Result<(), ErrorCode> {
180        // We configure the MCP230xx to use an active high interrupt.
181        // If we don't have an interrupt pin mapped to this driver then we
182        // obviously can't do interrupts.
183        let first = self
184            .interrupt_pin_a
185            .map_or(Err(ErrorCode::FAIL), |interrupt_pin| {
186                interrupt_pin.make_input();
187                let _ = interrupt_pin.enable_interrupts(gpio::InterruptEdge::RisingEdge);
188                Ok(())
189            });
190        if first != Ok(()) {
191            return first;
192        }
193        // Also do the other interrupt pin if it exists.
194        self.interrupt_pin_b.map(|interrupt_pin| {
195            interrupt_pin.make_input();
196            let _ = interrupt_pin.enable_interrupts(gpio::InterruptEdge::RisingEdge);
197        });
198        Ok(())
199    }
200
201    /// This calculates the actual register address to use based on the list of
202    /// registers in the `Registers` enum definitions. This is needed because
203    /// the addresses are different for single- and multi-port mcp230xx
204    /// extenders.
205    ///
206    /// If this is a single port extender then the register index is the same as
207    /// the `Registers` enum and what is passed in is returned. If the chip has
208    /// multiple banks then the register address is shifted based on the number
209    /// and size of the bank.
210    fn calc_register_addr(&self, register: Registers, pin_number: u8) -> u8 {
211        if self.number_of_banks == 1 {
212            pin_number
213        } else {
214            // Calculate an offset based on which bank this pin is in.
215            let offset = pin_number / self.bank_size;
216            // The register index is then the original value multiplied by
217            // the number of banks, plus the offset.
218            (register as u8 * self.number_of_banks) + offset
219        }
220    }
221
222    fn set_direction(&self, pin_number: u8, direction: Direction) -> Result<(), ErrorCode> {
223        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
224            self.i2c.enable();
225
226            buffer[0] = self.calc_register_addr(Registers::IoDir, pin_number);
227            // TODO verify errors
228            let _ = self.i2c.write(buffer, 1);
229            self.state.set(State::SelectIoDir(pin_number, direction));
230
231            Ok(())
232        })
233    }
234
235    /// Set the pull-up on the pin also configure it to be an input.
236    fn configure_pullup(&self, pin_number: u8, enabled: bool) -> Result<(), ErrorCode> {
237        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
238            self.i2c.enable();
239
240            buffer[0] = self.calc_register_addr(Registers::IoDir, pin_number);
241            // TODO verify errors
242            let _ = self.i2c.write(buffer, 1);
243            self.state
244                .set(State::SelectIoDirForGpPu(pin_number, enabled));
245
246            Ok(())
247        })
248    }
249
250    fn set_pin(&self, pin_number: u8, value: PinState) -> Result<(), ErrorCode> {
251        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
252            self.i2c.enable();
253
254            buffer[0] = self.calc_register_addr(Registers::Gpio, pin_number);
255            // TODO verify errors
256            let _ = self.i2c.write(buffer, 1);
257            self.state.set(State::SelectGpio(pin_number, value));
258
259            Ok(())
260        })
261    }
262
263    fn toggle_pin(&self, pin_number: u8) -> Result<(), ErrorCode> {
264        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
265            self.i2c.enable();
266
267            buffer[0] = self.calc_register_addr(Registers::Gpio, pin_number);
268            // TODO verify errors
269            let _ = self.i2c.write(buffer, 1);
270            self.state.set(State::SelectGpioToggle(pin_number));
271
272            Ok(())
273        })
274    }
275
276    fn read_pin(&self, pin_number: u8) -> Result<(), ErrorCode> {
277        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
278            self.i2c.enable();
279
280            buffer[0] = self.calc_register_addr(Registers::Gpio, pin_number);
281            // TODO verify errors
282            let _ = self.i2c.write(buffer, 1);
283            self.state.set(State::SelectGpioRead(pin_number));
284
285            Ok(())
286        })
287    }
288
289    fn enable_interrupt_pin(
290        &self,
291        pin_number: u8,
292        direction: gpio::InterruptEdge,
293    ) -> Result<(), ErrorCode> {
294        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
295            self.i2c.enable();
296
297            // Mark the settings that we have for this interrupt.
298            // Since the MCP230xx only seems to support level interrupts
299            // and both edge interrupts, we choose to use both edge interrupts
300            // and then filter here in the driver if the user only asked
301            // for one direction interrupts. To do this, we need to know what
302            // the user asked for.
303            self.save_pin_interrupt_state(pin_number, true, direction);
304
305            // Setup interrupt configs that are true of all interrupts
306            buffer[0] = self.calc_register_addr(Registers::IntCon, 0);
307            // Set all of the IntCon registers to zero.
308            let mut i: usize = 1;
309            for _ in 0..(self.number_of_banks as usize) {
310                buffer[i] = 0; // Make all pins toggle on every change.
311                i += 1;
312            }
313            // The next register is the IoCon (configuration) register, which
314            // we also want to set.
315            buffer[i] = 0b00000010; // Make MCP230xx interrupt pin active high.
316                                    // TODO verify errors
317            let _ = self.i2c.write(buffer, i + 1);
318            self.state.set(State::EnableInterruptSettings(pin_number));
319
320            Ok(())
321        })
322    }
323
324    fn disable_interrupt_pin(&self, pin_number: u8) -> Result<(), ErrorCode> {
325        self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
326            self.i2c.enable();
327
328            // Clear this interrupt from our setup.
329            self.remove_pin_interrupt_state(pin_number);
330
331            // Just have to write the new interrupt settings.
332            buffer[0] = self.calc_register_addr(Registers::GpIntEn, pin_number);
333            buffer[1] = self.get_pin_interrupt_enabled_state(pin_number);
334            // TODO verify errors
335            let _ = self.i2c.write(buffer, 2);
336            self.state.set(State::Done);
337
338            Ok(())
339        })
340    }
341
342    /// Helper function for keeping track of which interrupts are currently
343    /// enabled.
344    fn save_pin_interrupt_state(
345        &self,
346        pin_number: u8,
347        enabled: bool,
348        direction: gpio::InterruptEdge,
349    ) {
350        // Set the enabled bitmap.
351        let mut current_enabled = self.interrupts_enabled.get();
352        // Clear out existing settings
353        current_enabled &= !(1 << pin_number);
354        // Set new value
355        current_enabled |= (enabled as u32) << pin_number;
356        self.interrupts_enabled.set(current_enabled);
357
358        // Set the direction bitmap.
359        let mut current_mode = self.interrupts_mode.get();
360        // Clear out existing settings
361        current_mode &= !(0x03 << (2 * pin_number));
362        // Generate new settings
363        let new_settings = (direction as u32) & 0x03;
364        // Update settings
365        current_mode |= new_settings << (2 * pin_number);
366        self.interrupts_mode.set(current_mode);
367    }
368
369    fn remove_pin_interrupt_state(&self, pin_number: u8) {
370        let new_enabled = self.interrupts_enabled.get() & !(1 << pin_number);
371        self.interrupts_enabled.set(new_enabled);
372        let new_mode = self.interrupts_mode.get() & !(0x03 << (2 * pin_number));
373        self.interrupts_mode.set(new_mode);
374    }
375
376    /// Create an 8 bit bitmask of which interrupts are enabled.
377    fn get_pin_interrupt_enabled_state(&self, pin_number: u8) -> u8 {
378        let offset = (pin_number / self.bank_size) * self.bank_size;
379        let interrupts_enabled = self.interrupts_enabled.get();
380        (interrupts_enabled >> offset) as u8
381    }
382
383    fn check_pin_interrupt_enabled(&self, pin_number: u8) -> bool {
384        (self.interrupts_enabled.get() >> pin_number) & 0x01 == 0x01
385    }
386
387    fn get_pin_interrupt_direction(&self, pin_number: u8) -> gpio::InterruptEdge {
388        let direction = self.interrupts_mode.get() >> (pin_number * 2) & 0x03;
389        match direction {
390            0 => gpio::InterruptEdge::RisingEdge,
391            1 => gpio::InterruptEdge::FallingEdge,
392            _ => gpio::InterruptEdge::EitherEdge,
393        }
394    }
395}
396
397impl<I: hil::i2c::I2CDevice> hil::i2c::I2CClient for MCP230xx<'_, I> {
398    fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), hil::i2c::Error>) {
399        match self.state.get() {
400            State::SelectIoDir(pin_number, direction) => {
401                // TODO verify errors
402                let _ = self.i2c.read(buffer, 1);
403                self.state.set(State::ReadIoDir(pin_number, direction));
404            }
405            State::ReadIoDir(pin_number, direction) => {
406                if direction == Direction::Input {
407                    buffer[1] = buffer[0] | (1 << pin_number);
408                } else {
409                    buffer[1] = buffer[0] & !(1 << pin_number);
410                }
411                buffer[0] = self.calc_register_addr(Registers::IoDir, pin_number);
412                // TODO verify errors
413                let _ = self.i2c.write(buffer, 2);
414                self.state.set(State::Done);
415            }
416            State::SelectIoDirForGpPu(pin_number, enabled) => {
417                // TODO verify errors
418                let _ = self.i2c.read(buffer, 1);
419                self.state.set(State::ReadIoDirForGpPu(pin_number, enabled));
420            }
421            State::ReadIoDirForGpPu(pin_number, enabled) => {
422                // Make sure the pin is enabled.
423                buffer[1] = buffer[0] | (1 << pin_number);
424                buffer[0] = self.calc_register_addr(Registers::IoDir, pin_number);
425                // TODO verify errors
426                let _ = self.i2c.write(buffer, 2);
427                self.state.set(State::SetIoDirForGpPu(pin_number, enabled));
428            }
429            State::SetIoDirForGpPu(pin_number, enabled) => {
430                buffer[0] = self.calc_register_addr(Registers::GpPu, pin_number);
431                // TODO verify errors
432                let _ = self.i2c.write(buffer, 1);
433                self.state.set(State::ReadGpPu(pin_number, enabled));
434            }
435            State::ReadGpPu(pin_number, enabled) => {
436                // Configure the pullup status and save it in the buffer.
437                let pullup = match enabled {
438                    true => buffer[0] | (1 << pin_number),
439                    false => buffer[0] & !(1 << pin_number),
440                };
441                buffer[0] = self.calc_register_addr(Registers::GpPu, pin_number);
442                buffer[1] = pullup;
443                // TODO verify errors
444                let _ = self.i2c.write(buffer, 2);
445                self.state.set(State::Done);
446            }
447            State::SelectGpio(pin_number, value) => {
448                // TODO verify errors
449                let _ = self.i2c.read(buffer, 1);
450                self.state.set(State::ReadGpio(pin_number, value));
451            }
452            State::ReadGpio(pin_number, value) => {
453                buffer[1] = match value {
454                    PinState::High => buffer[0] | (1 << pin_number),
455                    PinState::Low => buffer[0] & !(1 << pin_number),
456                };
457                buffer[0] = self.calc_register_addr(Registers::Gpio, pin_number);
458                // TODO verify errors
459                let _ = self.i2c.write(buffer, 2);
460                self.state.set(State::Done);
461            }
462            State::SelectGpioToggle(pin_number) => {
463                // TODO verify errors
464                let _ = self.i2c.read(buffer, 1);
465                self.state.set(State::ReadGpioToggle(pin_number));
466            }
467            State::ReadGpioToggle(pin_number) => {
468                buffer[1] = buffer[0] ^ (1 << pin_number);
469                buffer[0] = self.calc_register_addr(Registers::Gpio, pin_number);
470                // TODO verify errors
471                let _ = self.i2c.write(buffer, 2);
472                self.state.set(State::Done);
473            }
474            State::SelectGpioRead(pin_number) => {
475                // TODO verify errors
476                let _ = self.i2c.read(buffer, 1);
477                self.state.set(State::ReadGpioRead(pin_number));
478            }
479            State::ReadGpioRead(pin_number) => {
480                let pin_value = (buffer[0] >> pin_number) & 0x01;
481
482                self.client.map(|client| {
483                    client.done(pin_value as usize);
484                });
485
486                self.buffer.replace(buffer);
487                self.i2c.disable();
488                self.state.set(State::Idle);
489            }
490            State::EnableInterruptSettings(pin_number) => {
491                // Rather than read the current interrupts and write those
492                // back, just write the entire register with our saved state.
493                buffer[0] = self.calc_register_addr(Registers::GpIntEn, pin_number);
494                buffer[1] = self.get_pin_interrupt_enabled_state(pin_number);
495                // TODO verify errors
496                let _ = self.i2c.write(buffer, 2);
497                self.state.set(State::Done);
498            }
499            State::ReadInterruptSetup(bank_number) => {
500                // Now read the interrupt flags and the state of the lines
501                // TODO verify errors
502                let _ = self.i2c.read(buffer, 3);
503                self.state.set(State::ReadInterruptValues(bank_number));
504            }
505            State::ReadInterruptValues(bank_number) => {
506                let interrupt_flags = buffer[0];
507                let pins_status = buffer[2];
508                // Check each bit to see if that pin triggered an interrupt.
509                for i in 0..8 {
510                    // Calculate the actual pin number based on which bank we
511                    // are examining.
512                    let pin_number = i + (bank_number * self.bank_size);
513                    // Check that this pin is actually enabled.
514                    if !self.check_pin_interrupt_enabled(pin_number) {
515                        continue;
516                    }
517                    if (interrupt_flags >> i) & 0x01 == 0x01 {
518                        // Use the GPIO register to determine which way the
519                        // interrupt went.
520                        let pin_status = (pins_status >> i) & 0x01;
521                        let interrupt_direction = self.get_pin_interrupt_direction(pin_number);
522                        // Check to see if this was an interrupt we want
523                        // to report.
524                        let fire_interrupt = match interrupt_direction {
525                            gpio::InterruptEdge::EitherEdge => true,
526                            gpio::InterruptEdge::RisingEdge => pin_status == 0x01,
527                            gpio::InterruptEdge::FallingEdge => pin_status == 0x00,
528                        };
529                        if fire_interrupt {
530                            // Signal this interrupt to the application.
531                            self.client.map(|client| {
532                                // Return both the pin that interrupted and
533                                // the identifier that was passed for
534                                // enable_interrupt.
535                                client.fired(pin_number as usize, 0);
536                            });
537                            break;
538                        }
539                    }
540                }
541                self.buffer.replace(buffer);
542                self.i2c.disable();
543                self.state.set(State::Idle);
544            }
545            State::Done => {
546                self.client.map(|client| {
547                    client.done(0);
548                });
549
550                self.buffer.replace(buffer);
551                self.i2c.disable();
552                self.state.set(State::Idle);
553            }
554            _ => {}
555        }
556    }
557}
558
559impl<I: hil::i2c::I2CDevice> gpio::ClientWithValue for MCP230xx<'_, I> {
560    fn fired(&self, value: u32) {
561        if value < 2 {
562            return; // Error, value specifies which pin A=0, B=1
563        }
564        self.buffer.take().map(|buffer| {
565            let bank_number = value;
566            self.i2c.enable();
567
568            // Need to read the IntF register which marks which pins
569            // interrupted.
570            buffer[0] =
571                self.calc_register_addr(Registers::IntF, bank_number as u8 * self.bank_size);
572            // TODO verify errors
573            let _ = self.i2c.write(buffer, 1);
574            self.state.set(State::ReadInterruptSetup(bank_number as u8));
575        });
576    }
577}
578
579impl<I: hil::i2c::I2CDevice> gpio_async::Port for MCP230xx<'_, I> {
580    fn disable(&self, pin: usize) -> Result<(), ErrorCode> {
581        // Best we can do is make this an input.
582        self.set_direction(pin as u8, Direction::Input)
583    }
584
585    fn make_output(&self, pin: usize) -> Result<(), ErrorCode> {
586        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
587            return Err(ErrorCode::INVAL);
588        }
589        self.set_direction(pin as u8, Direction::Output)
590    }
591
592    fn make_input(&self, pin: usize, mode: gpio::FloatingState) -> Result<(), ErrorCode> {
593        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
594            return Err(ErrorCode::INVAL);
595        }
596        match mode {
597            gpio::FloatingState::PullUp => self.configure_pullup(pin as u8, true),
598            gpio::FloatingState::PullDown => {
599                // No support for this
600                self.configure_pullup(pin as u8, false)
601            }
602            gpio::FloatingState::PullNone => self.configure_pullup(pin as u8, false),
603        }
604    }
605
606    fn read(&self, pin: usize) -> Result<(), ErrorCode> {
607        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
608            return Err(ErrorCode::INVAL);
609        }
610        self.read_pin(pin as u8)
611    }
612
613    fn toggle(&self, pin: usize) -> Result<(), ErrorCode> {
614        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
615            return Err(ErrorCode::INVAL);
616        }
617        self.toggle_pin(pin as u8)
618    }
619
620    fn set(&self, pin: usize) -> Result<(), ErrorCode> {
621        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
622            return Err(ErrorCode::INVAL);
623        }
624        self.set_pin(pin as u8, PinState::High)
625    }
626
627    fn clear(&self, pin: usize) -> Result<(), ErrorCode> {
628        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
629            return Err(ErrorCode::INVAL);
630        }
631        self.set_pin(pin as u8, PinState::Low)
632    }
633
634    fn enable_interrupt(&self, pin: usize, mode: gpio::InterruptEdge) -> Result<(), ErrorCode> {
635        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
636            return Err(ErrorCode::INVAL);
637        }
638        let ret = self.enable_host_interrupt();
639        match ret {
640            Ok(()) => self.enable_interrupt_pin(pin as u8, mode),
641            _ => ret,
642        }
643    }
644
645    fn disable_interrupt(&self, pin: usize) -> Result<(), ErrorCode> {
646        if pin > ((self.number_of_banks * self.bank_size) - 1) as usize {
647            return Err(ErrorCode::INVAL);
648        }
649        self.disable_interrupt_pin(pin as u8)
650    }
651
652    fn is_pending(&self, _pin: usize) -> bool {
653        false
654    }
655}