The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ppbus/pcfclock.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2000 Sascha Schumann. All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   15  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
   16  * EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   18  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
   19  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   20  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
   22  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   23  *
   24  *
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD: releng/11.0/sys/dev/ppbus/pcfclock.c 198358 2009-10-22 06:51:29Z brueffer $");
   29 
   30 #include "opt_pcfclock.h"
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/bus.h>
   35 #include <sys/sockio.h>
   36 #include <sys/mbuf.h>
   37 #include <sys/kernel.h>
   38 #include <sys/module.h>
   39 #include <sys/conf.h>
   40 #include <sys/fcntl.h>
   41 #include <sys/uio.h>
   42 
   43 #include <machine/bus.h>
   44 #include <machine/resource.h>
   45 
   46 #include <dev/ppbus/ppbconf.h>
   47 #include <dev/ppbus/ppb_msq.h>
   48 #include <dev/ppbus/ppbio.h>
   49 
   50 #include "ppbus_if.h"
   51 
   52 #define PCFCLOCK_NAME "pcfclock"
   53 
   54 struct pcfclock_data {
   55         device_t dev;
   56         struct cdev *cdev;
   57 };
   58 
   59 static devclass_t pcfclock_devclass;
   60 
   61 static  d_open_t                pcfclock_open;
   62 static  d_close_t               pcfclock_close;
   63 static  d_read_t                pcfclock_read;
   64 
   65 static struct cdevsw pcfclock_cdevsw = {
   66         .d_version =    D_VERSION,
   67         .d_open =       pcfclock_open,
   68         .d_close =      pcfclock_close,
   69         .d_read =       pcfclock_read,
   70         .d_name =       PCFCLOCK_NAME,
   71 };
   72 
   73 #ifndef PCFCLOCK_MAX_RETRIES
   74 #define PCFCLOCK_MAX_RETRIES 10
   75 #endif
   76 
   77 #define AFC_HI 0
   78 #define AFC_LO AUTOFEED
   79 
   80 /* AUTO FEED is used as clock */
   81 #define AUTOFEED_CLOCK(val) \
   82         ctr = (ctr & ~(AUTOFEED)) ^ (val); ppb_wctr(ppbus, ctr)
   83 
   84 /* SLCT is used as clock */
   85 #define CLOCK_OK \
   86         ((ppb_rstr(ppbus) & SELECT) == (i & 1 ? SELECT : 0))
   87 
   88 /* PE is used as data */
   89 #define BIT_SET (ppb_rstr(ppbus)&PERROR)
   90 
   91 /* the first byte sent as reply must be 00001001b */
   92 #define PCFCLOCK_CORRECT_SYNC(buf) (buf[0] == 9)
   93 
   94 #define NR(buf, off) (buf[off+1]*10+buf[off])
   95 
   96 /* check for correct input values */
   97 #define PCFCLOCK_CORRECT_FORMAT(buf) (\
   98         NR(buf, 14) <= 99 && \
   99         NR(buf, 12) <= 12 && \
  100         NR(buf, 10) <= 31 && \
  101         NR(buf,  6) <= 23 && \
  102         NR(buf,  4) <= 59 && \
  103         NR(buf,  2) <= 59)
  104 
  105 #define PCFCLOCK_BATTERY_STATUS_LOW(buf) (buf[8] & 4)
  106 
  107 #define PCFCLOCK_CMD_TIME 0             /* send current time */
  108 #define PCFCLOCK_CMD_COPY 7     /* copy received signal to PC */
  109 
  110 static void
  111 pcfclock_identify(driver_t *driver, device_t parent)
  112 {
  113 
  114         device_t dev;
  115 
  116         dev = device_find_child(parent, PCFCLOCK_NAME, -1);
  117         if (!dev)
  118                 BUS_ADD_CHILD(parent, 0, PCFCLOCK_NAME, -1);
  119 }
  120 
  121 static int
  122 pcfclock_probe(device_t dev)
  123 {
  124 
  125         device_set_desc(dev, "PCF-1.0");
  126         return (0);
  127 }
  128 
  129 static int
  130 pcfclock_attach(device_t dev)
  131 {
  132         struct pcfclock_data *sc = device_get_softc(dev);
  133         int unit;
  134 
  135         unit = device_get_unit(dev);
  136 
  137         sc->dev = dev;
  138         sc->cdev = make_dev(&pcfclock_cdevsw, unit,
  139                         UID_ROOT, GID_WHEEL, 0400, PCFCLOCK_NAME "%d", unit);
  140         if (sc->cdev == NULL) {
  141                 device_printf(dev, "Failed to create character device\n");
  142                 return (ENXIO);
  143         }
  144         sc->cdev->si_drv1 = sc;
  145 
  146         return (0);
  147 }
  148 
  149 static int
  150 pcfclock_open(struct cdev *dev, int flag, int fms, struct thread *td)
  151 {
  152         struct pcfclock_data *sc = dev->si_drv1;
  153         device_t pcfclockdev;
  154         device_t ppbus;
  155         int res;
  156 
  157         if (!sc)
  158                 return (ENXIO);
  159         pcfclockdev = sc->dev;
  160         ppbus = device_get_parent(pcfclockdev);
  161 
  162         ppb_lock(ppbus);
  163         res = ppb_request_bus(ppbus, pcfclockdev,
  164             (flag & O_NONBLOCK) ? PPB_DONTWAIT : PPB_WAIT);
  165         ppb_unlock(ppbus);
  166         return (res);
  167 }
  168 
  169 static int
  170 pcfclock_close(struct cdev *dev, int flags, int fmt, struct thread *td)
  171 {
  172         struct pcfclock_data *sc = dev->si_drv1;
  173         device_t pcfclockdev = sc->dev;
  174         device_t ppbus = device_get_parent(pcfclockdev);
  175 
  176         ppb_lock(ppbus);
  177         ppb_release_bus(ppbus, pcfclockdev);
  178         ppb_unlock(ppbus);
  179 
  180         return (0);
  181 }
  182 
  183 static void
  184 pcfclock_write_cmd(struct cdev *dev, unsigned char command)
  185 {
  186         struct pcfclock_data *sc = dev->si_drv1;
  187         device_t pcfclockdev = sc->dev;
  188         device_t ppbus = device_get_parent(pcfclockdev);
  189         unsigned char ctr = 14;
  190         char i;
  191 
  192         for (i = 0; i <= 7; i++) {
  193                 ppb_wdtr(ppbus, i);
  194                 AUTOFEED_CLOCK(i & 1 ? AFC_HI : AFC_LO);
  195                 DELAY(3000);
  196         }
  197         ppb_wdtr(ppbus, command);
  198         AUTOFEED_CLOCK(AFC_LO);
  199         DELAY(3000);
  200         AUTOFEED_CLOCK(AFC_HI);
  201 }
  202 
  203 static void
  204 pcfclock_display_data(struct cdev *dev, char buf[18])
  205 {
  206         struct pcfclock_data *sc = dev->si_drv1;
  207 #ifdef PCFCLOCK_VERBOSE
  208         int year;
  209 
  210         year = NR(buf, 14);
  211         if (year < 70)
  212                 year += 100;
  213 
  214         device_printf(sc->dev, "%02d.%02d.%4d %02d:%02d:%02d, "
  215                         "battery status: %s\n",
  216                         NR(buf, 10), NR(buf, 12), 1900 + year,
  217                         NR(buf, 6), NR(buf, 4), NR(buf, 2),
  218                         PCFCLOCK_BATTERY_STATUS_LOW(buf) ? "LOW" : "ok");
  219 #else
  220         if (PCFCLOCK_BATTERY_STATUS_LOW(buf))
  221                 device_printf(sc->dev, "BATTERY STATUS LOW ON\n");
  222 #endif
  223 }
  224 
  225 static int
  226 pcfclock_read_data(struct cdev *dev, char *buf, ssize_t bits)
  227 {
  228         struct pcfclock_data *sc = dev->si_drv1;
  229         device_t pcfclockdev = sc->dev;
  230         device_t ppbus = device_get_parent(pcfclockdev);
  231         int i;
  232         char waitfor;
  233         int offset;
  234 
  235         /* one byte per four bits */
  236         bzero(buf, ((bits + 3) >> 2) + 1);
  237 
  238         waitfor = 100;
  239         for (i = 0; i <= bits; i++) {
  240                 /* wait for clock, maximum (waitfor*100) usec */
  241                 while (!CLOCK_OK && --waitfor > 0)
  242                         DELAY(100);
  243 
  244                 /* timed out? */
  245                 if (!waitfor)
  246                         return (EIO);
  247 
  248                 waitfor = 100; /* reload */
  249 
  250                 /* give it some time */
  251                 DELAY(500);
  252 
  253                 /* calculate offset into buffer */
  254                 offset = i >> 2;
  255                 buf[offset] <<= 1;
  256 
  257                 if (BIT_SET)
  258                         buf[offset] |= 1;
  259         }
  260 
  261         return (0);
  262 }
  263 
  264 static int
  265 pcfclock_read_dev(struct cdev *dev, char *buf, int maxretries)
  266 {
  267         struct pcfclock_data *sc = dev->si_drv1;
  268         device_t pcfclockdev = sc->dev;
  269         device_t ppbus = device_get_parent(pcfclockdev);
  270         int error = 0;
  271 
  272         ppb_set_mode(ppbus, PPB_COMPATIBLE);
  273 
  274         while (--maxretries > 0) {
  275                 pcfclock_write_cmd(dev, PCFCLOCK_CMD_TIME);
  276                 if (pcfclock_read_data(dev, buf, 68))
  277                         continue;
  278 
  279                 if (!PCFCLOCK_CORRECT_SYNC(buf))
  280                         continue;
  281 
  282                 if (!PCFCLOCK_CORRECT_FORMAT(buf))
  283                         continue;
  284 
  285                 break;
  286         }
  287 
  288         if (!maxretries)
  289                 error = EIO;
  290 
  291         return (error);
  292 }
  293 
  294 static int
  295 pcfclock_read(struct cdev *dev, struct uio *uio, int ioflag)
  296 {
  297         struct pcfclock_data *sc = dev->si_drv1;
  298         device_t ppbus;
  299         char buf[18];
  300         int error = 0;
  301 
  302         if (uio->uio_resid < 18)
  303                 return (ERANGE);
  304 
  305         ppbus = device_get_parent(sc->dev);
  306         ppb_lock(ppbus);
  307         error = pcfclock_read_dev(dev, buf, PCFCLOCK_MAX_RETRIES);
  308         ppb_unlock(ppbus);
  309 
  310         if (error) {
  311                 device_printf(sc->dev, "no PCF found\n");
  312         } else {
  313                 pcfclock_display_data(dev, buf);
  314 
  315                 uiomove(buf, 18, uio);
  316         }
  317 
  318         return (error);
  319 }
  320 
  321 static device_method_t pcfclock_methods[] = {
  322         /* device interface */
  323         DEVMETHOD(device_identify,      pcfclock_identify),
  324         DEVMETHOD(device_probe,         pcfclock_probe),
  325         DEVMETHOD(device_attach,        pcfclock_attach),
  326 
  327         { 0, 0 }
  328 };
  329 
  330 static driver_t pcfclock_driver = {
  331         PCFCLOCK_NAME,
  332         pcfclock_methods,
  333         sizeof(struct pcfclock_data),
  334 };
  335 
  336 DRIVER_MODULE(pcfclock, ppbus, pcfclock_driver, pcfclock_devclass, 0, 0);

Cache object: bd42b34a759ecfdafc3e07b9386bbf7c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.