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> {
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 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 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}