Skip to content

Commit 3119f2c

Browse files
committed
rust: add GPIO support.
This allows drivers to register with the GPIO subsystem, so that they can be exposed to clients. It does not include IRQ chip support, which will come in a subsequent patch. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 540942e commit 3119f2c

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)