From 2213535a3a2d85ff97bd31d2e70e131947f3958d Mon Sep 17 00:00:00 2001
From: Naxdy <naxdy@naxdy.org>
Date: Tue, 29 Oct 2024 13:54:42 +0100
Subject: [PATCH] init

---
 .envrc        |   1 +
 .gitignore    |   8 ++
 Cargo.lock    | 318 ++++++++++++++++++++++++++++++++++++++++++++++++++
 Cargo.toml    |  12 ++
 build.rs      |  54 +++++++++
 ffi/wrapper.h |   1 +
 flake.lock    | 163 ++++++++++++++++++++++++++
 flake.nix     |  69 +++++++++++
 src/ffi.rs    |   6 +
 src/gpio.rs   |  36 ++++++
 src/lib.rs    |   2 +
 11 files changed, 670 insertions(+)
 create mode 100644 .envrc
 create mode 100644 .gitignore
 create mode 100644 Cargo.lock
 create mode 100644 Cargo.toml
 create mode 100644 build.rs
 create mode 100644 ffi/wrapper.h
 create mode 100644 flake.lock
 create mode 100644 flake.nix
 create mode 100644 src/ffi.rs
 create mode 100644 src/gpio.rs
 create mode 100644 src/lib.rs

diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5c9d904
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+/.direnv
+/target
+/result
+/result-*
+.vscode
+
+/build
+
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..fe7e318
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,318 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "itertools",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "cc"
+version = "1.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.161"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
+
+[[package]]
+name = "libloading"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
+dependencies = [
+ "cfg-if",
+ "windows-targets",
+]
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "syn"
+version = "2.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wiringop-rs"
+version = "0.1.0"
+dependencies = [
+ "bindgen",
+ "cc",
+ "glob",
+ "thiserror",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..f2ae784
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "wiringop-rs"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+thiserror = "1.0.65"
+
+[build-dependencies]
+cc = "1.1.31"
+glob = "0.3.1"
+bindgen = "0.70.1"
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..ce6231a
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,54 @@
+use std::{
+    env::{self, set_current_dir},
+    fs,
+    path::PathBuf,
+};
+
+fn main() {
+    fs::create_dir_all("build").expect("could not create build path");
+
+    let int_path = PathBuf::from("build")
+        .canonicalize()
+        .expect("cannot canonicalize path");
+
+    set_current_dir(int_path.clone()).expect("failed to change current directory");
+
+    let wiringop_path = PathBuf::from(
+        env::var("WIRINGOP_PATH").expect("could not retrieve wiringOP path from env"),
+    )
+    .canonicalize()
+    .expect("cannot canonicalize path");
+
+    let mut cc = cc::Build::new();
+
+    cc.files(
+        glob::glob(&format!("{}/wiringPi/*.c", wiringop_path.to_str().unwrap()))
+            .unwrap()
+            .filter_map(|e| e.ok()),
+    )
+    .include(format!("{}/wiringPi/", wiringop_path.to_str().unwrap()));
+
+    let target = std::env::var("TARGET").unwrap();
+
+    if target.starts_with("aarch64") {
+        cc.flag("-O2");
+    }
+
+    cc.static_flag(true).compile("wiringop");
+
+    let builder = bindgen::Builder::default()
+        .header("../ffi/wrapper.h")
+        .detect_include_paths(true);
+
+    let bindings = builder
+        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+        .generate()
+        .expect("unable to generate bindings");
+
+    let out_path = PathBuf::from(env::var("OUT_DIR").expect("failed to get out dir for bindings"))
+        .join("bindings.rs");
+
+    bindings
+        .write_to_file(out_path)
+        .expect("couldn't write bindings");
+}
diff --git a/ffi/wrapper.h b/ffi/wrapper.h
new file mode 100644
index 0000000..2b75d0c
--- /dev/null
+++ b/ffi/wrapper.h
@@ -0,0 +1 @@
+#include <wiringPi.h>
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..57ec184
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,163 @@
+{
+  "nodes": {
+    "fenix": {
+      "inputs": {
+        "nixpkgs": "nixpkgs",
+        "rust-analyzer-src": "rust-analyzer-src"
+      },
+      "locked": {
+        "lastModified": 1730183653,
+        "narHash": "sha256-dcJGcoDgNBxTagW8kECwBKsRBA1ZITtQ+p3N6KHg5ps=",
+        "owner": "nix-community",
+        "repo": "fenix",
+        "rev": "dc19afc39af5f5e69fca78ebae59170e61017df8",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "fenix",
+        "type": "github"
+      }
+    },
+    "flake-utils": {
+      "inputs": {
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1726560853,
+        "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "naersk": {
+      "inputs": {
+        "nixpkgs": "nixpkgs_2"
+      },
+      "locked": {
+        "lastModified": 1721727458,
+        "narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=",
+        "owner": "nix-community",
+        "repo": "naersk",
+        "rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-community",
+        "repo": "naersk",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1729880355,
+        "narHash": "sha256-RP+OQ6koQQLX5nw0NmcDrzvGL8HDLnyXt/jHhL1jwjM=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "18536bf04cd71abd345f9579158841376fdd0c5a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-unstable",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "nixpkgs_2": {
+      "locked": {
+        "lastModified": 1728888510,
+        "narHash": "sha256-nsNdSldaAyu6PE3YUA+YQLqUDJh+gRbBooMMekZJwvI=",
+        "path": "/nix/store/xnjw9gmfmpppdj6bxpw6cfkspc3h6xwl-source",
+        "rev": "a3c0b3b21515f74fd2665903d4ce6bc4dc81c77c",
+        "type": "path"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "type": "indirect"
+      }
+    },
+    "nixpkgs_3": {
+      "locked": {
+        "lastModified": 1730137625,
+        "narHash": "sha256-9z8oOgFZiaguj+bbi3k4QhAD6JabWrnv7fscC/mt0KE=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "64b80bfb316b57cdb8919a9110ef63393d74382a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "ref": "nixos-24.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "fenix": "fenix",
+        "flake-utils": "flake-utils",
+        "naersk": "naersk",
+        "nixpkgs": "nixpkgs_3",
+        "wiringop": "wiringop"
+      }
+    },
+    "rust-analyzer-src": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1730123801,
+        "narHash": "sha256-11FMcPraLSKuvfFF4OzmKWSKE5zXAzFqZOi3k/8/HFg=",
+        "owner": "rust-lang",
+        "repo": "rust-analyzer",
+        "rev": "cf8f950baab30f335917b177536d2d73e0aaa1ae",
+        "type": "github"
+      },
+      "original": {
+        "owner": "rust-lang",
+        "ref": "nightly",
+        "repo": "rust-analyzer",
+        "type": "github"
+      }
+    },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "wiringop": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1728531008,
+        "narHash": "sha256-jVfNb0YqaYFd2r5zupy8C1Q1jDAfetPRVKbhtyMBLtg=",
+        "owner": "orangepi-xunlong",
+        "repo": "wiringOP",
+        "rev": "d7bb9d97db265476e6afa8186f2406642e6946be",
+        "type": "github"
+      },
+      "original": {
+        "owner": "orangepi-xunlong",
+        "repo": "wiringOP",
+        "type": "github"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..0de33b2
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,69 @@
+{
+  description = "Rust library for interacting with wiringOP (Orange Pi fork of wiringPi)";
+
+  inputs = {
+    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-24.05";
+
+    fenix.url = "github:nix-community/fenix";
+
+    naersk.url = "github:nix-community/naersk";
+
+    wiringop = {
+      url = "github:orangepi-xunlong/wiringOP";
+      flake = false;
+    };
+
+    flake-utils.url = "github:numtide/flake-utils";
+  };
+
+  outputs = { self, nixpkgs, fenix, naersk, wiringop, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
+    let
+      target = "aarch64-unknown-linux-gnu";
+      pkgs = import nixpkgs {
+        inherit system;
+        overlays = [
+          fenix.overlays.default
+        ];
+      };
+
+      devToolchain = with fenix.packages.${system}; combine [
+        stable.cargo
+        stable.rustc
+        stable.clippy
+        stable.rustfmt
+        targets.${target}.stable.rust-std
+      ];
+
+      CARGO_BUILD_TARGET = target;
+      CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER =
+        let
+          inherit (pkgs.pkgsCross.aarch64-multiplatform.stdenv) cc;
+        in
+        "${cc}/bin/${cc.targetPrefix}cc";
+
+      wiringop-headers = pkgs.runCommandLocal "wiringOP-headers" { } ''
+        mkdir -p $out/include
+        cp ${wiringop}/wiringPi/*.h $out/include
+      '';
+
+      LIBCLANG_PATH = "${pkgs.llvmPackages_18.clang-unwrapped.lib}/lib";
+    in
+    {
+      devShells.default =
+        pkgs.mkShell {
+          nativeBuildInputs = [
+            devToolchain
+            pkgs.llvmPackages_18.clang
+          ];
+
+          buildInputs = [
+            wiringop-headers
+            pkgs.libxcrypt
+          ];
+
+          WIRINGOP_PATH = "${wiringop}";
+
+          inherit LIBCLANG_PATH CARGO_BUILD_TARGET CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER;
+        };
+    });
+}
diff --git a/src/ffi.rs b/src/ffi.rs
new file mode 100644
index 0000000..04d76a4
--- /dev/null
+++ b/src/ffi.rs
@@ -0,0 +1,6 @@
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(unused)]
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
diff --git a/src/gpio.rs b/src/gpio.rs
new file mode 100644
index 0000000..f53d9a9
--- /dev/null
+++ b/src/gpio.rs
@@ -0,0 +1,36 @@
+use std::sync::Mutex;
+
+use thiserror::Error;
+
+use crate::ffi;
+
+static PINS_HANDED_OUT: Mutex<Vec<i32>> = Mutex::new(Vec::new());
+
+#[derive(Error, Debug)]
+pub enum PinRetrievalError {
+    #[error("pin {0} has already been retrieved")]
+    AlreadyRetrieved(i32),
+}
+
+pub struct Pin {
+    pin_id: i32,
+}
+
+impl Pin {
+    pub fn new(pin_id: i32) -> Result<Self, PinRetrievalError> {
+        let can_retrieve = !PINS_HANDED_OUT
+            .lock()
+            .expect("failed to obtain pin list lock")
+            .contains(&pin_id);
+
+        if can_retrieve {
+            Ok(Self { pin_id })
+        } else {
+            Err(PinRetrievalError::AlreadyRetrieved(pin_id))
+        }
+    }
+
+    pub fn get_gpio_mode(&mut self) -> i32 {
+        unsafe { ffi::orangepi_get_gpio_mode(self.pin_id) }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..ea4a206
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,2 @@
+mod ffi;
+pub mod gpio;