9ns: Fix devtab function pointer signatures
[akaros.git] / kern / drivers / dev / alarm.c
index 65fab70..7b31548 100644 (file)
@@ -1,23 +1,39 @@
 /* Copyright (c) 2013 The Regents of the University of California
+ * Copyright (c) 2016 Google Inc.
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
  *
- * devalarm/#A: a device for registering per-process alarms.
+ * #alarm: a device for registering per-process alarms.
  *
- * Allows a process to set up alarms, where the kernel will send an event to a
- * posted ev_q at a certain TSC time.
+ * Allows a process to set up alarms, which they can tap to get events at a
+ * certain TSC time.
  *
- * Every process has their own alarm sets and view of #A; gen and friends look
- * at current's alarmset when it is time to gen or open a file.
+ * Every process has their own alarm sets and view of #alarm; gen and friends
+ * look at current's alarmset when it is time to gen or open a file.
  *
- * To use, first open #A/clone, and that gives you an alarm directory aN, where
- * N is ID of the alarm.  ctl takes two commands: "evq POINTER" (to tell the
- * kernel the pointer of your ev_q) and "cancel", to stop an alarm.  timer takes
- * just the value (in absolute tsc time) to fire the alarm.
+ * To use, first open #alarm/clone, and that gives you an alarm directory aN,
+ * where N is ID of the alarm.  The FD you get from clone points to 'ctl.'
  *
- * While each process has a separate view of #A, it is possible to post a chan
- * to Qctl or Qtimer to #s.  If another proc has your Qtimer, it can set it in
- * the past, thereby triggering an immediate event.  More clever than useful.
+ * 'ctl' takes no commands.  You can read it to get the ID.  That's it.
+ *
+ * 'timer' takes the hex string value (in absolute tsc time) to fire the alarm.
+ * Writing 0 disables the alarm.  You can read 'timer' to get the next time it
+ * will fire, in TSC time.  0 means it is disabled.  To find out about the timer
+ * firing, put an FD tap on 'timer' for FDTAP_FILT_WRITTEN.
+ *
+ * 'period' takes the hex string value (in TSC ticks) for the period of the
+ * alarm.  If non-zero, the alarm will rearm when it fires.  You can read the
+ * period.
+ *
+ * Reading the 'count' file will return the number of times the alarm has
+ * expired since the last read or the last write to 'timer'.  If this is 0, then
+ * read() will block or EAGAIN.  You cannot write 'count'.  You can tap it for
+ * FDTAP_FILT_READABLE.
+ *
+ * While each process has a separate view of #alarm, it is possible to post a
+ * chan to Qctl or Qtimer to #srv.  If another proc has your Qtimer, it can set
+ * it in the past, thereby triggering an immediate event.  More clever than
+ * useful.
  *
  * Notes on refcnting (the trickier parts here):
  * - the proc_alarms have counted references to their proc
 #include <kref.h>
 #include <atomic.h>
 #include <alarm.h>
-#include <event.h>
 #include <umem.h>
 #include <devalarm.h>
 
+struct dev alarmdevtab;
+
+static char *devname(void)
+{
+       return alarmdevtab.name;
+}
+
 /* qid path types */
 #define Qtopdir                                        1
 #define Qclone                                 2
 #define Qalarmdir                              3
 #define Qctl                                   4
 #define Qtimer                                 5       /* Qctl + 1 */
+#define Qperiod                                        6
+#define Qcount                                 7
 
 /* This paddr/kaddr is a bit dangerous.  it'll work so long as we don't need all
  * 64 bits for a physical address (48 is the current norm on x86_64). */
@@ -66,7 +90,7 @@
 #define QID2A(q) ((struct proc_alarm*)KADDR(((q).path >> ADDR_SHIFT)))
 #define TYPE(q) ((q).path & ((1 << ADDR_SHIFT) - 1))
 #define QID(ptr, type) ((PADDR(ptr) << ADDR_SHIFT) | type)
-extern char *eve;
+extern struct username eve;
 
 static void alarm_release(struct kref *kref)
 {
@@ -82,19 +106,31 @@ static void alarm_release(struct kref *kref)
        kfree(a);
 }
 
+static void alarm_fire_taps(struct proc_alarm *a, int filter)
+{
+       struct fd_tap *tap_i;
+
+       SLIST_FOREACH(tap_i, &a->fd_taps, link)
+               fire_tap(tap_i, filter);
+}
+
 static void proc_alarm_handler(struct alarm_waiter *a_waiter)
 {
        struct proc_alarm *a = container_of(a_waiter, struct proc_alarm, a_waiter);
-       struct event_queue *ev_q = ACCESS_ONCE(a->ev_q);
-       struct event_msg msg;
-       if (!ev_q || !a->proc) {
-               printk("[kernel] proc_alarm, bad ev_q %p or proc %p\n", ev_q, a->proc);
-               return;
+
+       cv_lock(&a->cv);
+       a->count++;
+       if (!a->period) {
+               a_waiter->wake_up_time = 0;
+       } else {
+               /* TODO: use an alarm helper, once we switch over to nsec */
+               a_waiter->wake_up_time += a->period;
+               set_alarm(a->proc->alarmset.tchain, a_waiter);
        }
-       memset(&msg, 0, sizeof(struct event_msg));
-       msg.ev_type = EV_ALARM;
-       msg.ev_arg2 = a->id;
-       send_event(a->proc, ev_q, &msg, 0);
+       __cv_broadcast(&a->cv);
+       /* Fires taps for both Qtimer and Qcount. */
+       alarm_fire_taps(a, FDTAP_FILT_WRITTEN | FDTAP_FILT_READABLE);
+       cv_unlock(&a->cv);
 }
 
 void devalarm_init(struct proc *p)
@@ -116,7 +152,7 @@ static int alarmgen(struct chan *c, char *entry_name,
        /* Whether we're in one dir or at the top, .. still takes us to the top. */
        if (s == DEVDOTDOT) {
                mkqid(&q, Qtopdir, 0, QTDIR);
-               devdir(c, q, "#A", 0, eve, 0555, dp);
+               devdir(c, q, devname(), 0, eve.name, 0555, dp);
                return 1;
        }
        switch (TYPE(c->qid)) {
@@ -125,12 +161,12 @@ static int alarmgen(struct chan *c, char *entry_name,
                         * alarm dirs at the top level */
                        if (s == 0) {
                                mkqid(&q, Qclone, 0, QTFILE);
-                               devdir(c, q, "clone", 0, eve, 0666, dp);
+                               devdir(c, q, "clone", 0, eve.name, 0666, dp);
                                return 1;
                        }
                        s--;    /* 1 -> 0th element, 2 -> 1st element, etc */
                        /* Gets the s-th element (0 index)
-                        * 
+                        *
                         * I would like to take advantage of the state machine and our
                         * previous answer to get the sth element of the list.  We can get
                         * at our previous run of gen from dp (struct dir), and use that to
@@ -148,8 +184,8 @@ static int alarmgen(struct chan *c, char *entry_name,
                         * parameters to devwalk), so that we can have some invariants
                         * between gen runs.
                         *
-                        * Til then, we're stuck with arrays like in #I (though we can use
-                        * Linux style fdsets) or lousy O(n^2) linked lists (like #s).
+                        * Til then, we're stuck with arrays like in #ip (though we can use
+                        * Linux style fdsets) or lousy O(n^2) linked lists (like #srv).
                         *
                         * Note that we won't always start a gen loop with s == 0
                         * (devdirread, for instance) */
@@ -165,7 +201,7 @@ static int alarmgen(struct chan *c, char *entry_name,
                        }
                        snprintf(get_cur_genbuf(), GENBUF_SZ, "a%d", a_i->id);
                        mkqid(&q, QID(a_i, Qalarmdir), 0, QTDIR);
-                       devdir(c, q, get_cur_genbuf(), 0, eve, 0555, dp);
+                       devdir(c, q, get_cur_genbuf(), 0, eve.name, 0555, dp);
                        spin_unlock(&p->alarmset.lock);
                        return 1;
                case Qalarmdir:
@@ -174,33 +210,47 @@ static int alarmgen(struct chan *c, char *entry_name,
                        switch (s) {
                                case Qctl:
                                        mkqid(&q, QID(QID2A(c->qid), Qctl), 0, QTFILE);
-                                       devdir(c, q, "ctl", 0, eve, 0666, dp);
+                                       devdir(c, q, "ctl", 0, eve.name, 0666, dp);
                                        return 1;
                                case Qtimer:
                                        mkqid(&q, QID(QID2A(c->qid), Qtimer), 0, QTFILE);
-                                       devdir(c, q, "timer", 0, eve, 0666, dp);
+                                       devdir(c, q, "timer", 0, eve.name, 0666, dp);
+                                       return 1;
+                               case Qperiod:
+                                       mkqid(&q, QID(QID2A(c->qid), Qperiod), 0, QTFILE);
+                                       devdir(c, q, "period", 0, eve.name, 0666, dp);
+                                       return 1;
+                               case Qcount:
+                                       mkqid(&q, QID(QID2A(c->qid), Qcount), 0, QTFILE);
+                                       devdir(c, q, "count", 0, eve.name, 0666, dp);
                                        return 1;
                        }
                        return -1;
-                       /* Need to also provide a direct hit for Qclone and all other files (at
-                        * all levels of the hierarchy).  Every file is both
-                        * generated (via the s increments in their respective directories) and
-                        * directly gen-able.  devstat() will call gen with a specific path in
-                        * the qid.  In these cases, we make a dir for whatever they are asking
-                        * for.  Note the qid stays the same.  I think this is what the old
-                        * plan9 comments above devgen were talking about for (ii).
+                       /* Need to also provide a direct hit for Qclone and all other files
+                        * (at all levels of the hierarchy).  Every file is both generated
+                        * (via the s increments in their respective directories) and
+                        * directly gen-able.  devstat() will call gen with a specific path
+                        * in the qid.  In these cases, we make a dir for whatever they are
+                        * asking for.  Note the qid stays the same.  I think this is what
+                        * the old plan9 comments above devgen were talking about for (ii).
                         *
-                        * We don't need to do this for the directories - devstat will look for
-                        * the a directory by path and fail.  Then it will manually build the
-                        * stat output (check the -1 case in devstat). */
+                        * We don't need to do this for the directories - devstat will look
+                        * for the a directory by path and fail.  Then it will manually
+                        * build the stat output (check the -1 case in devstat). */
                case Qclone:
-                       devdir(c, c->qid, "clone", 0, eve, 0666, dp);
+                       devdir(c, c->qid, "clone", 0, eve.name, 0666, dp);
                        return 1;
                case Qctl:
-                       devdir(c, c->qid, "ctl", 0, eve, 0666, dp);
+                       devdir(c, c->qid, "ctl", 0, eve.name, 0666, dp);
                        return 1;
                case Qtimer:
-                       devdir(c, c->qid, "timer", 0, eve, 0666, dp);
+                       devdir(c, c->qid, "timer", 0, eve.name, 0666, dp);
+                       return 1;
+               case Qperiod:
+                       devdir(c, c->qid, "period", 0, eve.name, 0666, dp);
+                       return 1;
+               case Qcount:
+                       devdir(c, c->qid, "count", 0, eve.name, 0666, dp);
                        return 1;
        }
        return -1;
@@ -212,18 +262,18 @@ static void alarminit(void)
 
 static struct chan *alarmattach(char *spec)
 {
-       struct chan *c = devattach('A', spec);
+       struct chan *c = devattach(devname(), spec);
        mkqid(&c->qid, Qtopdir, 0, QTDIR);
        return c;
 }
 
 static struct walkqid *alarmwalk(struct chan *c, struct chan *nc, char **name,
-                                                                int nname)
+                                                                unsigned int nname)
 {
        return devwalk(c, nc, name, nname, 0, 0, alarmgen);
 }
 
-static int alarmstat(struct chan *c, uint8_t * db, int n)
+static size_t alarmstat(struct chan *c, uint8_t *db, size_t n)
 {
        return devstat(c, db, n, 0, 0, alarmgen);
 }
@@ -237,14 +287,17 @@ static struct chan *alarmopen(struct chan *c, int omode)
        switch (TYPE(c->qid)) {
                case Qtopdir:
                case Qalarmdir:
-                       if (omode & ORCLOSE)
-                               error(Eperm);
-                       if (!IS_RDONLY(omode))
-                               error(Eisdir);
+                       if (omode & O_REMCLO)
+                               error(EPERM, ERROR_FIXME);
+                       if (omode & O_WRITE)
+                               error(EISDIR, ERROR_FIXME);
                        break;
                case Qclone:
-                       a = kzmalloc(sizeof(struct proc_alarm), KMALLOC_WAIT);
+                       a = kzmalloc(sizeof(struct proc_alarm), MEM_WAIT);
                        kref_init(&a->kref, alarm_release, 1);
+                       SLIST_INIT(&a->fd_taps);
+                       cv_init(&a->cv);
+                       qlock_init(&a->qlock);
                        init_awaiter(&a->a_waiter, proc_alarm_handler);
                        spin_lock(&p->alarmset.lock);
                        a->id = p->alarmset.id_counter++;
@@ -256,6 +309,8 @@ static struct chan *alarmopen(struct chan *c, int omode)
                        break;
                case Qctl:
                case Qtimer:
+               case Qperiod:
+               case Qcount:
                        /* the purpose of opening is to hold a kref on the proc_alarm */
                        a = QID2A(c->qid);
                        assert(a);
@@ -279,7 +334,7 @@ static struct chan *alarmopen(struct chan *c, int omode)
                        }
                        spin_unlock(&p->alarmset.lock);
                        if (!a_i)
-                               error("Unable to open alarm, concurrent closing");
+                               error(EFAIL, "Unable to open alarm, concurrent closing");
                        break;
        }
        c->mode = openmode(omode);
@@ -289,22 +344,6 @@ static struct chan *alarmopen(struct chan *c, int omode)
        return c;
 }
 
-static void alarmcreate(struct chan *c, char *name, int omode, uint32_t perm)
-{
-       error(Eperm);
-}
-
-static void alarmremove(struct chan *c)
-{
-       error(Eperm);
-}
-
-static int alarmwstat(struct chan *c, uint8_t * dp, int n)
-{
-       error("No alarmwstat");
-       return 0;
-}
-
 static void alarmclose(struct chan *c)
 {
        /* There are more closes than opens.  For instance, sysstat doesn't open,
@@ -315,85 +354,125 @@ static void alarmclose(struct chan *c)
        switch (TYPE(c->qid)) {
                case Qctl:
                case Qtimer:
+               case Qperiod:
+               case Qcount:
                        kref_put(&QID2A(c->qid)->kref);
                        break;
        }
 }
 
-static long alarmread(struct chan *c, void *ubuf, long n, int64_t offset)
+/* Helper for Qcount to encapsulate timerfd. */
+static long read_qcount(struct chan *c, void *ubuf, size_t n)
+{
+       ERRSTACK(1);
+       struct proc_alarm *a = QID2A(c->qid);
+       struct cv_lookup_elm cle;
+       unsigned long old_count;
+
+       if (n > sizeof(old_count))
+               error(EINVAL, "timerfd buffer is too small (%llu)", n);
+       /* TODO: have easily abortable CVs that don't require this mechanism. */
+       cv_lock(&a->cv);
+       __reg_abortable_cv(&cle, &a->cv);
+       if (waserror()) {
+               cv_unlock(&a->cv);
+               dereg_abortable_cv(&cle);
+               nexterror();
+       }
+       while (!a->count) {
+               if (c->flag & O_NONBLOCK)
+                       error(EAGAIN, "#alarm count was 0");
+               if (should_abort(&cle))
+                       error(EINTR, "syscall aborted");
+               cv_wait(&a->cv);
+       }
+       old_count = a->count;
+       a->count = 0;
+       cv_unlock(&a->cv);
+       dereg_abortable_cv(&cle);
+       poperror();
+       if (copy_to_user(ubuf, &old_count, sizeof(old_count)))
+               error(EFAULT, "timerfd copy_to_user failed");
+       return sizeof(old_count);
+}
+
+static size_t alarmread(struct chan *c, void *ubuf, size_t n, off64_t offset)
 {
        struct proc_alarm *p_alarm;
+
        switch (TYPE(c->qid)) {
                case Qtopdir:
                case Qalarmdir:
                        return devdirread(c, ubuf, n, 0, 0, alarmgen);
                case Qctl:
                        p_alarm = QID2A(c->qid);
+                       /* simple reads from p_alarm shouldn't need a lock */
                        return readnum(offset, ubuf, n, p_alarm->id, NUMSIZE32);
                case Qtimer:
                        p_alarm = QID2A(c->qid);
                        return readnum(offset, ubuf, n, p_alarm->a_waiter.wake_up_time,
                                                   NUMSIZE64);
+               case Qperiod:
+                       p_alarm = QID2A(c->qid);
+                       return readnum(offset, ubuf, n, p_alarm->period, NUMSIZE64);
+               case Qcount:
+                       return read_qcount(c, ubuf, n); /* ignore offset */
                default:
                        panic("Bad QID %p in devalarm", c->qid.path);
        }
        return 0;
 }
 
+/* Helper, sets the procalarm to hexval (abs TSC ticks).  0 disarms. */
+static void set_proc_alarm(struct proc_alarm *a, uint64_t hexval)
+{
+       /* Due to how we have to maintain 'count', we need to strictly account for
+        * the firings of the alarm.  Easiest thing is to disarm it, reset
+        * everything, then rearm it.  Note that if someone is blocked on count = 0,
+        * they may still be blocked until the next time the alarm fires.
+        *
+        * unset waits on the handler, which grabs the cv lock, so we don't grab the
+        * cv lock.  However, we still need to protect ourselves from multiple
+        * setters trying to run this at once.  Unset actually can handle being
+        * called concurrently, but alarm setters can't, nor can it handle the
+        * unsets and sets getting out of sync.  For instance, two unsets followed
+        * by two sets would be a bug.  Likewise, setting the awaiter value while it
+        * is on a tchain is a bug.  The qlock prevents that. */
+       qlock(&a->qlock);
+       unset_alarm(a->proc->alarmset.tchain, &a->a_waiter);
+       cv_lock(&a->cv);
+       a->count = 0;
+       if (hexval) {
+               set_awaiter_abs(&a->a_waiter, hexval);
+               set_alarm(a->proc->alarmset.tchain, &a->a_waiter);
+       }
+       cv_unlock(&a->cv);
+       qunlock(&a->qlock);
+}
+
 /* Note that in read and write we have an open chan, which means we have an
  * active kref on the p_alarm.  Also note that we make no assumptions about
  * current here - we find the proc (and the tchain) via the ref stored in the
  * proc_alarm. */
-static long alarmwrite(struct chan *c, void *ubuf, long n, int64_t unused)
+static size_t alarmwrite(struct chan *c, void *ubuf, size_t n, off64_t unused)
 {
-       ERRSTACK(1);
-       char buf[32];
-       struct cmdbuf *cb;
        struct proc_alarm *p_alarm;
-       uint64_t hexval;
 
        switch (TYPE(c->qid)) {
                case Qtopdir:
                case Qalarmdir:
-                       error(Eperm);
                case Qctl:
-                       p_alarm = QID2A(c->qid);
-                       cb = parsecmd(ubuf, n);
-                       if (waserror()) {
-                               kfree(cb);
-                               nexterror();
-                       }
-                       if (!strcmp(cb->f[0], "evq")) {
-                               if (cb->nf < 2)
-                                       error("evq needs a pointer");
-                               /* i think it's safe to do a stroul on a parsecmd.  it's kernel
-                                * memory, and space or 0 terminated */
-                               hexval = strtoul(cb->f[1], 0, 16);
-                               /* This is just to help userspace - event code can handle it */
-                               if (!is_user_rwaddr((void *)hexval, sizeof(struct event_queue)))
-                                       error("Non-user ev_q pointer");
-                               p_alarm->ev_q = (struct event_queue *)hexval;
-                       } else if (!strcmp(cb->f[0], "cancel")) {
-                               unset_alarm(p_alarm->proc->alarmset.tchain, &p_alarm->a_waiter);
-                       } else {
-                               error("%s: not implemented", cb->f[0]);
-                       }
-                       kfree(cb);
-                       poperror();
-                       break;
+               case Qcount:
+                       error(EPERM, ERROR_FIXME);
                case Qtimer:
-                       /* want to give strtoul a null-terminated buf (can't handle random
-                        * user strings) */
-                       if (n >= sizeof(buf))
-                               error(Egreg);
-                       memcpy(buf, ubuf, n);
-                       buf[n] = 0;
-                       hexval = strtoul(buf, 0, 16);
+                       set_proc_alarm(QID2A(c->qid), strtoul_from_ubuf(ubuf, n, 16));
+                       break;
+               case Qperiod:
                        p_alarm = QID2A(c->qid);
-                       /* if you don't know if it was running or not, resetting will turn
-                        * it on regardless. */
-                       reset_alarm_abs(p_alarm->proc->alarmset.tchain, &p_alarm->a_waiter,
-                                                       hexval);
+                       /* racing with the handler which checks the val repeatedly */
+                       cv_lock(&p_alarm->cv);
+                       p_alarm->period = strtoul_from_ubuf(ubuf, n, 16);
+                       cv_unlock(&p_alarm->cv);
                        break;
                default:
                        panic("Bad QID %p in devalarm", c->qid.path);
@@ -401,26 +480,95 @@ static long alarmwrite(struct chan *c, void *ubuf, long n, int64_t unused)
        return n;
 }
 
+/* We use the same tap list, regardless of Qtimer or Qcount */
+static int tap_alarm(struct proc_alarm *a, struct fd_tap *tap, int cmd,
+                     int legal_filter)
+{
+       int ret;
+
+       if (tap->filter & ~legal_filter) {
+               set_error(ENOSYS, "Unsupported #%s tap, must be %p", devname(),
+                                 legal_filter);
+               return -1;
+       }
+       cv_lock(&a->cv);
+       switch (cmd) {
+       case (FDTAP_CMD_ADD):
+               SLIST_INSERT_HEAD(&a->fd_taps, tap, link);
+               ret = 0;
+               break;
+       case (FDTAP_CMD_REM):
+               SLIST_REMOVE(&a->fd_taps, tap, fd_tap, link);
+               ret = 0;
+               break;
+       default:
+               set_error(ENOSYS, "Unsupported #%s tap command %p",
+                                 devname(), cmd);
+               ret = -1;
+       }
+       cv_unlock(&a->cv);
+       return ret;
+}
+
+static int alarm_tapfd(struct chan *c, struct fd_tap *tap, int cmd)
+{
+       struct proc_alarm *a = QID2A(c->qid);
+
+       /* We don't actually support HANGUP, but epoll implies it. */
+       #define ALARM_LEGAL_TIMER_TAPS (FDTAP_FILT_WRITTEN | FDTAP_FILT_HANGUP)
+       #define ALARM_LEGAL_COUNT_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_HANGUP)
+
+       switch (TYPE(c->qid)) {
+       case Qtimer:
+               return tap_alarm(a, tap, cmd, ALARM_LEGAL_TIMER_TAPS);
+       case Qcount:
+               return tap_alarm(a, tap, cmd, ALARM_LEGAL_COUNT_TAPS);
+       default:
+               set_error(ENOSYS, "Can't tap #%s file type %d", devname(),
+                         c->qid.path);
+               return -1;
+       }
+}
+
+static char *alarm_chaninfo(struct chan *ch, char *ret, size_t ret_l)
+{
+       struct proc_alarm *a;
+
+       switch (TYPE(ch->qid)) {
+       case Qctl:
+       case Qtimer:
+       case Qperiod:
+       case Qcount:
+               a = QID2A(ch->qid);
+               snprintf(ret, ret_l, "Id %d, %s, expires %llu, period %llu, count %llu",
+                        a->id, SLIST_EMPTY(&a->fd_taps) ? "untapped" : "tapped",
+                        a->a_waiter.wake_up_time, a->period, a->count);
+               break;
+       default:
+               return devchaninfo(ch, ret, ret_l);
+       }
+       return ret;
+}
+
 struct dev alarmdevtab __devtab = {
-       'A',
-       "alarm",
-
-       devreset,
-       alarminit,
-       devshutdown,
-       alarmattach,
-       alarmwalk,
-       alarmstat,
-       alarmopen,
-       alarmcreate,
-       alarmclose,
-       alarmread,
-       devbread,
-       alarmwrite,
-       devbwrite,
-       alarmremove,
-       alarmwstat,
-       devpower,
-       //devconfig,
-       devchaninfo,
+       .name = "alarm",
+
+       .reset = devreset,
+       .init = alarminit,
+       .shutdown = devshutdown,
+       .attach = alarmattach,
+       .walk = alarmwalk,
+       .stat = alarmstat,
+       .open = alarmopen,
+       .create = devcreate,
+       .close = alarmclose,
+       .read = alarmread,
+       .bread = devbread,
+       .write = alarmwrite,
+       .bwrite = devbwrite,
+       .remove = devremove,
+       .wstat = devwstat,
+       .power = devpower,
+       .chaninfo = alarm_chaninfo,
+       .tapfd = alarm_tapfd,
 };