source: mainline/uspace/drv/time/cmos-rtc/cmos-rtc.c@ f1380b7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f1380b7 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 17.4 KB
Line 
1/*
2 * Copyright (c) 2012 Maurizio Lombardi
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @defgroup CMOS RTC driver.
31 * @brief HelenOS RTC driver.
32 * @{
33 */
34
35/** @file
36 */
37
38#include <errno.h>
39#include <ddi.h>
40#include <as.h>
41#include <libarch/barrier.h>
42#include <stdio.h>
43#include <ddf/driver.h>
44#include <ddf/log.h>
45#include <ops/clock_dev.h>
46#include <ops/battery_dev.h>
47#include <fibril_synch.h>
48#include <device/hw_res.h>
49#include <macros.h>
50#include <time.h>
51
52#include "cmos-regs.h"
53
54#define NAME "cmos-rtc"
55
56#define REG_COUNT 2
57
58#define REG_SEL_PORT(port) (port)
59#define REG_RW_PORT(port) ((port) + 1)
60
61typedef struct rtc {
62 /** DDF device node */
63 ddf_dev_t *dev;
64 /** DDF function node */
65 ddf_fun_t *fun;
66 /** The fibril mutex for synchronizing the access to the device */
67 fibril_mutex_t mutex;
68 /** The base I/O address of the device registers */
69 ioport8_t *io_addr;
70 /** The I/O port used to access the CMOS registers */
71 ioport8_t *port;
72 /** true if device is removed */
73 bool removed;
74 /** number of connected clients */
75 int clients_connected;
76 /** time at which the system booted */
77 struct timeval boot_time;
78} rtc_t;
79
80static rtc_t *dev_rtc(ddf_dev_t *dev);
81static rtc_t *fun_rtc(ddf_fun_t *fun);
82static errno_t
83rtc_battery_status_get(ddf_fun_t *fun, battery_status_t *status);
84static errno_t rtc_time_get(ddf_fun_t *fun, struct tm *t);
85static errno_t rtc_time_set(ddf_fun_t *fun, struct tm *t);
86static errno_t rtc_dev_add(ddf_dev_t *dev);
87static errno_t rtc_dev_initialize(rtc_t *rtc);
88static bool rtc_pio_enable(rtc_t *rtc);
89static void rtc_dev_cleanup(rtc_t *rtc);
90static errno_t rtc_open(ddf_fun_t *fun);
91static void rtc_close(ddf_fun_t *fun);
92static bool rtc_update_in_progress(rtc_t *rtc);
93static int rtc_register_read(rtc_t *rtc, int reg);
94static unsigned bcd2bin(unsigned bcd);
95static unsigned bin2bcd(unsigned binary);
96static errno_t rtc_dev_remove(ddf_dev_t *dev);
97static void rtc_register_write(rtc_t *rtc, int reg, int data);
98static bool is_battery_ok(rtc_t *rtc);
99static errno_t rtc_fun_online(ddf_fun_t *fun);
100static errno_t rtc_fun_offline(ddf_fun_t *fun);
101
102static ddf_dev_ops_t rtc_dev_ops;
103
104/** The RTC device driver's standard operations */
105static driver_ops_t rtc_ops = {
106 .dev_add = rtc_dev_add,
107 .dev_remove = rtc_dev_remove,
108 .fun_online = rtc_fun_online,
109 .fun_offline = rtc_fun_offline,
110};
111
112/** The RTC device driver structure */
113static driver_t rtc_driver = {
114 .name = NAME,
115 .driver_ops = &rtc_ops,
116};
117
118/** Clock interface */
119static clock_dev_ops_t rtc_clock_dev_ops = {
120 .time_get = rtc_time_get,
121 .time_set = rtc_time_set,
122};
123
124/** Battery powered device interface */
125static battery_dev_ops_t rtc_battery_dev_ops = {
126 .battery_status_get = rtc_battery_status_get,
127 .battery_charge_level_get = NULL,
128};
129
130/** Obtain soft state structure from device node */
131static rtc_t *
132dev_rtc(ddf_dev_t *dev)
133{
134 return ddf_dev_data_get(dev);
135}
136
137/** Obtain soft state structure from function node */
138static rtc_t *
139fun_rtc(ddf_fun_t *fun)
140{
141 return dev_rtc(ddf_fun_get_dev(fun));
142}
143
144/** Initialize the RTC driver */
145static void
146rtc_init(void)
147{
148 ddf_log_init(NAME);
149
150 rtc_dev_ops.open = rtc_open;
151 rtc_dev_ops.close = rtc_close;
152
153 rtc_dev_ops.interfaces[CLOCK_DEV_IFACE] = &rtc_clock_dev_ops;
154 rtc_dev_ops.interfaces[BATTERY_DEV_IFACE] = &rtc_battery_dev_ops;
155 rtc_dev_ops.default_handler = NULL;
156}
157
158/** Clean up the RTC soft state
159 *
160 * @param rtc The RTC device
161 */
162static void
163rtc_dev_cleanup(rtc_t *rtc)
164{
165}
166
167/** Enable the I/O ports of the device
168 *
169 * @param rtc The real time clock device
170 *
171 * @return true in case of success, false otherwise
172 */
173static bool
174rtc_pio_enable(rtc_t *rtc)
175{
176 if (pio_enable((void *) rtc->io_addr, REG_COUNT,
177 (void **) &rtc->port)) {
178
179 ddf_msg(LVL_ERROR, "Cannot map the port %lx"
180 " for device %s", (long unsigned int)rtc->io_addr,
181 ddf_dev_get_name(rtc->dev));
182 return false;
183 }
184
185 return true;
186}
187
188/** Initialize the RTC device
189 *
190 * @param rtc Pointer to the RTC device
191 *
192 * @return EOK on success or an error code
193 */
194static errno_t
195rtc_dev_initialize(rtc_t *rtc)
196{
197 errno_t rc;
198 size_t i;
199 hw_resource_t *res;
200 bool ioport = false;
201 async_sess_t *parent_sess;
202
203 ddf_msg(LVL_DEBUG, "rtc_dev_initialize %s", ddf_dev_get_name(rtc->dev));
204
205 rtc->boot_time.tv_sec = 0;
206 rtc->boot_time.tv_usec = 0;
207 rtc->clients_connected = 0;
208
209 hw_resource_list_t hw_resources;
210 memset(&hw_resources, 0, sizeof(hw_resource_list_t));
211
212 /* Connect to the parent's driver */
213
214 parent_sess = ddf_dev_parent_sess_get(rtc->dev);
215 if (parent_sess == NULL) {
216 ddf_msg(LVL_ERROR, "Failed to connect to parent driver\
217 of device %s.", ddf_dev_get_name(rtc->dev));
218 rc = ENOENT;
219 goto error;
220 }
221
222 /* Get the HW resources */
223 rc = hw_res_get_resource_list(parent_sess, &hw_resources);
224 if (rc != EOK) {
225 ddf_msg(LVL_ERROR, "Failed to get HW resources\
226 for device %s", ddf_dev_get_name(rtc->dev));
227 goto error;
228 }
229
230 for (i = 0; i < hw_resources.count; ++i) {
231 res = &hw_resources.resources[i];
232
233 if (res->res.io_range.size < REG_COUNT) {
234 ddf_msg(LVL_ERROR, "I/O range assigned to \
235 device %s is too small",
236 ddf_dev_get_name(rtc->dev));
237 rc = ELIMIT;
238 continue;
239 }
240
241 rtc->io_addr = (ioport8_t *) (long) res->res.io_range.address;
242 ioport = true;
243 ddf_msg(LVL_NOTE, "Device %s was assigned I/O address "
244 "0x%lx", ddf_dev_get_name(rtc->dev),
245 (unsigned long int) rtc->io_addr);
246 rc = EOK;
247 break;
248 }
249
250 if (rc != EOK)
251 goto error;
252
253 if (!ioport) {
254 /* No I/O address assigned to this device */
255 ddf_msg(LVL_ERROR, "Missing HW resource for device %s",
256 ddf_dev_get_name(rtc->dev));
257 rc = ENOENT;
258 goto error;
259 }
260
261 hw_res_clean_resource_list(&hw_resources);
262
263 return EOK;
264
265error:
266 rtc_dev_cleanup(rtc);
267 hw_res_clean_resource_list(&hw_resources);
268
269 return rc;
270}
271
272/** Read a register from the CMOS memory
273 *
274 * @param rtc The rtc device
275 * @param reg The index of the register to read
276 *
277 * @return The value of the register
278 */
279static int
280rtc_register_read(rtc_t *rtc, int reg)
281{
282 pio_write_8(REG_SEL_PORT(rtc->port), reg);
283 return pio_read_8(REG_RW_PORT(rtc->port));
284}
285
286/** Write a register to the CMOS memory
287 *
288 * @param rtc The rtc device
289 * @param reg The index of the register to write
290 * @param data The data to write
291 */
292static void
293rtc_register_write(rtc_t *rtc, int reg, int data)
294{
295 pio_write_8(REG_SEL_PORT(rtc->port), reg);
296 pio_write_8(REG_RW_PORT(rtc->port), data);
297}
298
299/** Check if an update is in progress
300 *
301 * @param rtc The rtc device
302 *
303 * @return true if an update is in progress, false otherwise
304 */
305static bool
306rtc_update_in_progress(rtc_t *rtc)
307{
308 return rtc_register_read(rtc, RTC_STATUS_A) & RTC_A_UPDATE;
309}
310
311/** Read the current time from the CMOS
312 *
313 * @param fun The RTC function
314 * @param t Pointer to the time variable
315 *
316 * @return EOK on success or an error code
317 */
318static errno_t
319rtc_time_get(ddf_fun_t *fun, struct tm *t)
320{
321 bool bcd_mode;
322 bool pm_mode = false;
323 rtc_t *rtc = fun_rtc(fun);
324
325 fibril_mutex_lock(&rtc->mutex);
326
327 if (rtc->boot_time.tv_sec) {
328 /* There is no need to read the current time from the
329 * device because it has already been cached.
330 */
331
332 struct timeval curtime;
333
334 getuptime(&curtime);
335 tv_add(&curtime, &rtc->boot_time);
336 fibril_mutex_unlock(&rtc->mutex);
337
338 return time_tv2tm(&curtime, t);
339 }
340
341 /* Check if the RTC battery is OK */
342 if (!is_battery_ok(rtc)) {
343 fibril_mutex_unlock(&rtc->mutex);
344 return EIO;
345 }
346
347 /* Microseconds are below RTC's resolution, assume 0. */
348 t->tm_usec = 0;
349
350 /* now read the registers */
351 do {
352 /* Suspend until the update process has finished */
353 while (rtc_update_in_progress(rtc))
354 ;
355
356 t->tm_sec = rtc_register_read(rtc, RTC_SEC);
357 t->tm_min = rtc_register_read(rtc, RTC_MIN);
358 t->tm_hour = rtc_register_read(rtc, RTC_HOUR);
359 t->tm_mday = rtc_register_read(rtc, RTC_DAY);
360 t->tm_mon = rtc_register_read(rtc, RTC_MON);
361 t->tm_year = rtc_register_read(rtc, RTC_YEAR);
362
363 /* Now check if it is stable */
364 } while(t->tm_sec != rtc_register_read(rtc, RTC_SEC) ||
365 t->tm_min != rtc_register_read(rtc, RTC_MIN) ||
366 t->tm_mday != rtc_register_read(rtc, RTC_DAY) ||
367 t->tm_mon != rtc_register_read(rtc, RTC_MON) ||
368 t->tm_year != rtc_register_read(rtc, RTC_YEAR));
369
370 /* Check if the RTC is working in 12h mode */
371 bool _12h_mode = !(rtc_register_read(rtc, RTC_STATUS_B) &
372 RTC_B_24H);
373 if (_12h_mode) {
374 /* The RTC is working in 12h mode, check if it is AM or PM */
375 if (t->tm_hour & 0x80) {
376 /* PM flag is active, it must be cleared */
377 t->tm_hour &= ~0x80;
378 pm_mode = true;
379 }
380 }
381
382 /* Check if the RTC is working in BCD mode */
383 bcd_mode = !(rtc_register_read(rtc, RTC_STATUS_B) & RTC_B_BCD);
384 if (bcd_mode) {
385 t->tm_sec = bcd2bin(t->tm_sec);
386 t->tm_min = bcd2bin(t->tm_min);
387 t->tm_hour = bcd2bin(t->tm_hour);
388 t->tm_mday = bcd2bin(t->tm_mday);
389 t->tm_mon = bcd2bin(t->tm_mon);
390 t->tm_year = bcd2bin(t->tm_year);
391 }
392
393 if (_12h_mode) {
394 /* Convert to 24h mode */
395 if (pm_mode) {
396 if (t->tm_hour < 12)
397 t->tm_hour += 12;
398 } else if (t->tm_hour == 12)
399 t->tm_hour = 0;
400 }
401
402 /* Count the months starting from 0, not from 1 */
403 t->tm_mon--;
404
405 if (t->tm_year < 100) {
406 /* tm_year is the number of years since 1900 but the
407 * RTC epoch is 2000.
408 */
409 t->tm_year += 100;
410 }
411
412 /* Try to normalize the content of the tm structure */
413 time_t r = mktime(t);
414 errno_t result;
415
416 if (r < 0)
417 result = EINVAL;
418 else {
419 struct timeval uptime;
420
421 getuptime(&uptime);
422 rtc->boot_time.tv_sec = r;
423 rtc->boot_time.tv_usec = t->tm_usec; /* normalized */
424 tv_sub(&rtc->boot_time, &uptime);
425 result = EOK;
426 }
427
428 fibril_mutex_unlock(&rtc->mutex);
429
430 return result;
431}
432
433/** Set the time in the RTC
434 *
435 * @param fun The RTC function
436 * @param t The time value to set
437 *
438 * @return EOK or an error code
439 */
440static errno_t
441rtc_time_set(ddf_fun_t *fun, struct tm *t)
442{
443 bool bcd_mode;
444 time_t norm_time;
445 struct timeval uptime;
446 struct timeval ntv;
447 int reg_b;
448 int reg_a;
449 int epoch;
450 rtc_t *rtc = fun_rtc(fun);
451
452 /* Try to normalize the content of the tm structure */
453 if ((norm_time = mktime(t)) < 0)
454 return EINVAL;
455
456 ntv.tv_sec = norm_time;
457 ntv.tv_usec = t->tm_usec;
458 getuptime(&uptime);
459
460 if (tv_gteq(&uptime, &ntv)) {
461 /* This is not acceptable */
462 return EINVAL;
463 }
464
465 fibril_mutex_lock(&rtc->mutex);
466
467 if (!is_battery_ok(rtc)) {
468 fibril_mutex_unlock(&rtc->mutex);
469 return EIO;
470 }
471
472 /* boot_time must be recomputed */
473 rtc->boot_time.tv_sec = 0;
474 rtc->boot_time.tv_usec = 0;
475
476 /* Detect the RTC epoch */
477 if (rtc_register_read(rtc, RTC_YEAR) < 100)
478 epoch = 2000;
479 else
480 epoch = 1900;
481
482 if (epoch == 2000 && t->tm_year < 100) {
483 /* Can't set a year before the epoch */
484 fibril_mutex_unlock(&rtc->mutex);
485 return EINVAL;
486 }
487
488 t->tm_mon++; /* counts from 1, not from 0 */
489
490 reg_b = rtc_register_read(rtc, RTC_STATUS_B);
491
492 if (!(reg_b & RTC_B_24H)) {
493 /* Force 24h mode of operation */
494 reg_b |= RTC_B_24H;
495 rtc_register_write(rtc, RTC_STATUS_B, reg_b);
496 }
497
498 if (epoch == 2000) {
499 /* The RTC epoch is year 2000 but the tm_year
500 * field counts years since 1900.
501 */
502 t->tm_year -= 100;
503 }
504
505 /* Check if the rtc is working in bcd mode */
506 bcd_mode = !(reg_b & RTC_B_BCD);
507 if (bcd_mode) {
508 /* Convert the tm struct fields in BCD mode */
509 t->tm_sec = bin2bcd(t->tm_sec);
510 t->tm_min = bin2bcd(t->tm_min);
511 t->tm_hour = bin2bcd(t->tm_hour);
512 t->tm_mday = bin2bcd(t->tm_mday);
513 t->tm_mon = bin2bcd(t->tm_mon);
514 t->tm_year = bin2bcd(t->tm_year);
515 }
516
517 /* Inhibit updates */
518 rtc_register_write(rtc, RTC_STATUS_B, reg_b | RTC_B_INH);
519
520 /* Write current time to RTC */
521 rtc_register_write(rtc, RTC_SEC, t->tm_sec);
522 rtc_register_write(rtc, RTC_MIN, t->tm_min);
523 rtc_register_write(rtc, RTC_HOUR, t->tm_hour);
524 rtc_register_write(rtc, RTC_DAY, t->tm_mday);
525 rtc_register_write(rtc, RTC_MON, t->tm_mon);
526 rtc_register_write(rtc, RTC_YEAR, t->tm_year);
527
528 /* Stop the clock */
529 reg_a = rtc_register_read(rtc, RTC_STATUS_A);
530 rtc_register_write(rtc, RTC_STATUS_A, RTC_A_CLK_STOP | reg_a);
531
532 /* Enable updates */
533 rtc_register_write(rtc, RTC_STATUS_B, reg_b);
534 rtc_register_write(rtc, RTC_STATUS_A, reg_a);
535
536 fibril_mutex_unlock(&rtc->mutex);
537
538 return EOK;
539}
540
541/** Get the status of the real time clock battery
542 *
543 * @param fun The RTC function
544 * @param status The status of the battery
545 *
546 * @return EOK on success or an error code
547 */
548static errno_t
549rtc_battery_status_get(ddf_fun_t *fun, battery_status_t *status)
550{
551 rtc_t *rtc = fun_rtc(fun);
552
553 fibril_mutex_lock(&rtc->mutex);
554 const bool batt_ok = is_battery_ok(rtc);
555 fibril_mutex_unlock(&rtc->mutex);
556
557 *status = batt_ok ? BATTERY_OK : BATTERY_LOW;
558
559 return EOK;
560}
561
562/** Check if the battery is working properly or not.
563 * The caller already holds the rtc->mutex lock.
564 *
565 * @param rtc The RTC instance.
566 *
567 * @return true if the battery is ok, false otherwise.
568 */
569static bool
570is_battery_ok(rtc_t *rtc)
571{
572 return rtc_register_read(rtc, RTC_STATUS_D) & RTC_D_BATTERY_OK;
573}
574
575/** The dev_add callback of the rtc driver
576 *
577 * @param dev The RTC device
578 *
579 * @return EOK on success or an error code
580 */
581static errno_t
582rtc_dev_add(ddf_dev_t *dev)
583{
584 rtc_t *rtc;
585 ddf_fun_t *fun = NULL;
586 errno_t rc;
587 bool need_cleanup = false;
588
589 ddf_msg(LVL_DEBUG, "rtc_dev_add %s (handle = %d)",
590 ddf_dev_get_name(dev), (int) ddf_dev_get_handle(dev));
591
592 rtc = ddf_dev_data_alloc(dev, sizeof(rtc_t));
593 if (!rtc)
594 return ENOMEM;
595
596 rtc->dev = dev;
597 fibril_mutex_initialize(&rtc->mutex);
598
599 rc = rtc_dev_initialize(rtc);
600 if (rc != EOK)
601 goto error;
602
603 need_cleanup = true;
604
605 if (!rtc_pio_enable(rtc)) {
606 rc = EADDRNOTAVAIL;
607 goto error;
608 }
609
610 fun = ddf_fun_create(dev, fun_exposed, "a");
611 if (!fun) {
612 ddf_msg(LVL_ERROR, "Failed creating function");
613 rc = ENOENT;
614 goto error;
615 }
616
617 ddf_fun_set_ops(fun, &rtc_dev_ops);
618 rc = ddf_fun_bind(fun);
619 if (rc != EOK) {
620 ddf_msg(LVL_ERROR, "Failed binding function");
621 goto error;
622 }
623
624 rtc->fun = fun;
625
626 ddf_fun_add_to_category(fun, "clock");
627
628 ddf_msg(LVL_NOTE, "Device %s successfully initialized",
629 ddf_dev_get_name(dev));
630
631 return rc;
632
633error:
634 if (fun)
635 ddf_fun_destroy(fun);
636 if (need_cleanup)
637 rtc_dev_cleanup(rtc);
638 return rc;
639}
640
641/** The dev_remove callback for the rtc driver
642 *
643 * @param dev The RTC device
644 *
645 * @return EOK on success or an error code
646 */
647static errno_t
648rtc_dev_remove(ddf_dev_t *dev)
649{
650 rtc_t *rtc = dev_rtc(dev);
651 errno_t rc;
652
653 fibril_mutex_lock(&rtc->mutex);
654 if (rtc->clients_connected > 0) {
655 fibril_mutex_unlock(&rtc->mutex);
656 return EBUSY;
657 }
658
659 rtc->removed = true;
660 fibril_mutex_unlock(&rtc->mutex);
661
662 rc = rtc_fun_offline(rtc->fun);
663 if (rc != EOK) {
664 ddf_msg(LVL_ERROR, "Failed to offline function");
665 return rc;
666 }
667
668 rc = ddf_fun_unbind(rtc->fun);
669 if (rc != EOK) {
670 ddf_msg(LVL_ERROR, "Failed to unbind function");
671 return rc;
672 }
673
674 ddf_fun_destroy(rtc->fun);
675 rtc_dev_cleanup(rtc);
676
677 return rc;
678}
679
680/** Open the device
681 *
682 * @param fun The function node
683 *
684 * @return EOK on success or an error code
685 */
686static errno_t
687rtc_open(ddf_fun_t *fun)
688{
689 errno_t rc;
690 rtc_t *rtc = fun_rtc(fun);
691
692 fibril_mutex_lock(&rtc->mutex);
693
694 if (rtc->removed)
695 rc = ENXIO;
696 else {
697 rc = EOK;
698 rtc->clients_connected++;
699 }
700
701 fibril_mutex_unlock(&rtc->mutex);
702 return rc;
703}
704
705/** Close the device
706 *
707 * @param fun The function node
708 */
709static void
710rtc_close(ddf_fun_t *fun)
711{
712 rtc_t *rtc = fun_rtc(fun);
713
714 fibril_mutex_lock(&rtc->mutex);
715
716 rtc->clients_connected--;
717 assert(rtc->clients_connected >= 0);
718
719 fibril_mutex_unlock(&rtc->mutex);
720}
721
722/** Convert from BCD mode to binary mode
723 *
724 * @param bcd The number in BCD format to convert
725 *
726 * @return The converted value
727 */
728static unsigned
729bcd2bin(unsigned bcd)
730{
731 return ((bcd & 0xF0) >> 1) + ((bcd & 0xF0) >> 3) + (bcd & 0xf);
732}
733
734/** Convert from binary mode to BCD mode
735 *
736 * @param bcd The number in binary mode to convert
737 *
738 * @return The converted value
739 */
740static unsigned
741bin2bcd(unsigned binary)
742{
743 return ((binary / 10) << 4) + (binary % 10);
744}
745
746static errno_t
747rtc_fun_online(ddf_fun_t *fun)
748{
749 errno_t rc;
750
751 ddf_msg(LVL_DEBUG, "rtc_fun_online()");
752
753 rc = ddf_fun_online(fun);
754 if (rc == EOK)
755 ddf_fun_add_to_category(fun, "clock");
756
757 return rc;
758}
759
760static errno_t
761rtc_fun_offline(ddf_fun_t *fun)
762{
763 ddf_msg(LVL_DEBUG, "rtc_fun_offline()");
764 return ddf_fun_offline(fun);
765}
766
767int
768main(int argc, char **argv)
769{
770 printf(NAME ": HelenOS RTC driver\n");
771 rtc_init();
772 return ddf_driver_main(&rtc_driver);
773}
774
775/**
776 * @}
777 */
Note: See TracBrowser for help on using the repository browser.