/* * Copyright by Vladimir N. Silyaev 2000 * * 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$ * $vmFreeBSD: vmware/vmnet-only/freebsd/vmnet.c,v 1.14 2000/01/23 22:29:50 vsilyaev Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtc.h" #ifdef MODULE_DEPEND MODULE_DEPEND(rtc, linux, 1, 1, 1); #endif #define CDEV_MAJOR 202 #if defined(CDEV_MAJOR_) && CDEV_MAJOR != CDEV_MAJOR_ #error "CDEV_MAJOR != CDEV_MAJOR_" #endif #define DEVICE_NAME "rtc" enum rtc_log_level {Lenter=0, Lexit=1, Linfo, Lwarning, Lfail}; #define DEBUG 0 #if DEBUG #define DEB(x) x #define DLog(level, fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ##args) #else /* !DEBUG */ #define DEB(x) #define DLog(level, fmt, args...) #endif /* DEBUG */ struct rtc_softc { #if __FreeBSD_version >= 502117 struct cdev *dev; /* Back reference to device */ #else dev_t dev; /* Back reference to device */ #endif struct { int freq; struct { int opened:1; int enabled:1; } flags; struct callout rtc_handle; struct timespec lasttime; struct selinfo sip; unsigned long woken; void *rtc_ident; } var; }; static d_open_t rtc_open; static d_close_t rtc_close; static d_ioctl_t rtc_ioctl; static d_poll_t rtc_poll; static d_read_t rtc_read; static void rtc_callback(void *xtp); static int rtc_modeevent(module_t mod, int cmd, void *arg); static struct cdevsw rtc_cdevsw = { #if __FreeBSD_version >= 500104 #if __FreeBSD_version >= 502103 .d_version = D_VERSION, .d_flags = D_NEEDGIANT, #else .d_maj = CDEV_MAJOR, #endif .d_open = rtc_open, .d_close = rtc_close, .d_ioctl = rtc_ioctl, .d_poll = rtc_poll, .d_read = rtc_read, .d_name = DEVICE_NAME, #else /* open */ rtc_open, /* close */ rtc_close, /* read */ rtc_read, /* write */ nowrite, /* ioctl */ rtc_ioctl, /* poll */ rtc_poll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ DEVICE_NAME, /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, #if __FreeBSD_version <= 500018 /* bmaj */ -1, #endif #if __FreeBSD_version >= 500018 || __FreeBSD_version >= 430000 /* kqfilter */ nokqfilter, #endif #endif }; /* * Now declare the module to the system. * IMPORTANT: Must be before netgraph node declaration. */ DEV_MODULE(rtc, rtc_modeevent, 0); /* -=-=-=-=-=-=-=-=-= attach/detach device stuff -=-=-=-=-=-=-=-=-= */ static struct rtc_softc * #if __FreeBSD_version >= 502017 rtc_attach(struct cdev *dev) #else rtc_attach(dev_t dev) #endif { struct rtc_softc *sc; int unit; #if __FreeBSD_version >= 500014 unit = dev2unit(dev); #else unit = lminor(dev); #endif DLog(Lenter, "%d %p", unit, dev); if (dev->si_drv1) { DLog(Lexit, "old %p, %p", dev, dev->si_drv1); return dev->si_drv1; } MALLOC(sc, struct rtc_softc*, sizeof(*sc), M_DEVBUF, M_WAITOK); if (sc==NULL) return NULL; bzero(sc, sizeof(*sc)); dev->si_drv1 = sc; /* Link together */ sc->dev = dev; sc->var.freq = 1; #if __FreeBSD_version >= 500000 callout_init(&sc->var.rtc_handle, 1); #else callout_init(&sc->var.rtc_handle); #endif DLog(Lexit, "new %p,%p", dev, sc); return sc; } static int #if __FreeBSD_version >= 502017 rtc_detach(struct cdev *dev, struct rtc_softc *sc) #else rtc_detach(dev_t dev, struct rtc_softc *sc) #endif { int error=0; if (sc == NULL) { return error; } callout_stop(&sc->var.rtc_handle); FREE(sc, M_DEVBUF); dev->si_drv1 = NULL; return error; } /* -=-=-=-=-=-=-=-=-= character device stuff -=-=-=-=-=-=-=-=-= */ static int #if __FreeBSD_version >= 502017 rtc_open(struct cdev *dev, int oflag, int otyp, struct thread *p) #elif __FreeBSD_version >= 500023 rtc_open(dev_t dev, int oflag, int otyp, struct thread *p) #else rtc_open(dev_t dev, int oflag, int otyp, struct proc *p) #endif { struct rtc_softc *sc; sc = rtc_attach(dev); if (sc==NULL) return (EAGAIN); if (sc->var.flags.opened) return (EBUSY); sc->var.flags.opened = 1; return 0; } int #if __FreeBSD_version >= 502017 rtc_close(struct cdev *dev, int fflag, int otyp, struct thread *p) #elif __FreeBSD_version >= 500023 rtc_close(dev_t dev, int fflag, int otyp, struct thread *p) #else rtc_close(dev_t dev, int fflag, int otyp, struct proc *p) #endif { struct rtc_softc *sc = (struct rtc_softc *) dev->si_drv1; rtc_detach(dev, sc); sc->var.flags.opened = 0; return 0; } int #if __FreeBSD_version >= 502017 rtc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *p) #elif __FreeBSD_version >= 500023 rtc_ioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct thread *p) #else rtc_ioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc *p) #endif { struct rtc_softc *sc = (struct rtc_softc *) dev->si_drv1; int error=0, freq, sleep; DLog(Lenter, "cmd 0x%04lx", cmd); switch (cmd) { case RTCIO_IRQP_SET: freq = *(int *)arg; if (freq < 2) { error = EINVAL; break; } if (freq > 1024) { error = EINVAL; break; } sc->var.freq = freq; if ((sc->var.freq > hz) && (hz < 1000)) { printf("rtc: %d > kern.hz: Timing will be inaccurate, please increase kern.hz.\n", sc->var.freq); sc->var.freq = hz; } sleep = hz / sc->var.freq; DLog(Linfo, "Set RTC freq %d", sc->var.freq); callout_stop(&sc->var.rtc_handle); nanouptime(&sc->var.lasttime); callout_reset(&sc->var.rtc_handle, sleep, &rtc_callback, (void *)sc); break; case RTCIO_PIE_ON: sc->var.flags.enabled = 1; DLog(Linfo, "Enable interrupts"); break; default: DLog(Lfail, "Unknown IOCTL 0x%04lx", cmd); error = EINVAL; break; } DLog(Lenter, "result %d", error); return (error); } int #if __FreeBSD_version >= 502017 rtc_poll(struct cdev *dev, int events, struct thread *p) #elif __FreeBSD_version >= 500023 rtc_poll(dev_t dev, int events, struct thread *p) #else rtc_poll(dev_t dev, int events, struct proc *p) #endif { struct rtc_softc *sc = (struct rtc_softc *) dev->si_drv1; int revents = 0; if (!sc->var.flags.enabled) return 0; if (events) { DLog(Linfo, "Delay for %d usec", delay); if (sc->var.woken) { revents = events; } else { selrecord(p, &sc->var.sip); } } return revents; } int #if __FreeBSD_version >= 502017 rtc_read(struct cdev *dev, struct uio *uio, int flags __unused) #else rtc_read(dev_t dev, struct uio *uio, int flags __unused) #endif { struct rtc_softc *sc = (struct rtc_softc *) dev->si_drv1; int error = 0; if (!sc->var.flags.enabled) return 0; if (flags & IO_NDELAY) return EAGAIN; DLog(Linfo, "Delay for %d usec", delay); if (sc->var.woken == 0) tsleep(&sc->var.rtc_ident, PCATCH, "rtc rd", hz * 10); #if 0 if (sc->var.woken > 1) printf("woken: %lu\n", sc->var.woken); #endif if (uio->uio_resid == sizeof(unsigned int)) { error = uiomove(&sc->var.woken, sizeof(unsigned int), uio); } else if (uio->uio_resid == sizeof(unsigned long)) { error = uiomove(&sc->var.woken, sizeof(unsigned long), uio); } sc->var.woken = 0; return error; } /* -=-=-=-=-=-=-=-=-= module load/unload stuff -=-=-=-=-=-=-=-=-= */ #if __FreeBSD_version >= 502017 static struct cdev *rtc_dev = NULL; #else static dev_t rtc_dev = NULL; #endif static int init_module(void) { int error = 0; #if __FreeBSD_version < 500104 error = cdevsw_add(&rtc_cdevsw); if (error) return error; #endif rtc_dev = make_dev(&rtc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0644, DEVICE_NAME); if (rtc_dev==NULL) error = ENOMEM; return error; } static int cleanup_module(void) { int error = 0; destroy_dev(rtc_dev); #if __FreeBSD_version < 500104 error = cdevsw_remove(&rtc_cdevsw); #endif DLog(Linfo, "return %d", error); return error; } static int rtc_modeevent(module_t mod, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD: error = init_module(); break; case MOD_UNLOAD: /* fall through */ case MOD_SHUTDOWN: error = cleanup_module(); break; default: /* we only understand load/unload */ error = EINVAL; break; } return (error); } void rtc_callback(void *xtp) { int sleep; struct rtc_softc *sc = (struct rtc_softc *)xtp; struct timespec curtime, nexttime, increment; if (callout_pending(&sc->var.rtc_handle) || !callout_active(&sc->var.rtc_handle)) return; /* Wakeup sleepers */ sc->var.woken++; selwakeup(&sc->var.sip); wakeup(&sc->var.rtc_ident); /* Setup for our next nap. */ nanouptime(&curtime); restart: increment.tv_sec = 0; increment.tv_nsec = 1000000000 / sc->var.freq; timespecadd(&sc->var.lasttime, &increment); nexttime.tv_sec = sc->var.lasttime.tv_sec; nexttime.tv_nsec = sc->var.lasttime.tv_nsec; timespecadd(&nexttime, &increment); if (timespeccmp(&nexttime, &curtime, <)) { /* Catch up if we lag curtime */ sc->var.lasttime.tv_sec = curtime.tv_sec; sc->var.lasttime.tv_nsec = curtime.tv_nsec; timespecsub(&sc->var.lasttime, &increment); timespecsub(&nexttime, &curtime); #if 0 printf("lagging curtime by %d.%ld\n", nexttime.tv_sec, nexttime.tv_nsec); #endif goto restart; } else { timespecsub(&nexttime, &curtime); sleep = nexttime.tv_nsec / (1000000000 / hz); } callout_reset(&sc->var.rtc_handle, sleep, &rtc_callback, xtp); }