|
| 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 | +} |
0 commit comments