1use 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
125fn 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
161fn 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 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 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}