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
//! KPAL plugin to control the output of a single GPIO pin using the GPIO char device.
mod errors;

use std::cell::RefCell;
use std::convert::TryInto;
use std::ffi::{CStr, CString};

use gpio_cdev::{Chip, LineHandle, LineRequestFlags};
use libc::c_int;
use log;

use kpal_plugin::constants::*;
use kpal_plugin::*;

use crate::errors::*;

const DEVICE_FILE: &'static str = "/dev/gpiochip0";

/// The GPIO pin number.
const OFFSET: u32 = 4;

#[derive(Debug)]
#[repr(C)]
struct GPIOPlugin {
    chip: RefCell<Chip>,
    line_handle: LineHandle,
    pin_state_label: CString,
}

impl PluginAPI<GPIOPluginError> for GPIOPlugin {
    type Plugin = GPIOPlugin;

    /// Returns a new instance of a GPIO plugin.
    fn new() -> Result<GPIOPlugin, GPIOPluginError> {
        let mut chip = Chip::new(DEVICE_FILE)?;

        let handle = chip
            .get_line(OFFSET)?
            .request(LineRequestFlags::OUTPUT, 0, "set-output")?;

        Ok(GPIOPlugin {
            chip: RefCell::new(chip),
            line_handle: handle,
            pin_state_label: CString::new("Pin state").expect("failed to create attribute name"),
        })
    }

    fn attribute_name(&self, id: usize) -> Result<&CStr, GPIOPluginError> {
        log::debug!("Received request for the name of attribute: {}", id);
        match id {
            0 => Ok(&self.pin_state_label),
            _ => Err(GPIOPluginError {
                error_code: ATTRIBUTE_DOES_NOT_EXIST,
                side: None,
            }),
        }
    }

    fn attribute_value(&self, id: usize) -> Result<Value, GPIOPluginError> {
        log::debug!("Received request for the value of attribute: {}", id);
        let value = self.line_handle.get_value()?;

        let value = value.try_into()?;

        Ok(Value::Int(value))
    }

    fn attribute_set_value(&mut self, id: usize, value: &Value) -> Result<(), GPIOPluginError> {
        log::debug!("Received request to set the value of attribute: {}", id);
        let value = match value {
            Value::Int(value) => value.to_owned(),
            _ => {
                return Err(GPIOPluginError {
                    error_code: ATTRIBUTE_TYPE_MISMATCH,
                    side: None,
                })
            }
        }
        .try_into()?;

        self.line_handle.set_value(value)?;

        Ok(())
    }
}

// FFI
#[no_mangle]
pub extern "C" fn kpal_library_init() -> c_int {
    env_logger::init();
    PLUGIN_OK
}

#[no_mangle]
pub extern "C" fn kpal_plugin_init(plugin: *mut Plugin) -> c_int {
    let plugin_data = match GPIOPlugin::new() {
        Ok(plugin_data) => plugin_data,
        Err(e) => {
            log::error!("Failed to initialize the plugin: {:?}", e);
            return PLUGIN_INIT_ERR;
        }
    };

    let plugin_data: Box<GPIOPlugin> = Box::new(plugin_data);
    let plugin_data = Box::into_raw(plugin_data) as *mut Peripheral;

    let vtable = VTable {
        peripheral_free: peripheral_free,
        error_message: error_message,
        attribute_name: attribute_name::<GPIOPlugin, GPIOPluginError>,
        attribute_value: attribute_value::<GPIOPlugin, GPIOPluginError>,
        set_attribute_value: set_attribute_value::<GPIOPlugin, GPIOPluginError>,
    };

    unsafe {
        plugin.write(Plugin {
            peripheral: plugin_data,
            vtable: vtable,
        });
    }

    log::debug!("Initialized plugin: {:?}", plugin);
    PLUGIN_OK
}