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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
use kernel::hil;
use kernel::syscall::{CommandReturn, SyscallDriver};
use kernel::utilities::cells::OptionalCell;
use kernel::{ErrorCode, ProcessId};
/// Syscall driver number.
use capsules_core::driver;
pub const DRIVER_NUM: usize = driver::NUM::Pwm as usize;
// An empty app, for potential uses in future updates of the driver
#[derive(Default)]
pub struct App;
pub struct Pwm<'a, const NUM_PINS: usize> {
/// The usable pwm pins.
pwm_pins: &'a [&'a dyn hil::pwm::PwmPin; NUM_PINS],
/// Per-app state.
apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
/// An array of apps associated to their reserved pins.
active_process: [OptionalCell<ProcessId>; NUM_PINS],
}
impl<'a, const NUM_PINS: usize> Pwm<'a, NUM_PINS> {
pub fn new(
pwm_pins: &'a [&'a dyn hil::pwm::PwmPin; NUM_PINS],
grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
) -> Pwm<'a, NUM_PINS> {
assert!(u16::try_from(NUM_PINS).is_ok());
const EMPTY: OptionalCell<ProcessId> = OptionalCell::empty();
Pwm {
pwm_pins,
apps: grant,
active_process: [EMPTY; NUM_PINS],
}
}
pub fn claim_pin(&self, processid: ProcessId, pin: usize) -> bool {
// Attempt to get the app that is using the pin.
self.active_process[pin].map_or(true, |id| {
// If the app is empty, that means that there is no app currently using this pin,
// therefore the pin could be usable by the new app
if id == processid {
// The same app is trying to access the pin it has access to, valid
true
} else {
// An app is trying to access another app's pin, invalid
false
}
})
}
pub fn release_pin(&self, pin: usize) {
// Release the claimed pin so that it can now be used by another process.
self.active_process[pin].clear();
}
}
/// Provide an interface for userland.
impl<'a, const NUM_PINS: usize> SyscallDriver for Pwm<'a, NUM_PINS> {
/// Command interface.
///
/// ### `command_num`
///
/// - `0`: Driver existence check.
/// - `1`: Start the PWM pin output. First 16 bits of `data1` are used for the duty cycle, as a
/// percentage with 2 decimals, and the last 16 bits of `data1` are used for the PWM channel
/// to be controlled. `data2` is used for the frequency in hertz. For the duty cycle, 100% is
/// the max duty cycle for this pin.
/// - `2`: Stop the PWM output.
/// - `3`: Return the maximum possible frequency for this pin.
/// - `4`: Return number of PWM pins if this driver is included on the platform.
fn command(
&self,
command_num: usize,
data1: usize,
data2: usize,
processid: ProcessId,
) -> CommandReturn {
match command_num {
// Check existence.
0 => CommandReturn::success(),
// Start the pwm output.
// data1 stores the duty cycle and the pin number in the format
// +------------------+------------------+
// | duty cycle (u16) | pwm pin (u16) |
// +------------------+------------------+
// This format was chosen because there are only 2 parameters in the command function that can be used for storing values,
// but in this case, 3 values are needed (pin, frequency, duty cycle), so data1 stores two of these values that can be
// represented using only 16 bits.
1 => {
let pin = data1 & ((1 << 16) - 1);
let duty_cycle = data1 >> 16;
let frequency_hz = data2;
if pin >= NUM_PINS {
// App asked to use a pin that doesn't exist.
CommandReturn::failure(ErrorCode::INVAL)
} else {
if !self.claim_pin(processid, pin) {
// App cannot claim pin.
CommandReturn::failure(ErrorCode::RESERVE)
} else {
// App can claim pin, start pwm pin at given frequency and duty_cycle.
self.active_process[pin].set(processid);
// Duty cycle is represented as a 4 digit number, so we divide by 10000 to get the percentage of the max duty cycle.
// e.g.: a duty cycle of 60.5% is represented as 6050, so the actual value of the duty cycle is
// 6050 * max_duty_cycle / 10000 = 0.605 * max_duty_cycle
self.pwm_pins[pin]
.start(
frequency_hz,
duty_cycle * self.pwm_pins[pin].get_maximum_duty_cycle() / 10000,
)
.into()
}
}
}
// Stop the PWM output.
2 => {
let pin = data1;
if pin >= NUM_PINS {
// App asked to use a pin that doesn't exist.
CommandReturn::failure(ErrorCode::INVAL)
} else {
if !self.claim_pin(processid, pin) {
// App cannot claim pin.
CommandReturn::failure(ErrorCode::RESERVE)
} else if self.active_process[pin].is_none() {
// If there is no active app, the pwm pin isn't in use.
CommandReturn::failure(ErrorCode::OFF)
} else {
// Release the pin and stop pwm output.
self.release_pin(pin);
self.pwm_pins[pin].stop().into()
}
}
}
// Get max frequency of pin.
3 => {
let pin = data1;
if pin >= NUM_PINS {
CommandReturn::failure(ErrorCode::INVAL)
} else {
CommandReturn::success_u32(self.pwm_pins[pin].get_maximum_frequency_hz() as u32)
}
}
// Return number of usable PWM pins.
4 => CommandReturn::success_u32(NUM_PINS as u32),
_ => CommandReturn::failure(ErrorCode::NOSUPPORT),
}
}
fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
self.apps.enter(processid, |_, _| {})
}
}