File:  [DragonFly] / src / sys / dev / misc / pcfclock / pcfclock.c
Revision 1.6: download - view: text, annotated - select for diffs
Thu May 13 23:49:16 2004 UTC (9 years, 11 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
device switch 1/many: Remove d_autoq, add d_clone (where d_autoq was).

d_autoq was used to allow the device port dispatch to mix old-style synchronous
calls with new style messaging calls within a particular device.  It was never
used for that purpose.

d_clone will be more fully implemented as work continues.  We are going to
install d_port in the dev_t (struct specinfo) structure itself and d_clone
will be needed to allow devices to 'revector' the port on a minor-number
by minor-number basis, in particular allowing minor numbers to be directly
dispatched to distinct threads.  This is something we will be needing later
on.

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