Skip to main content

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///
163/// Check file documentation for details on how the u32 numbers stores data
164/// The two u32 numbers are returned as a tuple
165fn date_as_u32_tuple(set_date: date_time::DateTimeValues) -> Result<(u32, u32), ErrorCode> {
166    let month = month_as_u32(set_date.month);
167    let dotw = dotw_as_u32(set_date.day_of_week);
168
169    let date =
170        set_date.year as u32 * (1 << 9) as u32 + month * (1 << 5) as u32 + set_date.day as u32;
171    let time = dotw * (1 << 17) as u32
172        + set_date.hour as u32 * (1 << 12) as u32
173        + set_date.minute as u32 * (1 << 6) as u32
174        + set_date.seconds as u32;
175
176    Ok((date, time))
177}
178
179impl<'a, DateTime: date_time::DateTime<'a>> DateTimeCapsule<'a, DateTime> {
180    pub fn new(
181        date_time: &'a DateTime,
182        grant: Grant<AppData, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
183    ) -> DateTimeCapsule<'a, DateTime> {
184        DateTimeCapsule {
185            date_time,
186            apps: grant,
187            in_progress: OptionalCell::empty(),
188        }
189    }
190
191    fn call_driver(&self, command: DateTimeCommand, processid: ProcessId) -> Result<(), ErrorCode> {
192        match command {
193            DateTimeCommand::ReadDateTime => {
194                let date_result = self.date_time.get_date_time();
195                match date_result {
196                    Result::Ok(()) => {
197                        self.in_progress.set(processid);
198                        Ok(())
199                    }
200                    Result::Err(e) => Err(e),
201                }
202            }
203            DateTimeCommand::SetDateTime(r2, r3) => {
204                let date = match date_from_u32_tuple(r2, r3) {
205                    Result::Ok(d) => d,
206                    Result::Err(e) => {
207                        return Err(e);
208                    }
209                };
210
211                let get_date_result = self.date_time.set_date_time(date);
212
213                match get_date_result {
214                    Result::Ok(()) => {
215                        self.in_progress.set(processid);
216                        Ok(())
217                    }
218                    Result::Err(e) => Err(e),
219                }
220            }
221        }
222    }
223
224    fn enqueue_command(&self, command: DateTimeCommand, processid: ProcessId) -> CommandReturn {
225        let grant_enter_res = self.apps.enter(processid, |app, _| {
226            if !(app.task.is_none()) {
227                CommandReturn::failure(ErrorCode::BUSY)
228            } else {
229                app.task = Some(command);
230                CommandReturn::success()
231            }
232        });
233
234        // If no command is currently run, run the current command
235        if self.in_progress.is_none() {
236            match grant_enter_res {
237                Ok(_) => match self.call_driver(command, processid) {
238                    Ok(()) => CommandReturn::success(),
239                    Err(e) => CommandReturn::failure(e),
240                },
241                Err(_e) => CommandReturn::failure(ErrorCode::FAIL),
242            }
243        } else {
244            match grant_enter_res {
245                Ok(_) => CommandReturn::success(),
246                Err(_e) => CommandReturn::failure(ErrorCode::FAIL),
247            }
248        }
249    }
250
251    fn queue_next_command(&self) {
252        self.apps.iter().find_map(|grant| {
253            let processid = grant.processid();
254            grant.enter(|app, kernel| {
255                app.task.and_then(|command| {
256                    let command_return = self.call_driver(command, processid);
257                    match command_return {
258                        Ok(()) => Some(()),
259                        Err(e) => {
260                            let upcall_status = into_statuscode(Err(e));
261                            let _ = kernel.schedule_upcall(0, (upcall_status, 0, 0));
262                            None
263                        }
264                    }
265                })
266            })
267        });
268    }
269}
270
271impl<'a, DateTime: date_time::DateTime<'a>> date_time::DateTimeClient
272    for DateTimeCapsule<'a, DateTime>
273{
274    fn get_date_time_done(&self, datetime: Result<date_time::DateTimeValues, ErrorCode>) {
275        self.in_progress.clear();
276        let mut upcall_status: usize = into_statuscode(Ok(()));
277        let mut upcall_r1: usize = 0;
278        let mut upcall_r2: usize = 0;
279
280        for cntr in self.apps.iter() {
281            cntr.enter(|app, upcalls| {
282                if app.task == Some(DateTimeCommand::ReadDateTime) {
283                    app.task = None;
284                    match datetime {
285                        Result::Ok(date) => {
286                            let (year_month_dotm, dotw_hour_min_sec) = match date_as_u32_tuple(date)
287                            {
288                                Result::Ok(t) => t,
289                                Result::Err(e) => {
290                                    upcall_status = into_statuscode(Result::Err(e));
291                                    (0, 0)
292                                }
293                            };
294
295                            upcall_r1 = year_month_dotm as usize;
296                            upcall_r2 = dotw_hour_min_sec as usize;
297                        }
298                        Result::Err(e) => {
299                            upcall_status = into_statuscode(Result::Err(e));
300                        }
301                    }
302
303                    let _ = upcalls.schedule_upcall(0, (upcall_status, upcall_r1, upcall_r2));
304                }
305            });
306        }
307
308        self.queue_next_command();
309    }
310
311    fn set_date_time_done(&self, result: Result<(), ErrorCode>) {
312        // in_progress.take() also sets the OptionalCell to None
313        let processid = self.in_progress.take().unwrap();
314        let _enter_grant = self.apps.enter(processid, |app, upcalls| {
315            app.task = None;
316
317            let _ = upcalls.schedule_upcall(0, (into_statuscode(result), 0, 0));
318        });
319
320        self.queue_next_command();
321    }
322}
323
324impl<'a, DateTime: date_time::DateTime<'a>> SyscallDriver for DateTimeCapsule<'a, DateTime> {
325    fn command(
326        &self,
327        command_number: usize,
328        r2: usize,
329        r3: usize,
330        process_id: ProcessId,
331    ) -> CommandReturn {
332        match command_number {
333            0 => CommandReturn::success(),
334            1 => self.enqueue_command(DateTimeCommand::ReadDateTime, process_id),
335            2 => self.enqueue_command(
336                DateTimeCommand::SetDateTime(r2 as u32, r3 as u32),
337                process_id,
338            ),
339            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
340        }
341    }
342
343    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
344        self.apps.enter(processid, |_, _| {})
345    }
346}