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, |_, _| {})
    }
}