capsules_extra/
date_time.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Real Time Clock driver
6//!
7//! Allows handling of the current date and time
8//!
9//! Authors: Irina Bradu <irinabradu.a@gmail.com>
10//!          Remus Rughinis <remus.rughinis.007@gmail.com>
11//!
12//! Usage
13//! -----
14//!
15//! ```rust,ignore
16//!  let grant_dt = create_capability!(capabilities::MemoryAllocationCapability);
17//!  let grant_date_time = board_kernel.create_grant(capsules::date_time::DRIVER_NUM, &grant_dt);
18//!
19//!  let date_time = static_init!(
20//!     capsules::date_time::DateTime<'static>,
21//!     capsules::date_time::DateTime::new(&peripherals.rtc, grant_date_time)
22//!  );
23//!  kernel::hil::date_time::DateTime::set_client(&peripherals.rtc, date_time);
24//! ```
25//!
26//! A DateTimeValues structure can be transformed to and from a u32 tuple in the following way:
27//!         first number (year, month, day_of_the_month):
28//!                 -last 5 bits store the day_of_the_month
29//!                 -previous 4 bits store the month
30//!                 -previous 12 bits store the year
31//!         second number (day_of_the_week, hour, minute, seconds):
32//!                 -last 6 bits store the seconds
33//!                 -previous 6 store the minute
34//!                 -previous 5 store the hour
35//!                 -previous 3 store the day_of_the_week
36
37use capsules_core::driver::NUM;
38use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
39use kernel::hil::date_time;
40
41use kernel::errorcode::into_statuscode;
42use kernel::syscall::{CommandReturn, SyscallDriver};
43use kernel::utilities::cells::OptionalCell;
44use kernel::{ErrorCode, ProcessId};
45
46pub const DRIVER_NUM: usize = NUM::DateTime as usize;
47
48#[derive(Clone, Copy, PartialEq, Debug)]
49pub enum DateTimeCommand {
50    ReadDateTime,
51    SetDateTime(u32, u32),
52}
53
54#[derive(Default)]
55pub struct AppData {
56    task: Option<DateTimeCommand>,
57}
58
59pub struct DateTimeCapsule<'a, DateTime: date_time::DateTime<'a>> {
60    date_time: &'a DateTime,
61    apps: Grant<AppData, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
62    in_progress: OptionalCell<ProcessId>,
63}
64
65fn month_as_u32(month: date_time::Month) -> u32 {
66    match month {
67        date_time::Month::January => 1,
68        date_time::Month::February => 2,
69        date_time::Month::March => 3,
70        date_time::Month::April => 4,
71        date_time::Month::May => 5,
72        date_time::Month::June => 6,
73        date_time::Month::July => 7,
74        date_time::Month::August => 8,
75        date_time::Month::September => 9,
76        date_time::Month::October => 10,
77        date_time::Month::November => 11,
78        date_time::Month::December => 12,
79    }
80}
81
82fn u32_as_month(month_num: u32) -> Result<date_time::Month, ErrorCode> {
83    match month_num {
84        1 => Ok(date_time::Month::January),
85        2 => Ok(date_time::Month::February),
86        3 => Ok(date_time::Month::March),
87        4 => Ok(date_time::Month::April),
88        5 => Ok(date_time::Month::May),
89        6 => Ok(date_time::Month::June),
90        7 => Ok(date_time::Month::July),
91        8 => Ok(date_time::Month::August),
92        9 => Ok(date_time::Month::September),
93        10 => Ok(date_time::Month::October),
94        11 => Ok(date_time::Month::November),
95        12 => Ok(date_time::Month::December),
96        _ => Err(ErrorCode::INVAL),
97    }
98}
99
100fn dotw_as_u32(dotw: date_time::DayOfWeek) -> u32 {
101    match dotw {
102        date_time::DayOfWeek::Sunday => 0,
103        date_time::DayOfWeek::Monday => 1,
104        date_time::DayOfWeek::Tuesday => 2,
105        date_time::DayOfWeek::Wednesday => 3,
106        date_time::DayOfWeek::Thursday => 4,
107        date_time::DayOfWeek::Friday => 5,
108        date_time::DayOfWeek::Saturday => 6,
109    }
110}
111
112fn u32_as_dotw(dotw_num: u32) -> Result<date_time::DayOfWeek, ErrorCode> {
113    match dotw_num {
114        0 => Ok(date_time::DayOfWeek::Sunday),
115        1 => Ok(date_time::DayOfWeek::Monday),
116        2 => Ok(date_time::DayOfWeek::Tuesday),
117        3 => Ok(date_time::DayOfWeek::Wednesday),
118        4 => Ok(date_time::DayOfWeek::Thursday),
119        5 => Ok(date_time::DayOfWeek::Friday),
120        6 => Ok(date_time::DayOfWeek::Saturday),
121        _ => Err(ErrorCode::INVAL),
122    }
123}
124
125/// Transforms two u32 numbers into a DateTimeValues structure (year, month, dotm, dotw, hour, minute, seconds)
126/// Check file documentation for details on how the u32 tuple stores data
127fn date_from_u32_tuple(date: u32, time: u32) -> Result<date_time::DateTimeValues, ErrorCode> {
128    let month_num = date % (1 << 9) / (1 << 5);
129    let month_name = u32_as_month(month_num)?;
130
131    let dotw_num = time % (1 << 20) / (1 << 17);
132    let dotw_name = u32_as_dotw(dotw_num)?;
133
134    let date_result = date_time::DateTimeValues {
135        year: (date % (1 << 21) / (1 << 9)) as u16,
136        month: month_name,
137        day: (date % (1 << 5)) as u8,
138
139        day_of_week: dotw_name,
140        hour: (time % (1 << 17) / (1 << 12)) as u8,
141        minute: (time % (1 << 12) / (1 << 6)) as u8,
142        seconds: (time % (1 << 6)) as u8,
143    };
144
145    if !(date_result.day <= 31) {
146        return Err(ErrorCode::INVAL);
147    }
148    if !(date_result.hour <= 24) {
149        return Err(ErrorCode::INVAL);
150    }
151    if !(date_result.minute <= 60) {
152        return Err(ErrorCode::INVAL);
153    }
154    if !(date_result.seconds <= 60) {
155        return Err(ErrorCode::INVAL);
156    }
157
158    Ok(date_result)
159}
160
161/// Transforms DateTimeValues structure (year, month, dotm, dotw, hour, minute, seconds) into two u32 numbers
162/// Check file documentation for details on how the u32 numbers stores data
163/// The two u32 numbers are returned as a tuple
164fn date_as_u32_tuple(set_date: date_time::DateTimeValues) -> Result<(u32, u32), ErrorCode> {
165    let month = month_as_u32(set_date.month);
166    let dotw = dotw_as_u32(set_date.day_of_week);
167
168    let date =
169        set_date.year as u32 * (1 << 9) as u32 + month * (1 << 5) as u32 + set_date.day as u32;
170    let time = dotw * (1 << 17) as u32
171        + set_date.hour as u32 * (1 << 12) as u32
172        + set_date.minute as u32 * (1 << 6) as u32
173        + set_date.seconds as u32;
174
175    Ok((date, time))
176}
177
178impl<'a, DateTime: date_time::DateTime<'a>> DateTimeCapsule<'a, DateTime> {
179    pub fn new(
180        date_time: &'a DateTime,
181        grant: Grant<AppData, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
182    ) -> DateTimeCapsule<'a, DateTime> {
183        DateTimeCapsule {
184            date_time,
185            apps: grant,
186            in_progress: OptionalCell::empty(),
187        }
188    }
189
190    fn call_driver(&self, command: DateTimeCommand, processid: ProcessId) -> Result<(), ErrorCode> {
191        match command {
192            DateTimeCommand::ReadDateTime => {
193                let date_result = self.date_time.get_date_time();
194                match date_result {
195                    Result::Ok(()) => {
196                        self.in_progress.set(processid);
197                        Ok(())
198                    }
199                    Result::Err(e) => Err(e),
200                }
201            }
202            DateTimeCommand::SetDateTime(r2, r3) => {
203                let date = match date_from_u32_tuple(r2, r3) {
204                    Result::Ok(d) => d,
205                    Result::Err(e) => {
206                        return Err(e);
207                    }
208                };
209
210                let get_date_result = self.date_time.set_date_time(date);
211
212                match get_date_result {
213                    Result::Ok(()) => {
214                        self.in_progress.set(processid);
215                        Ok(())
216                    }
217                    Result::Err(e) => Err(e),
218                }
219            }
220        }
221    }
222
223    fn enqueue_command(&self, command: DateTimeCommand, processid: ProcessId) -> CommandReturn {
224        let grant_enter_res = self.apps.enter(processid, |app, _| {
225            if !(app.task.is_none()) {
226                CommandReturn::failure(ErrorCode::BUSY)
227            } else {
228                app.task = Some(command);
229                CommandReturn::success()
230            }
231        });
232
233        // If no command is currently run, run the current command
234        if self.in_progress.is_none() {
235            match grant_enter_res {
236                Ok(_) => match self.call_driver(command, processid) {
237                    Ok(()) => CommandReturn::success(),
238                    Err(e) => CommandReturn::failure(e),
239                },
240                Err(_e) => CommandReturn::failure(ErrorCode::FAIL),
241            }
242        } else {
243            match grant_enter_res {
244                Ok(_) => CommandReturn::success(),
245                Err(_e) => CommandReturn::failure(ErrorCode::FAIL),
246            }
247        }
248    }
249
250    fn queue_next_command(&self) {
251        self.apps.iter().find_map(|grant| {
252            let processid = grant.processid();
253            grant.enter(|app, kernel| {
254                app.task.map_or(None, |command| {
255                    let command_return = self.call_driver(command, processid);
256                    match command_return {
257                        Ok(()) => Some(()),
258                        Err(e) => {
259                            let upcall_status = into_statuscode(Err(e));
260                            kernel.schedule_upcall(0, (upcall_status, 0, 0)).ok();
261                            None
262                        }
263                    }
264                })
265            })
266        });
267    }
268}
269
270impl<'a, DateTime: date_time::DateTime<'a>> date_time::DateTimeClient
271    for DateTimeCapsule<'a, DateTime>
272{
273    fn get_date_time_done(&self, datetime: Result<date_time::DateTimeValues, ErrorCode>) {
274        self.in_progress.clear();
275        let mut upcall_status: usize = into_statuscode(Ok(()));
276        let mut upcall_r1: usize = 0;
277        let mut upcall_r2: usize = 0;
278
279        for cntr in self.apps.iter() {
280            cntr.enter(|app, upcalls| {
281                if app.task == Some(DateTimeCommand::ReadDateTime) {
282                    app.task = None;
283                    match datetime {
284                        Result::Ok(date) => {
285                            let (year_month_dotm, dotw_hour_min_sec) = match date_as_u32_tuple(date)
286                            {
287                                Result::Ok(t) => t,
288                                Result::Err(e) => {
289                                    upcall_status = into_statuscode(Result::Err(e));
290                                    (0, 0)
291                                }
292                            };
293
294                            upcall_r1 = year_month_dotm as usize;
295                            upcall_r2 = dotw_hour_min_sec as usize;
296                        }
297                        Result::Err(e) => {
298                            upcall_status = into_statuscode(Result::Err(e));
299                        }
300                    }
301
302                    upcalls
303                        .schedule_upcall(0, (upcall_status, upcall_r1, upcall_r2))
304                        .ok();
305                }
306            });
307        }
308
309        self.queue_next_command();
310    }
311
312    fn set_date_time_done(&self, result: Result<(), ErrorCode>) {
313        // in_progress.take() also sets the OptionalCell to None
314        let processid = self.in_progress.take().unwrap();
315        let _enter_grant = self.apps.enter(processid, |app, upcalls| {
316            app.task = None;
317
318            upcalls
319                .schedule_upcall(0, (into_statuscode(result), 0, 0))
320                .ok();
321        });
322
323        self.queue_next_command();
324    }
325}
326
327impl<'a, DateTime: date_time::DateTime<'a>> SyscallDriver for DateTimeCapsule<'a, DateTime> {
328    fn command(
329        &self,
330        command_number: usize,
331        r2: usize,
332        r3: usize,
333        process_id: ProcessId,
334    ) -> CommandReturn {
335        match command_number {
336            0 => CommandReturn::success(),
337            1 => self.enqueue_command(DateTimeCommand::ReadDateTime, process_id),
338            2 => self.enqueue_command(
339                DateTimeCommand::SetDateTime(r2 as u32, r3 as u32),
340                process_id,
341            ),
342            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
343        }
344    }
345
346    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
347        self.apps.enter(processid, |_, _| {})
348    }
349}