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/misc/pcfclock/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 REGENTS 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  * $FreeBSD: src/sys/dev/ppbus/pcfclock.c,v 1.3.2.1 2000/05/24 00:20:57 n_hibma Exp $
   25  *
   26  */
   27 
   28 #include "opt_pcfclock.h"
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/bus.h>
   33 #include <sys/conf.h>
   34 #include <sys/device.h>
   35 #include <sys/sockio.h>
   36 #include <sys/mbuf.h>
   37 #include <sys/kernel.h>
   38 #include <sys/fcntl.h>
   39 #include <sys/uio.h>
   40 
   41 #include <machine/clock.h>      /* for DELAY */
   42 
   43 #include <bus/ppbus/ppbconf.h>
   44 #include <bus/ppbus/ppb_msq.h>
   45 #include <bus/ppbus/ppbio.h>
   46 
   47 #include "ppbus_if.h"
   48 
   49 #define PCFCLOCK_NAME "pcfclock"
   50 
   51 struct pcfclock_data {
   52         int     count;
   53         struct  ppb_device pcfclock_dev;
   54 };
   55 
   56 #define DEVTOSOFTC(dev) \
   57         ((struct pcfclock_data *)device_get_softc(dev))
   58 #define UNITOSOFTC(unit) \
   59         ((struct pcfclock_data *)devclass_get_softc(pcfclock_devclass, (unit)))
   60 #define UNITODEVICE(unit) \
   61         (devclass_get_device(pcfclock_devclass, (unit)))
   62 
   63 static devclass_t pcfclock_devclass;
   64 
   65 static  d_open_t                pcfclock_open;
   66 static  d_close_t               pcfclock_close;
   67 static  d_read_t                pcfclock_read;
   68 
   69 static struct dev_ops pcfclock_ops = {
   70         { PCFCLOCK_NAME, 0, 0 },
   71         .d_open =       pcfclock_open,
   72         .d_close =      pcfclock_close,
   73         .d_read =       pcfclock_read,
   74 };
   75 
   76 #ifndef PCFCLOCK_MAX_RETRIES
   77 #define PCFCLOCK_MAX_RETRIES 10
   78 #endif
   79 
   80 #define AFC_HI 0
   81 #define AFC_LO AUTOFEED
   82 
   83 /* AUTO FEED is used as clock */
   84 #define AUTOFEED_CLOCK(val) \
   85         ctr = (ctr & ~(AUTOFEED)) ^ (val); ppb_wctr(ppbus, ctr)
   86 
   87 /* SLCT is used as clock */
   88 #define CLOCK_OK \
   89         ((ppb_rstr(ppbus) & SELECT) == (i & 1 ? SELECT : 0))
   90 
   91 /* PE is used as data */
   92 #define BIT_SET (ppb_rstr(ppbus)&PERROR)
   93 
   94 /* the first byte sent as reply must be 00001001b */
   95 #define PCFCLOCK_CORRECT_SYNC(buf) (buf[0] == 9)
   96 
   97 #define NR(buf, off) (buf[off+1]*10+buf[off])
   98 
   99 /* check for correct input values */
  100 #define PCFCLOCK_CORRECT_FORMAT(buf) (\
  101         NR(buf, 14) <= 99 && \
  102         NR(buf, 12) <= 12 && \
  103         NR(buf, 10) <= 31 && \
  104         NR(buf,  6) <= 23 && \
  105         NR(buf,  4) <= 59 && \
  106         NR(buf,  2) <= 59)
  107 
  108 #define PCFCLOCK_BATTERY_STATUS_LOW(buf) (buf[8] & 4)
  109          
  110 #define PCFCLOCK_CMD_TIME 0             /* send current time */
  111 #define PCFCLOCK_CMD_COPY 7     /* copy received signal to PC */
  112 
  113 static int
  114 pcfclock_probe(device_t dev)
  115 {
  116         struct pcfclock_data *sc;
  117 
  118         device_set_desc(dev, "PCF-1.0");
  119 
  120         sc = DEVTOSOFTC(dev);
  121         bzero(sc, sizeof(struct pcfclock_data));
  122         
  123         return (0);
  124 }
  125 
  126 static int
  127 pcfclock_attach(device_t dev)
  128 {
  129         int unit;
  130         
  131         unit = device_get_unit(dev);
  132 
  133         make_dev(&pcfclock_ops, unit,
  134                  UID_ROOT, GID_WHEEL, 0444, PCFCLOCK_NAME "%d", unit);
  135 
  136         return (0);
  137 }
  138 
  139 static int 
  140 pcfclock_open(struct dev_open_args *ap)
  141 {
  142         cdev_t dev = ap->a_head.a_dev;
  143         u_int unit = minor(dev);
  144         struct pcfclock_data *sc = UNITOSOFTC(unit);
  145         device_t pcfclockdev = UNITODEVICE(unit);
  146         device_t ppbus = device_get_parent(pcfclockdev);
  147         int res;
  148         
  149         if (!sc)
  150                 return (ENXIO);
  151 
  152         if ((res = ppb_request_bus(ppbus, pcfclockdev,
  153                 (ap->a_oflags & O_NONBLOCK) ? PPB_DONTWAIT : PPB_WAIT)))
  154                 return (res);
  155 
  156         sc->count++;
  157         
  158         return (0);
  159 }
  160 
  161 static int
  162 pcfclock_close(struct dev_close_args *ap)
  163 {
  164         cdev_t dev = ap->a_head.a_dev;
  165         u_int unit = minor(dev);
  166         struct pcfclock_data *sc = UNITOSOFTC(unit);
  167         device_t pcfclockdev = UNITODEVICE(unit);
  168         device_t ppbus = device_get_parent(pcfclockdev);
  169 
  170         sc->count--;
  171         if (sc->count == 0) {
  172                 ppb_release_bus(ppbus, pcfclockdev);
  173         }
  174 
  175         return (0);
  176 }
  177 
  178 static void
  179 pcfclock_write_cmd(cdev_t dev, unsigned char command)
  180 {
  181         u_int unit = minor(dev);
  182         device_t ppidev = UNITODEVICE(unit);
  183         device_t ppbus = device_get_parent(ppidev);
  184         unsigned char ctr = 14;
  185         char i;
  186         
  187         for (i = 0; i <= 7; i++) {
  188                 ppb_wdtr(ppbus, i);
  189                 AUTOFEED_CLOCK(i & 1 ? AFC_HI : AFC_LO);
  190                 DELAY(3000);
  191         }
  192         ppb_wdtr(ppbus, command);
  193         AUTOFEED_CLOCK(AFC_LO);
  194         DELAY(3000);
  195         AUTOFEED_CLOCK(AFC_HI);
  196 }
  197 
  198 static void
  199 pcfclock_display_data(cdev_t dev, char buf[18]) 
  200 {
  201         u_int unit = minor(dev);
  202 #ifdef PCFCLOCK_VERBOSE
  203         int year;
  204 
  205         year = NR(buf, 14);
  206         if (year < 70)
  207                 year += 100;
  208         kprintf(PCFCLOCK_NAME "%d: %02d.%02d.%4d %02d:%02d:%02d, "
  209                         "battery status: %s\n",
  210                         unit,
  211                         NR(buf, 10), NR(buf, 12), 1900 + year,
  212                         NR(buf, 6), NR(buf, 4), NR(buf, 2),
  213                         PCFCLOCK_BATTERY_STATUS_LOW(buf) ? "LOW" : "ok");
  214 #else
  215         if (PCFCLOCK_BATTERY_STATUS_LOW(buf))
  216                 kprintf(PCFCLOCK_NAME "%d: BATTERY STATUS LOW ON\n",
  217                                 unit);
  218 #endif
  219 }
  220 
  221 static int 
  222 pcfclock_read_data(cdev_t dev, char *buf, ssize_t bits)
  223 {
  224         u_int unit = minor(dev);
  225         device_t ppidev = UNITODEVICE(unit);
  226         device_t ppbus = device_get_parent(ppidev);
  227         int i;
  228         char waitfor;
  229         int offset;
  230 
  231         /* one byte per four bits */
  232         bzero(buf, ((bits + 3) >> 2) + 1);
  233         
  234         waitfor = 100;
  235         for (i = 0; i <= bits; i++) {
  236                 /* wait for clock, maximum (waitfor*100) usec */
  237                 while(!CLOCK_OK && --waitfor > 0)
  238                         DELAY(100);
  239 
  240                 /* timed out? */
  241                 if (!waitfor) 
  242                         return (EIO);
  243                 
  244                 waitfor = 100; /* reload */
  245                 
  246                 /* give it some time */
  247                 DELAY(500);
  248 
  249                 /* calculate offset into buffer */
  250                 offset = i >> 2;
  251                 buf[offset] <<= 1;
  252 
  253                 if (BIT_SET)
  254                         buf[offset] |= 1;
  255         }
  256 
  257         return (0);
  258 }
  259 
  260 static int 
  261 pcfclock_read_dev(cdev_t dev, char *buf, int maxretries) 
  262 {
  263         u_int unit = minor(dev);
  264         device_t ppidev = UNITODEVICE(unit);
  265         device_t ppbus = device_get_parent(ppidev);
  266         int error = 0;
  267 
  268         ppb_set_mode(ppbus, PPB_COMPATIBLE);
  269 
  270         while (--maxretries > 0) {
  271                 pcfclock_write_cmd(dev, PCFCLOCK_CMD_TIME);
  272                 if (pcfclock_read_data(dev, buf, 68))
  273                         continue;
  274                         
  275                 if (!PCFCLOCK_CORRECT_SYNC(buf))
  276                         continue;
  277 
  278                 if (!PCFCLOCK_CORRECT_FORMAT(buf))
  279                         continue;
  280 
  281                 break;
  282         }
  283 
  284         if (!maxretries)
  285                 error = EIO;
  286         
  287         return (error);
  288 }
  289 
  290 static int
  291 pcfclock_read(struct dev_read_args *ap)
  292 {
  293         cdev_t dev = ap->a_head.a_dev;
  294         u_int unit = minor(dev);
  295         char buf[18];
  296         int error = 0;
  297 
  298         if (ap->a_uio->uio_resid < 18)
  299                 return (ERANGE);
  300 
  301         error = pcfclock_read_dev(dev, buf, PCFCLOCK_MAX_RETRIES);
  302         
  303         if (error) {
  304                 kprintf(PCFCLOCK_NAME "%d: no PCF found\n", unit);
  305         } else {
  306                 pcfclock_display_data(dev, buf);
  307                 
  308                 uiomove(buf, 18, ap->a_uio);
  309         }
  310         
  311         return (error);
  312 }
  313 
  314 /*
  315  * Because pcfclock is a static device that always exists under any
  316  * attached ppbus, and not scanned by the ppbus, we need an identify function
  317  * to create the device.
  318  */
  319 static device_method_t pcfclock_methods[] = {
  320         /* device interface */
  321         DEVMETHOD(device_identify,      bus_generic_identify),
  322         DEVMETHOD(device_probe,         pcfclock_probe),
  323         DEVMETHOD(device_attach,        pcfclock_attach),
  324 
  325         DEVMETHOD_END
  326 };
  327 
  328 static driver_t pcfclock_driver = {
  329         PCFCLOCK_NAME,
  330         pcfclock_methods,
  331         sizeof(struct pcfclock_data),
  332 };
  333 
  334 DRIVER_MODULE(pcfclock, ppbus, pcfclock_driver, pcfclock_devclass, NULL, NULL);
  335 

Cache object: d1d6bec1969cf10da4ef4f627da0a96b


[ 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.