summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortanimura <tanimura@FreeBSD.org>2002-01-04 01:16:18 +0000
committertanimura <tanimura@FreeBSD.org>2002-01-04 01:16:18 +0000
commitc7b71266e628c4538b327973a4e90ee7cf92f905 (patch)
tree503ef16b281b235795c748bba089ae7b5159c559
parentc9a37a6ceaafff4e7bb45214a8cb8bec4cbe1040 (diff)
downloadFreeBSD-src-c7b71266e628c4538b327973a4e90ee7cf92f905.zip
FreeBSD-src-c7b71266e628c4538b327973a4e90ee7cf92f905.tar.gz
Add the system default timer engine.
-rw-r--r--sys/dev/sound/midi/timer.c564
-rw-r--r--sys/dev/sound/midi/timer.h80
2 files changed, 644 insertions, 0 deletions
diff --git a/sys/dev/sound/midi/timer.c b/sys/dev/sound/midi/timer.c
new file mode 100644
index 0000000..969153e
--- /dev/null
+++ b/sys/dev/sound/midi/timer.c
@@ -0,0 +1,564 @@
+/*
+ * This is the timer engine of /dev/music for FreeBSD.
+ *
+ * (C) 2002 Seigo Tanimura
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/midi/sequencer.h>
+
+#define TMR2TICKS(scp, tmr_val) \
+ ((((tmr_val) * (scp)->tempo * (scp)->timebase) + (30 * hz)) / (60 * hz))
+#define CURTICKS(scp) \
+ ((scp)->ticks_offset + (scp)->ticks_cur - (scp)->ticks_base)
+
+struct systmr_timer_softc {
+ int running;
+
+ u_long ticks_offset;
+ u_long ticks_base;
+ u_long ticks_cur;
+
+ int tempo;
+ int timebase;
+
+ u_long nexteventtime;
+ u_long preveventtime;
+
+ struct callout timer;
+};
+
+static timeout_t systmr_timer;
+static void systmr_reset(timerdev_info *tmd);
+static u_long systmr_time(void);
+
+static tmr_open_t systmr_open;
+static tmr_close_t systmr_close;
+static tmr_event_t systmr_event;
+static tmr_gettime_t systmr_gettime;
+static tmr_ioctl_t systmr_ioctl;
+static tmr_armtimer_t systmr_armtimer;
+
+static timerdev_info systmr_timerdev = {
+ "System clock",
+ 0,
+ 0,
+ systmr_open,
+ systmr_close,
+ systmr_event,
+ systmr_gettime,
+ systmr_ioctl,
+ systmr_armtimer,
+};
+
+static TAILQ_HEAD(,_timerdev_info) timer_info;
+static struct mtx timerinfo_mtx;
+static int timerinfo_mtx_init;
+static int ntimer;
+
+
+/* Install a system timer. */
+int
+timerdev_install(void)
+{
+ int ret;
+ timerdev_info *tmd;
+ struct systmr_timer_softc *scp;
+
+ SEQ_DEBUG(printf("timerdev_install: install a new timer.\n"));
+
+ ret = 0;
+ tmd = NULL;
+ scp = NULL;
+
+ scp = malloc(sizeof(*scp), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (scp == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tmd = create_timerdev_info_unit(&systmr_timerdev);
+ if (tmd == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ tmd->softc = scp;
+ callout_init(&scp->timer, 0);
+
+ mtx_unlock(&tmd->mtx);
+
+ SEQ_DEBUG(printf("timerdev_install: installed a new timer, unit %d.\n", tmd->unit));
+
+ return (0);
+
+fail:
+ if (scp != NULL)
+ free(scp, M_DEVBUF);
+ if (tmd != NULL) {
+ TAILQ_REMOVE(&timer_info, tmd, tmd_link);
+ free(tmd, M_DEVBUF);
+ }
+
+ SEQ_DEBUG(printf("timerdev_install: installation failed.\n"));
+
+ return (ret);
+}
+
+/* Create a new timer device info structure. */
+timerdev_info *
+create_timerdev_info_unit(timerdev_info *tmdinf)
+{
+ int unit;
+ timerdev_info *tmd, *tmdnew;
+
+ /* XXX */
+ if (!timerinfo_mtx_init) {
+ timerinfo_mtx_init = 1;
+ mtx_init(&timerinfo_mtx, "tmrinf", MTX_DEF);
+ TAILQ_INIT(&timer_info);
+ }
+
+ /* As malloc(9) might block, allocate timerdev_info now. */
+ tmdnew = malloc(sizeof(timerdev_info), M_DEVBUF, M_WAITOK | M_ZERO);
+ if (tmdnew == NULL)
+ return NULL;
+ bcopy(tmdinf, tmdnew, sizeof(timerdev_info));
+ mtx_init(&tmdnew->mtx, "tmrmtx", MTX_DEF);
+
+ mtx_lock(&timerinfo_mtx);
+
+ ntimer++;
+
+ for (unit = 0 ; ; unit++) {
+ TAILQ_FOREACH(tmd, &timer_info, tmd_link) {
+ if (tmd->unit == unit)
+ break;
+ }
+ if (tmd == NULL)
+ break;
+ }
+
+ tmdnew->unit = unit;
+ mtx_lock(&tmdnew->mtx);
+ tmd = TAILQ_FIRST(&timer_info);
+ while (tmd != NULL) {
+ if (tmd->prio < tmdnew->prio)
+ break;
+ tmd = TAILQ_NEXT(tmd, tmd_link);
+ }
+ if (tmd != NULL)
+ TAILQ_INSERT_BEFORE(tmd, tmdnew, tmd_link);
+ else
+ TAILQ_INSERT_TAIL(&timer_info, tmdnew, tmd_link);
+
+ mtx_unlock(&timerinfo_mtx);
+
+ return (tmdnew);
+}
+
+/*
+ * a small utility function which, given a unit number, returns
+ * a pointer to the associated timerdev_info struct.
+ */
+timerdev_info *
+get_timerdev_info_unit(int unit)
+{
+ timerdev_info *tmd;
+
+ /* XXX */
+ if (!timerinfo_mtx_init) {
+ timerinfo_mtx_init = 1;
+ mtx_init(&timerinfo_mtx, "tmrinf", MTX_DEF);
+ TAILQ_INIT(&timer_info);
+ }
+
+ mtx_lock(&timerinfo_mtx);
+ TAILQ_FOREACH(tmd, &timer_info, tmd_link) {
+ mtx_lock(&tmd->mtx);
+ if (tmd->unit == unit && tmd->seq == NULL)
+ break;
+ mtx_unlock(&tmd->mtx);
+ }
+ mtx_unlock(&timerinfo_mtx);
+
+ return tmd;
+}
+
+/*
+ * a small utility function which returns a pointer
+ * to the best preferred timerdev_info struct with
+ * no sequencer.
+ */
+timerdev_info *
+get_timerdev_info(void)
+{
+ timerdev_info *tmd;
+
+ /* XXX */
+ if (!timerinfo_mtx_init) {
+ timerinfo_mtx_init = 1;
+ mtx_init(&timerinfo_mtx, "tmrinf", MTX_DEF);
+ TAILQ_INIT(&timer_info);
+ }
+
+ mtx_lock(&timerinfo_mtx);
+ TAILQ_FOREACH(tmd, &timer_info, tmd_link) {
+ mtx_lock(&tmd->mtx);
+ if (tmd->seq == NULL)
+ break;
+ mtx_unlock(&tmd->mtx);
+ }
+ mtx_unlock(&timerinfo_mtx);
+
+ return tmd;
+}
+
+
+/* ARGSUSED */
+static void
+systmr_timer(void *d)
+{
+ timerdev_info *tmd;
+ struct systmr_timer_softc *scp;
+ void *seq;
+
+ tmd = (timerdev_info *)d;
+ scp = (struct systmr_timer_softc *)tmd->softc;
+ seq = NULL;
+
+ mtx_lock(&tmd->mtx);
+
+ if (tmd->opened) {
+ callout_reset(&scp->timer, 1, systmr_timer, tmd);
+
+ if (scp->running) {
+ scp->ticks_cur = TMR2TICKS(scp, systmr_time());
+
+ if (CURTICKS(scp) >= scp->nexteventtime) {
+ SEQ_DEBUG(printf("systmr_timer: CURTICKS %lu, call the sequencer.\n", CURTICKS(scp)));
+ scp->nexteventtime = ULONG_MAX;
+ seq = tmd->seq;
+ }
+ }
+ }
+
+ mtx_unlock(&tmd->mtx);
+
+ if (seq != NULL)
+ seq_timer(seq);
+}
+
+static void
+systmr_reset(timerdev_info *tmd)
+{
+ struct systmr_timer_softc *scp;
+
+ scp = (struct systmr_timer_softc *)tmd->softc;
+
+ mtx_assert(&tmd->mtx, MA_OWNED);
+
+ SEQ_DEBUG(printf("systmr_reset: unit %d.\n", tmd->unit));
+
+ scp->ticks_offset = 0;
+ scp->ticks_base = scp->ticks_cur = TMR2TICKS(scp, systmr_time());
+
+ scp->nexteventtime = ULONG_MAX;
+ scp->preveventtime = 0;
+}
+
+static u_long
+systmr_time(void)
+{
+ struct timeval timecopy;
+
+ getmicrotime(&timecopy);
+ return timecopy.tv_usec / (1000000 / hz) + (u_long) timecopy.tv_sec * hz;
+}
+
+
+/* ARGSUSED */
+static int
+systmr_open(timerdev_info *tmd, int oflags, int devtype, struct thread *td)
+{
+ struct systmr_timer_softc *scp;
+
+ scp = (struct systmr_timer_softc *)tmd->softc;
+
+ SEQ_DEBUG(printf("systmr_open: unit %d.\n", tmd->unit));
+
+ mtx_lock(&tmd->mtx);
+
+ if (tmd->opened) {
+ mtx_unlock(&tmd->mtx);
+ return (EBUSY);
+ }
+
+ systmr_reset(tmd);
+ scp->tempo = 60;
+ scp->timebase = hz;
+ tmd->opened = 1;
+
+ callout_reset(&scp->timer, 1, systmr_timer, tmd);
+
+ mtx_unlock(&tmd->mtx);
+
+ return (0);
+}
+
+static int
+systmr_close(timerdev_info *tmd, int fflag, int devtype, struct thread *td)
+{
+ struct systmr_timer_softc *scp;
+
+ scp = (struct systmr_timer_softc *)tmd->softc;
+
+ SEQ_DEBUG(printf("systmr_close: unit %d.\n", tmd->unit));
+
+ mtx_lock(&tmd->mtx);
+
+ tmd->opened = 0;
+ scp->running = 0;
+
+ callout_stop(&scp->timer);
+
+ mtx_unlock(&tmd->mtx);
+
+ return (0);
+}
+
+static int
+systmr_event(timerdev_info *tmd, u_char *ev)
+{
+ struct systmr_timer_softc *scp;
+ u_char cmd;
+ u_long parm, t;
+ int ret;
+ void * seq;
+
+ scp = (struct systmr_timer_softc *)tmd->softc;
+ cmd = ev[1];
+ parm = *(int *)&ev[4];
+ ret = MORE;
+
+ SEQ_DEBUG(printf("systmr_event: unit %d, cmd %s, parm %lu.\n",
+ tmd->unit, midi_cmdname(cmd, cmdtab_timer), parm));
+
+ mtx_lock(&tmd->mtx);
+
+ switch (cmd) {
+ case TMR_WAIT_REL:
+ parm += scp->preveventtime;
+ /* FALLTHRU */
+ case TMR_WAIT_ABS:
+ if (parm > 0) {
+ if (parm <= CURTICKS(scp))
+ break;
+ t = parm;
+ scp->nexteventtime = scp->preveventtime = t;
+ ret = TIMERARMED;
+ break;
+ }
+ break;
+
+ case TMR_START:
+ systmr_reset(tmd);
+ scp->running = 1;
+ break;
+
+ case TMR_STOP:
+ scp->running = 0;
+ break;
+
+ case TMR_CONTINUE:
+ scp->running = 1;
+ break;
+
+ case TMR_TEMPO:
+ if (parm > 0) {
+ RANGE(parm, 8, 360);
+ scp->ticks_offset += scp->ticks_cur
+ - scp->ticks_base;
+ scp->ticks_base = scp->ticks_cur;
+ scp->tempo = parm;
+ }
+ break;
+
+ case TMR_ECHO:
+ seq = tmd->seq;
+ mtx_unlock(&tmd->mtx);
+ seq_copytoinput(seq, ev, 8);
+ mtx_lock(&tmd->mtx);
+ break;
+ }
+
+ mtx_unlock(&tmd->mtx);
+
+ SEQ_DEBUG(printf("systmr_event: timer %s.\n",
+ ret == TIMERARMED ? "armed" : "not armed"));
+
+ return (ret);
+}
+
+static int
+systmr_gettime(timerdev_info *tmd, u_long *t)
+{
+ struct systmr_timer_softc *scp;
+ int ret;
+
+ scp = (struct systmr_timer_softc *)tmd->softc;
+
+ SEQ_DEBUG(printf("systmr_gettime: unit %d.\n", tmd->unit));
+
+ mtx_lock(&tmd->mtx);
+
+ if (!tmd->opened || t == NULL) {
+ ret = EINVAL;
+ goto fail;
+ }
+
+ *t = CURTICKS(scp);
+ SEQ_DEBUG(printf("systmr_gettime: ticks %lu.\n", *t));
+
+fail:
+ mtx_unlock(&tmd->mtx);
+
+ return (0);
+}
+
+static int
+systmr_ioctl(timerdev_info *tmd, u_long cmd, caddr_t data, int fflag, struct thread *td)
+{
+ struct systmr_timer_softc *scp;
+ int ret, val;
+
+ scp = (struct systmr_timer_softc *)tmd->softc;
+ ret = 0;
+
+ SEQ_DEBUG(printf("systmr_ioctl: unit %d, cmd %s.\n",
+ tmd->unit, midi_cmdname(cmd, cmdtab_seqioctl)));
+
+ switch (cmd) {
+ case SNDCTL_TMR_SOURCE:
+ *(int *)data = TMR_INTERNAL;
+ break;
+
+ case SNDCTL_TMR_START:
+ mtx_lock(&tmd->mtx);
+ systmr_reset(tmd);
+ scp->running = 1;
+ mtx_unlock(&tmd->mtx);
+ break;
+
+ case SNDCTL_TMR_STOP:
+ mtx_lock(&tmd->mtx);
+ scp->running = 0;
+ mtx_unlock(&tmd->mtx);
+ break;
+
+ case SNDCTL_TMR_CONTINUE:
+ mtx_lock(&tmd->mtx);
+ scp->running = 1;
+ mtx_unlock(&tmd->mtx);
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ val = *(int *)data;
+ mtx_lock(&tmd->mtx);
+ if (val > 0) {
+ RANGE(val, 1, 1000);
+ scp->timebase = val;
+ }
+ *(int *)data = scp->timebase;
+ mtx_unlock(&tmd->mtx);
+ SEQ_DEBUG(printf("systmr_ioctl: timebase %d.\n", *(int *)data));
+ break;
+
+ case SNDCTL_TMR_TEMPO:
+ val = *(int *)data;
+ mtx_lock(&tmd->mtx);
+ if (val > 0) {
+ RANGE(val, 8, 360);
+ scp->ticks_offset += scp->ticks_cur
+ - scp->ticks_base;
+ scp->ticks_base = scp->ticks_cur;
+ scp->tempo = val;
+ }
+ *(int *)data = scp->tempo;
+ SEQ_DEBUG(printf("systmr_ioctl: tempo %d.\n", *(int *)data));
+ mtx_unlock(&tmd->mtx);
+ break;
+
+ case SNDCTL_SEQ_CTRLRATE:
+ val = *(int *)data;
+ if (val > 0)
+ ret = EINVAL;
+ else {
+ mtx_lock(&tmd->mtx);
+ *(int *)data = ((scp->tempo * scp->timebase) + 30) / 60;
+ mtx_unlock(&tmd->mtx);
+ SEQ_DEBUG(printf("systmr_ioctl: ctrlrate %d.\n", *(int *)data));
+ }
+ break;
+
+ case SNDCTL_TMR_METRONOME:
+ /* NOP. */
+ break;
+
+ case SNDCTL_TMR_SELECT:
+ /* NOP. */
+ break;
+
+ default:
+ ret = EINVAL;
+ }
+
+ return (ret);
+}
+
+static int
+systmr_armtimer(timerdev_info *tmd, u_long t)
+{
+ struct systmr_timer_softc *scp;
+
+ scp = (struct systmr_timer_softc *)tmd->softc;
+
+ SEQ_DEBUG(printf("systmr_armtimer: unit %d, t %lu.\n", tmd->unit, t));
+
+ mtx_lock(&tmd->mtx);
+
+ if (t < 0)
+ t = CURTICKS(scp) + 1;
+ else if (t > CURTICKS(scp))
+ scp->nexteventtime = scp->preveventtime = t;
+
+ mtx_unlock(&tmd->mtx);
+
+ return (0);
+}
diff --git a/sys/dev/sound/midi/timer.h b/sys/dev/sound/midi/timer.h
new file mode 100644
index 0000000..a615ab6
--- /dev/null
+++ b/sys/dev/sound/midi/timer.h
@@ -0,0 +1,80 @@
+/*
+ * Include file for a midi timer.
+ *
+ * Copyright by Seigo Tanimura 2002.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+typedef struct _timerdev_info timerdev_info;
+
+typedef int (tmr_open_t)(timerdev_info *tmd, int oflags, int devtype, struct thread *td);
+typedef int (tmr_close_t)(timerdev_info *tmd, int fflag, int devtype, struct thread *td);
+typedef int (tmr_event_t)(timerdev_info *tmd, u_char *ev);
+typedef int (tmr_gettime_t)(timerdev_info *tmd, u_long *t);
+typedef int (tmr_ioctl_t)(timerdev_info *tmd, u_long cmd, caddr_t data, int fflag, struct thread *td);
+typedef int (tmr_armtimer_t)(timerdev_info *tmd, u_long t);
+
+struct _timerdev_info {
+ /*
+ * the first part of the descriptor is filled up from a
+ * template.
+ */
+ char name[32];
+
+ int caps;
+ int prio;
+
+ tmr_open_t *open;
+ tmr_close_t *close;
+ tmr_event_t *event;
+ tmr_gettime_t *gettime;
+ tmr_ioctl_t *ioctl;
+ tmr_armtimer_t *armtimer;
+
+
+ int unit;
+ void *softc;
+ void *seq;
+
+ /* The tailq entry of the next timer device. */
+ TAILQ_ENTRY(_timerdev_info) tmd_link;
+
+ int opened;
+
+ struct mtx mtx;
+};
+
+#ifdef _KERNEL
+int timerdev_install(void);
+timerdev_info *create_timerdev_info_unit(timerdev_info *tmdinf);
+timerdev_info *get_timerdev_info_unit(int unit);
+timerdev_info *get_timerdev_info(void);
+#endif /* _KERNEL */
+
+#endif /* _TIMER_H_ */
OpenPOWER on IntegriCloud