Skip to content

Commit 3593915

Browse files
authored
Merge pull request #559 from wedsonaf/gpio-core
rust: add GPIO support.
2 parents 540942e + 3119f2c commit 3593915

File tree

6 files changed

+327
-1
lines changed

6 files changed

+327
-1
lines changed

rust/helpers.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ void *rust_helper_dev_get_drvdata(struct device *dev)
324324
}
325325
EXPORT_SYMBOL_GPL(rust_helper_dev_get_drvdata);
326326

327+
const char *rust_helper_dev_name(const struct device *dev)
328+
{
329+
return dev_name(dev);
330+
}
331+
EXPORT_SYMBOL_GPL(rust_helper_dev_name);
332+
327333
void rust_helper___seqcount_init(seqcount_t *s, const char *name,
328334
struct lock_class_key *key)
329335
{

rust/kernel/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/interrupt.h>
2424
#include <linux/irqdomain.h>
2525
#include <linux/amba/bus.h>
26+
#include <linux/gpio/driver.h>
2627

2728
// `bindgen` gets confused at certain things
2829
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;

rust/kernel/device.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//!
55
//! C header: [`include/linux/device.h`](../../../../include/linux/device.h)
66
7-
use crate::bindings;
7+
use crate::{bindings, str::CStr};
88

99
/// A raw device.
1010
///
@@ -13,9 +13,26 @@ use crate::bindings;
1313
/// Implementers must ensure that the `*mut device` returned by [`RawDevice::raw_device`] is
1414
/// related to `self`, that is, actions on it will affect `self`. For example, if one calls
1515
/// `get_device`, then the refcount on the device represented by `self` will be incremented.
16+
///
17+
/// Additionally, implementers must ensure that the device is never renamed. Commit a5462516aa994
18+
/// has details on why `device_rename` should not be used.
1619
pub unsafe trait RawDevice {
1720
/// Returns the raw `struct device` related to `self`.
1821
fn raw_device(&self) -> *mut bindings::device;
22+
23+
/// Returns the name of the device.
24+
fn name(&self) -> &CStr {
25+
let ptr = self.raw_device();
26+
27+
// SAFETY: `ptr` is valid because `self` keeps it alive.
28+
let name = unsafe { bindings::dev_name(ptr) };
29+
30+
// SAFETY: The name of the device remains valid while it is alive (because the device is
31+
// never renamed, per the safety requirement of this trait). This is guaranteed to be the
32+
// case because the reference to `self` outlives the one of the returned `CStr` (enforced
33+
// by the compiler because of their lifetimes).
34+
unsafe { CStr::from_char_ptr(name) }
35+
}
1936
}
2037

2138
/// A ref-counted device.

rust/kernel/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ impl Error {
316316

317317
declare_err!(ERESTARTSYS, "Restart the system call.");
318318

319+
declare_err!(ENOTSUPP, "Operation is not supported.");
320+
319321
/// Creates an [`Error`] from a kernel error code.
320322
///
321323
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would

rust/kernel/gpio.rs

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Support for gpio device drivers.
4+
//!
5+
//! C header: [`include/linux/gpio/driver.h`](../../../../include/linux/gpio/driver.h)
6+
7+
use crate::{bindings, c_types, device, from_kernel_result, types::PointerWrapper, Error, Result};
8+
use core::{
9+
cell::UnsafeCell,
10+
marker::{PhantomData, PhantomPinned},
11+
pin::Pin,
12+
};
13+
14+
/// The direction of a gpio line.
15+
pub enum LineDirection {
16+
/// Direction is input.
17+
In = bindings::GPIO_LINE_DIRECTION_IN as _,
18+
19+
/// Direction is output.
20+
Out = bindings::GPIO_LINE_DIRECTION_OUT as _,
21+
}
22+
23+
/// A gpio chip.
24+
pub trait Chip {
25+
/// Context data associated with the gpio chip.
26+
///
27+
/// It determines the type of the context data passed to each of the methods of the trait.
28+
type Data: PointerWrapper + Sync + Send;
29+
30+
/// The methods to use to populate [`struct gpio_chip`]. This is typically populated with
31+
/// [`declare_gpio_chip_operations`].
32+
const TO_USE: ToUse;
33+
34+
/// Returns the direction of the given gpio line.
35+
fn get_direction(
36+
_data: <Self::Data as PointerWrapper>::Borrowed<'_>,
37+
_offset: u32,
38+
) -> Result<LineDirection> {
39+
Err(Error::ENOTSUPP)
40+
}
41+
42+
/// Configures the direction as input of the given gpio line.
43+
fn direction_input(
44+
_data: <Self::Data as PointerWrapper>::Borrowed<'_>,
45+
_offset: u32,
46+
) -> Result {
47+
Err(Error::EIO)
48+
}
49+
50+
/// Configures the direction as output of the given gpio line.
51+
///
52+
/// The value that will be initially output is also specified.
53+
fn direction_output(
54+
_data: <Self::Data as PointerWrapper>::Borrowed<'_>,
55+
_offset: u32,
56+
_value: bool,
57+
) -> Result {
58+
Err(Error::ENOTSUPP)
59+
}
60+
61+
/// Returns the current value of the given gpio line.
62+
fn get(_data: <Self::Data as PointerWrapper>::Borrowed<'_>, _offset: u32) -> Result<bool> {
63+
Err(Error::EIO)
64+
}
65+
66+
/// Sets the value of the given gpio line.
67+
fn set(_data: <Self::Data as PointerWrapper>::Borrowed<'_>, _offset: u32, _value: bool) {}
68+
}
69+
70+
/// Represents which fields of [`struct gpio_chip`] should be populated with pointers.
71+
///
72+
/// This is typically populated with the [`declare_gpio_chip_operations`] macro.
73+
pub struct ToUse {
74+
/// The `get_direction` field of [`struct gpio_chip`].
75+
pub get_direction: bool,
76+
77+
/// The `direction_input` field of [`struct gpio_chip`].
78+
pub direction_input: bool,
79+
80+
/// The `direction_output` field of [`struct gpio_chip`].
81+
pub direction_output: bool,
82+
83+
/// The `get` field of [`struct gpio_chip`].
84+
pub get: bool,
85+
86+
/// The `set` field of [`struct gpio_chip`].
87+
pub set: bool,
88+
}
89+
90+
/// A constant version where all values are set to `false`, that is, all supported fields will be
91+
/// set to null pointers.
92+
pub const USE_NONE: ToUse = ToUse {
93+
get_direction: false,
94+
direction_input: false,
95+
direction_output: false,
96+
get: false,
97+
set: false,
98+
};
99+
100+
/// Defines the [`Chip::TO_USE`] field based on a list of fields to be populated.
101+
#[macro_export]
102+
macro_rules! declare_gpio_chip_operations {
103+
() => {
104+
const TO_USE: $crate::gpio::ToUse = $crate::gpio::USE_NONE;
105+
};
106+
($($i:ident),+) => {
107+
const TO_USE: $crate::gpio::ToUse =
108+
$crate::gpio::ToUse {
109+
$($i: true),+ ,
110+
..$crate::gpio::USE_NONE
111+
};
112+
};
113+
}
114+
115+
/// A registration of a gpio chip.
116+
pub struct Registration<T: Chip> {
117+
gc: UnsafeCell<bindings::gpio_chip>,
118+
parent: Option<device::Device>,
119+
_p: PhantomData<T>,
120+
_pin: PhantomPinned,
121+
}
122+
123+
impl<T: Chip> Registration<T> {
124+
/// Creates a new [`Registration`] but does not register it yet.
125+
///
126+
/// It is allowed to move.
127+
pub fn new() -> Self {
128+
Self {
129+
parent: None,
130+
gc: UnsafeCell::new(bindings::gpio_chip::default()),
131+
_pin: PhantomPinned,
132+
_p: PhantomData,
133+
}
134+
}
135+
136+
/// Registers a gpio chip with the rest of the kernel.
137+
pub fn register(
138+
self: Pin<&mut Self>,
139+
gpio_count: u16,
140+
base: Option<i32>,
141+
parent: &dyn device::RawDevice,
142+
data: T::Data,
143+
) -> Result {
144+
// SAFETY: We never move out of `this`.
145+
let this = unsafe { self.get_unchecked_mut() };
146+
147+
if this.parent.is_some() {
148+
// Already registered.
149+
return Err(Error::EINVAL);
150+
}
151+
152+
{
153+
let gc = this.gc.get_mut();
154+
155+
// Set up the callbacks.
156+
gc.request = Some(bindings::gpiochip_generic_request);
157+
gc.free = Some(bindings::gpiochip_generic_free);
158+
if T::TO_USE.get_direction {
159+
gc.get_direction = Some(get_direction_callback::<T>);
160+
}
161+
if T::TO_USE.direction_input {
162+
gc.direction_input = Some(direction_input_callback::<T>);
163+
}
164+
if T::TO_USE.direction_output {
165+
gc.direction_output = Some(direction_output_callback::<T>);
166+
}
167+
if T::TO_USE.get {
168+
gc.get = Some(get_callback::<T>);
169+
}
170+
if T::TO_USE.set {
171+
gc.set = Some(set_callback::<T>);
172+
}
173+
174+
// When a base is not explicitly given, use -1 for one to be picked.
175+
if let Some(b) = base {
176+
gc.base = b;
177+
} else {
178+
gc.base = -1;
179+
}
180+
181+
gc.ngpio = gpio_count;
182+
gc.parent = parent.raw_device();
183+
gc.label = parent.name().as_char_ptr();
184+
185+
// TODO: Define `gc.owner` as well.
186+
}
187+
188+
let data_pointer = <T::Data as PointerWrapper>::into_pointer(data);
189+
// SAFETY: `gc` was initilised above, so it is valid.
190+
let ret = unsafe {
191+
bindings::gpiochip_add_data_with_key(
192+
this.gc.get(),
193+
data_pointer as _,
194+
core::ptr::null_mut(),
195+
core::ptr::null_mut(),
196+
)
197+
};
198+
if ret < 0 {
199+
// SAFETY: `data_pointer` was returned by `into_pointer` above.
200+
unsafe { T::Data::from_pointer(data_pointer) };
201+
return Err(Error::from_kernel_errno(ret));
202+
}
203+
204+
this.parent = Some(device::Device::from_dev(parent));
205+
Ok(())
206+
}
207+
}
208+
209+
// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between threads
210+
// or CPUs, so it is safe to share it.
211+
unsafe impl<T: Chip> Sync for Registration<T> {}
212+
213+
// SAFETY: Registration with and unregistration from the gpio subsystem can happen from any thread.
214+
// Additionally, `T::Data` (which is dropped during unregistration) is `Send`, so it is ok to move
215+
// `Registration` to different threads.
216+
unsafe impl<T: Chip> Send for Registration<T> {}
217+
218+
impl<T: Chip> Default for Registration<T> {
219+
fn default() -> Self {
220+
Self::new()
221+
}
222+
}
223+
224+
impl<T: Chip> Drop for Registration<T> {
225+
/// Removes the registration from the kernel if it has completed successfully before.
226+
fn drop(&mut self) {
227+
if self.parent.is_some() {
228+
// Get a pointer to the data stored in chip before destroying it.
229+
// SAFETY: `gc` was during registration, which is guaranteed to have succeeded (because
230+
// `parent` is `Some(_)`, so it remains valid.
231+
let data_pointer = unsafe { bindings::gpiochip_get_data(self.gc.get()) };
232+
233+
// SAFETY: By the same argument above, `gc` is still valid.
234+
unsafe { bindings::gpiochip_remove(self.gc.get()) };
235+
236+
// Free data as well.
237+
// SAFETY: `data_pointer` was returned by `into_pointer` during registration.
238+
unsafe { <T::Data as PointerWrapper>::from_pointer(data_pointer) };
239+
}
240+
}
241+
}
242+
243+
unsafe extern "C" fn get_direction_callback<T: Chip>(
244+
gc: *mut bindings::gpio_chip,
245+
offset: c_types::c_uint,
246+
) -> c_types::c_int {
247+
from_kernel_result! {
248+
// SAFETY: The value stored as chip data was returned by `into_pointer` during registration.
249+
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };
250+
Ok(T::get_direction(data, offset)? as i32)
251+
}
252+
}
253+
254+
unsafe extern "C" fn direction_input_callback<T: Chip>(
255+
gc: *mut bindings::gpio_chip,
256+
offset: c_types::c_uint,
257+
) -> c_types::c_int {
258+
from_kernel_result! {
259+
// SAFETY: The value stored as chip data was returned by `into_pointer` during registration.
260+
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };
261+
T::direction_input(data, offset)?;
262+
Ok(0)
263+
}
264+
}
265+
266+
unsafe extern "C" fn direction_output_callback<T: Chip>(
267+
gc: *mut bindings::gpio_chip,
268+
offset: c_types::c_uint,
269+
value: c_types::c_int,
270+
) -> c_types::c_int {
271+
from_kernel_result! {
272+
// SAFETY: The value stored as chip data was returned by `into_pointer` during registration.
273+
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };
274+
T::direction_output(data, offset, value != 0)?;
275+
Ok(0)
276+
}
277+
}
278+
279+
unsafe extern "C" fn get_callback<T: Chip>(
280+
gc: *mut bindings::gpio_chip,
281+
offset: c_types::c_uint,
282+
) -> c_types::c_int {
283+
from_kernel_result! {
284+
// SAFETY: The value stored as chip data was returned by `into_pointer` during registration.
285+
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };
286+
let v = T::get(data, offset)?;
287+
Ok(v as _)
288+
}
289+
}
290+
291+
unsafe extern "C" fn set_callback<T: Chip>(
292+
gc: *mut bindings::gpio_chip,
293+
offset: c_types::c_uint,
294+
value: c_types::c_int,
295+
) {
296+
// SAFETY: The value stored as chip data was returned by `into_pointer` during registration.
297+
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };
298+
T::set(data, offset, value != 0);
299+
}

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub mod driver;
5252
mod error;
5353
pub mod file;
5454
pub mod file_operations;
55+
pub mod gpio;
5556
pub mod irq;
5657
pub mod miscdev;
5758
pub mod pages;

0 commit comments

Comments
 (0)