From owner-freebsd-bugs@FreeBSD.ORG Mon Jan 28 06:10:01 2008 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 9D66216A46E for ; Mon, 28 Jan 2008 06:10:01 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 7612613C4E8 for ; Mon, 28 Jan 2008 06:10:01 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.2/8.14.2) with ESMTP id m0S6A1d1084416 for ; Mon, 28 Jan 2008 06:10:01 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.2/8.14.1/Submit) id m0S6A1ZS084415; Mon, 28 Jan 2008 06:10:01 GMT (envelope-from gnats) Resent-Date: Mon, 28 Jan 2008 06:10:01 GMT Resent-Message-Id: <200801280610.m0S6A1ZS084415@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Joe Landers Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D9B7816A419 for ; Mon, 28 Jan 2008 06:02:25 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id C738913C4D1 for ; Mon, 28 Jan 2008 06:02:25 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.2/8.14.2) with ESMTP id m0S60gxn023183 for ; Mon, 28 Jan 2008 06:00:42 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.2/8.14.1/Submit) id m0S60gMK023182; Mon, 28 Jan 2008 06:00:42 GMT (envelope-from nobody) Message-Id: <200801280600.m0S60gMK023182@www.freebsd.org> Date: Mon, 28 Jan 2008 06:00:42 GMT From: Joe Landers To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: kern/120073: [patch] need support for Meinberg PCI-based GPS reference clock X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 28 Jan 2008 06:10:01 -0000 >Number: 120073 >Category: kern >Synopsis: [patch] need support for Meinberg PCI-based GPS reference clock >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Mon Jan 28 06:10:01 UTC 2008 >Closed-Date: >Last-Modified: >Originator: Joe Landers >Release: FreeBSD 7.0-RC1 i386 >Organization: VMware, Inc. >Environment: FreeBSD gps170pci.eng.vmware.com 7.0-RC1 FreeBSD 7.0-RC1 #0: Sun Jan 27 12:33:41 PST 2008 root@gps170pci.eng.vmware.com:/usr /src/sys/i386/compile/GPS170PCI i386 >Description: FreeBSD 7.0 does not support Meinberg GPS reference clocks. The Meinberg GPS reference clock is a current production radio receiver capable of delivering precise time. Although a simple driver exists for OpenBSD, the OpenBSD driver is only suitable for the OpenNTP implemenation. The most useful and popular NTP distribution from ntp.org, included with FreeBSD, can not use this driver. >How-To-Repeat: n/a >Fix: The attached patch provides support the Meinberg GPS170PCI radio clock. This driver has been built and tested on both FreeBSD 7.0 and 6.2 i386 systems. mbg allows the board to be used as a reference clock source for ntpd using the generic (parse) clock driver. VMware, Inc. is providing the MBG Driver (source form) to you under the terms of a BSD license. This Driver is provided as is, with no warranties or support of any kind. VMware disclaims all liability in connection with your use or inability to use this Driver. Any use of the attached is considered acceptance of the above. Thanks, Joe Landers jlanders@vmware.com Patch attached with submission follows: diff -rn -N -U 1 usr/src.old/share/man/man4/mbg.4 usr/src/share/man/man4/mbg.4 --- usr/src.old/share/man/man4/mbg.4 1969-12-31 16:00:00.000000000 -0800 +++ usr/src/share/man/man4/mbg.4 2008-01-27 12:06:27.000000000 -0800 @@ -0,0 +1,111 @@ +.\" +.\" Copyright (c) 2007-2008 VMware, Inc. All rights reserved. +.\" +.\" Joe Landers +.\" +.\" 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. +.\" 3. Neither the name of VMware, Inc. nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" with specific prior written permission. +.\" +.\" 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. +.\" +.Dd November 27, 2007 +.Dt MBG 4 +.Os +.Sh NAME +.Nm mbg +.Nd "Meinberg Funkuhren radio clock" +.Sh SYNOPSIS +.Cd "device mbg" +.Sh DESCRIPTION +The +.Nm +driver supports Meinberg Funkuhren radio clocks. +.Nm +allows the device to be used as a reference clock +source by +.Xr ntpd 8 +via the generic (parse) clock driver. +.Sh HARDWARE +.Nm +provides support for the +GPS170PCI satellite radio plug-in board. +.Sh IOCTLS +The following +.Xr ioctl 2 +calls are supported by the +.Nm +driver. +They are defined in the header file +.In sys/mbgio.h . +.Bl -tag -width indent +.It Dv MBG_GET_DEVICE_NAME Fa "char *" +Get the board's device name. +.It Dv MBG_GET_FIRMWARE_ID Fa "char *" +Get the board's firmware id. +.It Dv MBG_GET_TIME Fa "struct mbg_tframe" +Get the current time in standard format. +.It Dv MBG_GET_HR_TIME Fa "struct mbg_hr_time" +Get the current time in high resolution format. +.It Dv MBG_GET_LAST_SYNC_TIME Fa "struct mbg_tframe" +Get the last synchronization time. +.It Dv MBG_GET_RECV_INFO Fa "struct mbg_gps_recv_info" +Get information about the GPS receiver configuration. +.It Dv MBG_GET_RECV_STAT Fa "struct mbg_gps_stat_info" +Get the GPS receiver status. +.It Dv MBG_GET_GPS_POSITION Fa "struct mbg_gps_position" +Get the GPS receiver's position. +.It Dv MBG_GET_GPS_PORT_PARM Fa "struct mbg_gps_port_parm" +Get the GPS receiver's port parameters. +.It Dv MBG_GET_GPS_TZDL Fa "struct mbg_gps_tzdl" +Get the board's timezone and daylight savings time configuration. +.It Dv MBG_GET_UCAPTURE_STATS Fa "struct mbg_ucapture_stats" +Get the board's user capture statistics. +.It Dv MBG_GET_UCAPTURE_EVENT Fa "struct mbg_hr_time" +Get a user capture event from the FIFO. +.It Dv MBG_SET_UCAPTURE_CLEAR Fa void +Clear the user capture event FIFO. +.It Dv MBG_SET_GPS_CMD Fa "int" +Send a command to the GPS receiver. +.It Dv MBG_SET_GPS_TZDL Fa "struct mbg_gps_tzdl" +Set the timezone and daylight savings parameters. +.It Dv MBG_SET_GPS_POS_LLA Fa "double lla[3]" +Set the latitude, longitude and altitude. +Both latitude and longitude are in radians. +Altitude is in meters. +.It Dv MBG_SET_GPS_TIME Fa "struct mbg_gps_dt" +Set the board's date and time. +.El +.Sh SEE ALSO +.Xr ioctl 2 , +.Xr ntpd 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Joe Landers Aq jlanders@vmware.com . + diff -rn -N -U 1 usr/src.old/sys/conf/files.MBG usr/src/sys/conf/files.MBG --- usr/src.old/sys/conf/files.MBG 1969-12-31 16:00:00.000000000 -0800 +++ usr/src/sys/conf/files.MBG 2008-01-27 12:05:30.000000000 -0800 @@ -0,0 +1 @@ +dev/mbg/mbg.c optional mbg diff -rn -N -U 1 usr/src.old/sys/dev/mbg/mbg.c usr/src/sys/dev/mbg/mbg.c --- usr/src.old/sys/dev/mbg/mbg.c 1969-12-31 16:00:00.000000000 -0800 +++ usr/src/sys/dev/mbg/mbg.c 2008-01-27 12:19:11.000000000 -0800 @@ -0,0 +1,1938 @@ +/* + * Copyright (c) 2007-2008 VMware, Inc. All rights reserved. + * + * Joe Landers + * + * 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. + * 3. Neither the name of VMware, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * with specific prior written permission. + * + * 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. + */ + +/*****************************************************************************/ +/* #includes */ +/*****************************************************************************/ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include /* cdevsw stuff */ +#include /* SYSINIT stuff */ +#include /* SYSINIT stuff */ +#include /* malloc region definitions */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* mbg ioctl() definitions */ + +#include +#include +#include + +#include +#include + +/*****************************************************************************/ +/* #defines and enums */ +/*****************************************************************************/ + +/* + * Macros for handling mbg_softc + */ +#define DEV2SOFTC(dev) ((struct mbg_softc *) (dev)->si_drv1) +#define DEVICE2SOFTC(dev) ((struct mbg_softc *) device_get_softc(dev)) + +/* + * Vendor and device IDs + */ +#define MBG_PCI_VENDOR_MEINBERG 0x1360 +#define MBG_PCI_DEVICE_GPS170PCI 0x0204 + +/* + * Return status codes + * Note: MBG_RET_STATUS_TIMEOUT may be returned if the board + * has a fault or the delay loop in mbg_pci_read() + * isn't properly calibrated. MBG_RET_STATUS_BUSY may + * returned if the board is still initializing and can + * not respond. + */ +#define MBG_RET_STATUS_OK 0x00 /* no error */ +#define MBG_RET_STATUS_TIMEOUT 0x01 /* operation timed out */ +#define MBG_RET_STATUS_BUSY 0x02 /* board reports busy */ +#define MBG_RET_STATUS_NBYTES 0x03 /* parameter byte count invalid */ +#define MBG_RET_STATUS_INV_TYPE 0x04 /* invalid data type specified */ + +/* + * Possible board commands + */ +#define MBG_CMD_GET_GROUP 0x00 /* get time group */ +#define MBG_CMD_GET_TIME 0x00 /* current time */ +#define MBG_CMD_GET_TIME_NC 0x01 /* cur time, don't update last read */ +#define MBG_CMD_GET_SYNC_TIME 0x02 /* last sync'd time */ +#define MBG_CMD_GET_HR_TIME 0x03 /* high resolution time */ + +#define MBG_CMD_SET_GROUP 0x10 /* set time group */ +#define MBG_CMD_SET_TIME 0x10 /* set board time */ +#define MBG_CMD_SET_EVENT_TIME 0x14 /* set event time (needs CERN firmware)*/ + +#define MBG_CMD_IRQ_GROUP 0x20 /* IRQ group */ +#define MBG_CMD_IRQ_NONE 0x20 /* disable board's hardware IRQ */ +#define MBG_CMD_IRQ_1_SEC 0x21 /* enable board's hardware IRQ 1/sec */ +#define MBG_CMD_IRQ_1_MIN 0x22 /* enable board's hardware IRQ 1/min */ +#define MBG_CMD_IRQ_10_MIN 0x24 /* enable board's IRQ every 10 min */ +#define MBG_CMD_IRQ_30_MIN 0x28 /* enable board's IRQ every 30 min */ + +#define MBG_CMD_CFG_GROUP 0x30 /* configuration group */ +#define MBG_CMD_GET_SERIAL 0x30 /* get serial configuration */ +#define MBG_CMD_SET_SERIAL 0x31 /* set serial configuration */ +#define MBG_CMD_GET_TZCODE 0x32 /* get timezone code */ +#define MBG_CMD_SET_TZCODE 0x33 /* set timezone code */ +#define MBG_CMD_GET_TZDL 0x34 /* get timezone code and DST config */ +#define MBG_CMD_SET_TZDL 0x35 /* set timezone code and DST config */ +#define MBG_CMD_GET_REFOFF 0x36 /* get ref offset (for IRIG input) */ +#define MBG_CMD_SET_REFOFF 0x37 /* set ref offset (for IRIG input) */ +#define MBG_CMD_GET_OPT 0x38 /* get optional settings */ +#define MBG_CMD_SET_OPT 0x39 /* set optional settings */ +#define MBG_CMD_GET_IRIG_RX 0x3a /* get IRIG rx config */ +#define MBG_CMD_SET_IRIG_RX 0x3b /* set IRIG rx config */ +#define MBG_CMD_GET_IRIG_TX 0x3c /* get IRIG tx config */ +#define MBG_CMD_SET_IRIG_TX 0x3d /* set IRIG tx config */ +#define MBG_CMD_GET_SYNTH 0x3e /* get synthesizer config */ +#define MBG_CMD_SET_SYNTH 0x3f /* set synthesizer config */ + +#define MBG_CMD_DATA_GROUP 0x40 /* data group */ +#define MBG_CMD_GET_FW_ID_1 0x40 /* get first block of firmware id */ +#define MBG_CMD_GET_FW_ID_2 0x41 /* get second block of firmware id */ +#define MBG_CMD_GET_SERNUM 0x42 /* get serial number */ +#define MBG_CMD_GENERIC_IO 0x43 /* generic i/o */ +#define MBG_CMD_GET_SYNTH_STATE 0x44 /* get synthesizer state */ +#define MBG_CMD_GET_STATUS_PORT 0x4d /* get status port */ +#define MBG_CMD_GET_DEBUG_STATUS 0x4c /* get debug status */ + +#define MBG_CMD_GPS_GROUP 0x50 /* GPS group */ +#define MBG_CMD_GET_GPS_DATA 0x50 /* read GPS data */ +#define MBG_CMD_SET_GPS_DATA 0x51 /* set GPS data */ + +#define MBG_CMD_CONTROL_GROUP 0x60 /* control group */ +#define MBG_CMD_CLEAR_CAP_BUF 0x60 /* clear capture buffer */ +#define MBG_CMD_GET_CAP_STATS 0x61 /* get capture buffer stats */ +#define MBG_CMD_GET_CAP_EVENT 0x62 /* get event from capture buffer */ + +#define MBG_CMD_FORCE_RESET 0x80 /* reset microprocessor */ + +/* + * Feature list + */ +#define MBG_FEAT_SET_TIME 0x00001UL +#define MBG_FEAT_SERIAL 0x00002UL +#define MBG_FEAT_SYNC_TIME 0x00004UL +#define MBG_FEAT_TZDL 0x00008UL +#define MBG_FEAT_IDENT 0x00010UL +#define MBG_FEAT_UTC_OFFS 0x00020UL +#define MBG_FEAT_HR_TIME 0x00040UL +#define MBG_FEAT_SERNUM 0x00080UL +#define MBG_FEAT_TZCODE 0x00100UL +#define MBG_FEAT_CABLE_LEN 0x00200UL +#define MBG_FEAT_EVENT_TIME 0x00400UL +#define MBG_FEAT_RECV_INFO 0x00800UL +#define MBG_FEAT_CLR_UCAP_BUFF 0x01000UL +#define MBG_FEAT_PCPS_TZDL 0x02000UL +#define MBG_FEAT_UCAP 0x04000UL +#define MBG_FEAT_IRIG_TX 0x08000UL +#define MBG_FEAT_GPS_DATA_16 0x10000UL +#define MBG_FEAT_SYNTH 0x20000UL +#define MBG_FEAT_GENERIC_IO 0x40000UL + +/* + * GPS170PCI features. + * Note: See also mbg_pci_attach() where, after a GPS + * engine query, MBG_FEAT_SYNTH may get set too + */ +#define MBG_FEAT_GPS170PCI ( MBG_FEAT_SET_TIME \ + | MBG_FEAT_SERIAL \ + | MBG_FEAT_SYNC_TIME \ + | MBG_FEAT_TZDL \ + | MBG_FEAT_IDENT \ + | MBG_FEAT_UTC_OFFS \ + | MBG_FEAT_HR_TIME \ + | MBG_FEAT_CABLE_LEN \ + | MBG_FEAT_RECV_INFO \ + | MBG_FEAT_CLR_UCAP_BUFF \ + | MBG_FEAT_UCAP \ + | MBG_FEAT_IRIG_TX \ + | MBG_FEAT_GPS_DATA_16 \ + | MBG_FEAT_GENERIC_IO ) + +/* + * ASIC register offsets + */ +#define MBG_ASIC_REG_CFG 0x00 /* writable config */ +#define MBG_ASIC_REG_FEATURES 0x08 /* r/o, currently 0 */ +#define MBG_ASIC_REG_STATUS 0x10 /* status */ +#define MBG_ASIC_REG_CTLSTATUS 0x14 /* control status */ +#define MBG_ASIC_REG_DATA 0x18 /* ASIC data (for write) */ +#define MBG_ASIC_REG_RES1 0x1c /* reserved */ +#define MBG_ASIC_REG_FIFO 0x20 /* ASIC FIFO (for read) */ + +#define MBG_ASIC_PCI_IRQ_FLAG 0x00010000UL + +/* + * Supported IRQ operations + */ +enum MBG_IRQ_OP +{ + MBG_IRQ_OP_DISABLE = 0, /* disable 1/sec IRQ */ + MBG_IRQ_OP_ENABLE, /* enable 1/sec IRQ */ + MBG_IRQ_OP_ACK /* ack an IRQ */ +}; + +/* + * GPS receiver commands + */ +enum +{ + MBG_GPS_TZDL = 0, /* Time zone / daylight saving */ + MBG_GPS_SW_REV, /* Software revision */ + MBG_GPS_BVAR_STAT, /* Status of buffered variables */ + MBG_GPS_TIME, /* Current time */ + MBG_GPS_POS_XYZ, /* Current pos. in ECEF coords */ + MBG_GPS_POS_LLA, /* Current pos. in geogr. coords */ + MBG_GPS_PORT_PARM, /* Serial port params */ + MBG_GPS_ANT_INFO, /* Time diff after ant. disconnected */ + MBG_GPS_UCAP, /* User capture */ + MBG_GPS_ENABLE_FLAGS, /* Controls when to enable output */ + MBG_GPS_STAT_INFO, /* */ + MBG_GPS_CMD, /* Commands */ + MBG_GPS_IDENT, /* Serial number */ + MBG_GPS_POS, /* Position XYZ, LLA, and DMS */ + MBG_GPS_ANT_CABLE_LEN, /* Used to compensate delay */ + + /* + * The codes below are supported by new GPS receiver boards + */ + MBG_GPS_RECV_INFO, /* Receiver model info */ + MBG_GPS_ALL_STR_TYPE_INFO, /* All string types */ + MBG_GPS_ALL_PORT_INFO, /* All port info */ + MBG_GPS_PORT_SETTINGS_IDX, /* Port settings only */ + MBG_GPS_ALL_POUT_INFO, /* All pout info */ + MBG_GPS_POUT_SETTINGS_IDX, /* Pout settings only */ + + /* + * GPS data + */ + MBG_GPS_CFGH = 0x80, /* SVs' config. and health codes */ + MBG_GPS_ALM, /* One SV's num and almanac */ + MBG_GPS_EPH, /* One SV's num and ephemeris */ + MBG_GPS_UTC, /* UTC corr. param. */ + MBG_GPS_IONO, /* Ionospheric corr. param. */ + MBG_GPS_ASCII_MSG /* GPS ASCII message */ +}; + +/* + * Status port mask + */ +#define MBG_STATUS_BUSY 0x01 /* busy filling output FIFO */ +#define MBG_STATUS_IRQF 0x02 /* generated an IRQ */ + +/* + * Constants + */ +#define MBG_FIFO_LEN 16 +#define MBG_ID_LEN (2 * MBG_FIFO_LEN + 1) +#define MBG_TSTAMP_LEN 33 + +/*****************************************************************************/ +/* data structures */ +/*****************************************************************************/ + +/* + * mbg_softc + */ +struct mbg_softc { + bus_space_tag_t bt; /* bus space tag */ + bus_space_handle_t bh; /* bus space handle */ + int rid_ioport; /* I/O port resource */ + int rid_irq; /* IRQ resource */ + uint32_t features; /* card features mask */ + struct mbg_tframe tframe; /* time frame */ + struct resource *res_ioport; /* resource for port range */ + struct resource *res_irq; /* resource for irq range */ + device_t device; /* device handle */ + void *intr_cookie; /* interrupt cookie */ + int (*mbg_irq_op)(struct mbg_softc *, enum MBG_IRQ_OP); + int (*mbg_read)(struct mbg_softc *scp, uint32_t cmd, + void *buf, size_t len); + int (*mbg_got_intr)(struct mbg_softc *scp); + struct mbgntp_struct { + struct cdev *ntp; /* mbgntp cdev handle */ + struct mtx spinLock; /* driver spin lock */ + struct mtx sema; /* driver semaphore */ + struct mtx lock; /* lock protecting "opened" field */ + struct selinfo select;/* select() info */ + int hasTimeData; /* time frame field has data */ + int opened; /* opened device count */ + } mbgntp; + struct mbgclk_struct { + struct cdev *clk; /* mbgclk cdev handle */ + struct mtx lock; /* lock protecting "opened" field */ + int opened; /* opened device count */ + int accessInProgress; /* set if access is in progress */ + } mbgclk; +}; + +/*****************************************************************************/ +/* function prototypes */ +/*****************************************************************************/ + +/* + * Generic functions + */ +static int mbg_deallocate_resources(device_t device); +static int mbg_allocate_resources(device_t device); +static int mbg_attach(device_t device, struct mbg_softc *scp); +static int mbg_detach(device_t device, struct mbg_softc *scp); + +/* + * PCI functions + */ +static int mbg_pci_probe(device_t); +static int mbg_pci_attach(device_t); +static int mbg_pci_detach(device_t); +static int mbg_pci_shutdown(device_t); +static int mbg_pci_suspend(device_t); +static int mbg_pci_resume(device_t); + +/* + * mbgntp cdevsw functions + */ +static d_open_t mbgntp_open; +static d_close_t mbgntp_close; +static d_read_t mbgntp_read; +static d_write_t mbgntp_write; +static d_ioctl_t mbgntp_ioctl; +static d_poll_t mbgntp_poll; + +/* + * mbgclk cdevsw functions + */ +static d_open_t mbgclk_open; +static d_close_t mbgclk_close; +static d_ioctl_t mbgclk_ioctl; + +/* + * interrupt handler + */ +static void mbg_intr(void *arg); + +/*****************************************************************************/ +/* statics and global variables */ +/*****************************************************************************/ + +/* + * mbgntp cdevsw + */ +static struct cdevsw mbgntp_cdevsw = { + .d_version = D_VERSION, + .d_open = mbgntp_open, + .d_close = mbgntp_close, + .d_read = mbgntp_read, + .d_write = mbgntp_write, + .d_ioctl = mbgntp_ioctl, + .d_poll = mbgntp_poll, + .d_name = "mbgntp", +}; + +/* + * mbgclk cdevsw + */ +static struct cdevsw mbgclk_cdevsw = { + .d_version = D_VERSION, + .d_open = mbgclk_open, + .d_close = mbgclk_close, + .d_ioctl = mbgclk_ioctl, + .d_name = "mbgclk", +}; + +/* + * devclass + */ +static devclass_t mbg_devclass; + +/* + * PCI attachment + */ +static device_method_t mbg_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mbg_pci_probe), + DEVMETHOD(device_attach, mbg_pci_attach), + DEVMETHOD(device_detach, mbg_pci_detach), + DEVMETHOD(device_shutdown, mbg_pci_shutdown), + DEVMETHOD(device_suspend, mbg_pci_suspend), + DEVMETHOD(device_resume, mbg_pci_resume), + { 0, 0 } +}; + +static driver_t mbg_pci_driver = { + "mbg", + mbg_pci_methods, + sizeof(struct mbg_softc), +}; + +static struct mbg_pci_ids +{ + uint16_t vendor_id; + uint16_t device_id; + const char *desc; +} mbg_pci_ids[] = { + { MBG_PCI_VENDOR_MEINBERG, + MBG_PCI_DEVICE_GPS170PCI, + "Meinberg GPS170PCI Radio Clock" }, + { 0x0000, + 0x0000, + NULL } +}; + +/* + * Module glue + */ +DRIVER_MODULE(mbg, pci, mbg_pci_driver, mbg_devclass, 0, 0); +MODULE_DEPEND(em, pci, 1, 1, 1); + +/* + * Driver version string + */ +char mbg_driver_version[] = "Version - 1.0.1"; + +/* + * Onboard oscillator names + */ +static char *mbg_osc_feature_names[] = { + "unknown", + "TCXO LQ", + "TCXO HQ", + "OCXO LQ", + "OCXO MQ", + "OCXO HQ", + "OCXO XHQ", + "RUBIDIUM", + "TCXO MQ", + "OCXO DHQ" +}; + +/* + * GPS feature names + */ +static char *mbg_gps_feature_names[] = { + "Pulse Per Second", + "Pulse Per Minute", + "Programmable Synthesizer Output", + "DCF77 Time Marks", + "Onboard IRIG Out", + "Onboard IRIG In", + "IPv4 LAN Interface", + "Multiple Reference Sources", + "GPS Receive Timeout", + "Ignore Lock", + "5 MHz Output", + "External Multiple Ref. Src. Cfg.", + "Supports Optional Settings" +}; + +/*****************************************************************************/ +/* utilities */ +/*****************************************************************************/ + +/* + * mbg_swap32: + * Swap uint16_t's in a uint32_t + * + * bytes before: + * +---+---+---+---+ + * | 0 | 1 | 2 | 3 | + * +---+---+---+---+ + * + * bytes after: + * +---+---+---+---+ + * | 2 | 3 | 0 | 1 | + * +---+---+---+---+ + * + * Returns swapped value + */ +static inline uint32_t +mbg_swap32(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + return p[2] | (p[3] << 8) | (p[0] << 16) | (p[1] << 24); +} + +/* + * mbg_swap64 + * Swap uint16_t's in a uint64_t (i.e. "swap doubles") + * + * bytes before: + * +---+---+---+---+---+---+---+---+ + * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + * +---+---+---+---+---+---+---+---+ + * + * bytes after: + * +---+---+---+---+---+---+---+---+ + * | 6 | 7 | 4 | 5 | 2 | 3 | 0 | 1 | + * +---+---+---+---+---+---+---+---+ + * + * Returns swapped value + */ +static inline uint64_t +mbg_swap64(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + return (uint64_t)mbg_swap32(p + 4) | + ((uint64_t)mbg_swap32(p) << 32); +} + +/* + * mbg_pci_read: + * Send a command and read back results + * + * Returns one of MBG_RET_STATUS_* + */ +static int +mbg_pci_read(struct mbg_softc *scp, uint32_t cmd, void *buf, size_t len) +{ + size_t n; + uint32_t data; + uint16_t port; + uint8_t status; + char *p = buf; + long timer, tmax; + + /* + * Write the command + */ + bus_space_write_4(scp->bt, scp->bh, MBG_ASIC_REG_DATA, cmd); + + /* + * Wait for BUSY to go low + */ + tmax = cold ? 1000 : 200 * (hz / 1000); + timer = ticks + tmax; + do { + status = bus_space_read_1(scp->bt, scp->bh, + MBG_ASIC_REG_STATUS); + } while ((status & MBG_STATUS_BUSY) && (ticks <= timer)); + + /* + * Error, "never" became available + */ + if ((status & MBG_STATUS_BUSY) || (ticks > timer)) + return MBG_RET_STATUS_TIMEOUT; + + /* + * Read data from the FIFO + */ + port = MBG_ASIC_REG_FIFO; + for (n = 0; n < len / 4; n++) { + data = bus_space_read_4(scp->bt, scp->bh, port); + *(uint32_t *)p = data; + p += sizeof(data); + port += sizeof(data); + } + + if (len % 4) { + data = bus_space_read_4(scp->bt, scp->bh, port); + for (n = 0; n < len % 4; n++) { + *p++ = data & 0xff; + data >>= 8; + } + } + + return MBG_RET_STATUS_OK; +} + +/* + * mbg_gps_read_block: + * Get one block of data from the GPS receiver + * + * Returns one of MBG_RET_STATUS_* + */ +static int +mbg_gps_read_block(struct mbg_softc *scp, uint8_t dataType, + void *buf, size_t bufLen, + uint32_t blockNum, size_t blockSize) +{ + int rc; + uint8_t dataSize, sizeNBytes; + uint16_t nBytes = 0; + + /* + * Write the command + */ + rc = scp->mbg_read(scp, MBG_CMD_GET_GPS_DATA, + &dataSize, sizeof(dataSize)); + if (rc != MBG_RET_STATUS_OK) { + device_printf(scp->device, + "mbg_gps_read_block(): MBG_CMD_GET_GPS_DATA failed!\n"); + return rc; + } + + if (dataSize != 1) { + if (dataSize == 0) { + device_printf(scp->device, + "mbg_gps_read_block(): dataSize == 0!\n"); + return MBG_RET_STATUS_BUSY; + } else { + device_printf(scp->device, + "mbg_gps_read_block(): dataSize == %d!\n", + dataSize); + return MBG_RET_STATUS_NBYTES; + } + } + + /* + * Write the data type code + */ + if (scp->features & MBG_FEAT_GPS_DATA_16) + sizeNBytes = sizeof(nBytes); + else { + if (bufLen > 255) + return MBG_RET_STATUS_NBYTES; + sizeNBytes = sizeof(uint8_t); + } + + rc = scp->mbg_read(scp, dataType, &nBytes, sizeNBytes); + if (rc != MBG_RET_STATUS_OK) { + device_printf(scp->device, + "mbg_gps_read_block(): can't set data type\n"); + return rc; + } + + if (nBytes == 0) { + device_printf(scp->device, + "mbg_gps_read_block(): invalid data type\n"); + return MBG_RET_STATUS_INV_TYPE; + } + + if (nBytes != bufLen) { + device_printf(scp->device, + "mbg_gps_read_block(): nBytes (%d) != bufLen (%d)\n", + nBytes, bufLen); + return MBG_RET_STATUS_NBYTES; + } + + /* + * Write the block number, and read blockSize bytes + */ + rc = scp->mbg_read(scp, blockNum, buf, blockSize); + if (rc != MBG_RET_STATUS_OK) + device_printf(scp->device, + "mbg_gps_read_block(): block %d size %d failed\n", + blockNum, blockSize); + + return rc; +} + +/* + * mbg_gps_read: + * Get data from the GPS receiver + * + * Returns one of MBG_RET_STATUS_* + */ +static int +mbg_gps_read(struct mbg_softc *scp, uint8_t dataType, + void *buf, size_t bufLen) +{ + char *p = buf; + int i, rc = MBG_RET_STATUS_OK; + int nBlocks = bufLen / MBG_FIFO_LEN; + int nRemain = bufLen % MBG_FIFO_LEN; + + /* + * Get nBlocks of data + */ + for (i = 0; i < nBlocks; i++) { + rc = mbg_gps_read_block(scp, dataType, p, + bufLen, i, MBG_FIFO_LEN); + if (rc != MBG_RET_STATUS_OK) { + device_printf(scp->device, + "mbg_gps_read(): can't read block %d/%d\n", + i, nBlocks); + return rc; + } + p += MBG_FIFO_LEN; + } + + /* + * Get nRemain bytes of data + */ + if (nRemain) { + rc = mbg_gps_read_block(scp, dataType, p, + bufLen, i, nRemain); + if (rc != MBG_RET_STATUS_OK) + device_printf(scp->device, + "mbg_gps_read(): can't read remain block %d, size %d\n", + i, nRemain); + } + + return rc; +} + +/* + * mbg_gps_write + * Write data to the GPS receiver + * + * Returns one of MBG_RET_STATUS_* + */ +static int +mbg_gps_write(struct mbg_softc *scp, uint8_t dataType, + void *buf, size_t bufLen) +{ + int i, rc; + uint8_t *p = (uint8_t *)buf; + uint16_t nBytes = 0, retVal = 0; + uint8_t dataSize, sizeNBytes, sizeRetVal; + + /* + * Write the command + */ + rc = scp->mbg_read(scp, MBG_CMD_SET_GPS_DATA, + &dataSize, sizeof(dataSize)); + if (rc != MBG_RET_STATUS_OK) { + device_printf(scp->device, + "mbg_gps_write(): MBG_CMD_SET_GPS_DATA failed!\n"); + return rc; + } + + if (dataSize != 1) { + device_printf(scp->device, + "mbg_gps_write(): dataSize == %d!\n", + dataSize); + return MBG_RET_STATUS_NBYTES; + } + + /* + * Write the data type code + */ + if (scp->features & MBG_FEAT_GPS_DATA_16) { + sizeNBytes = sizeof(nBytes); + sizeRetVal = sizeof(rc); + } else { + if (bufLen > 255) + return MBG_RET_STATUS_NBYTES; + sizeRetVal = sizeNBytes = sizeof(uint8_t); + } + + rc = scp->mbg_read(scp, dataType, &nBytes, sizeNBytes); + if (rc != MBG_RET_STATUS_OK) { + device_printf(scp->device, + "mbg_gps_write(): can't set datatype\n"); + return rc; + } + + if (nBytes != bufLen) { + device_printf(scp->device, + "mbg_gps_write(): dataType nBytes (%d) != bufLen (%d)\n", + nBytes, bufLen); + return MBG_RET_STATUS_NBYTES; + } + + /* + * Write bufLen - 1 bytes without reading + */ + for (i = 0; i < bufLen - 1; i++) { + rc = scp->mbg_read(scp, *p++, NULL, 0); + if (rc != MBG_RET_STATUS_OK) { + device_printf(scp->device, + "mbg_gps_write(): %d/%d failed\n", i, bufLen); + return rc; + } + } + + /* + * Write final byte from buffer and read result + */ + rc = scp->mbg_read(scp, *p, &retVal, sizeRetVal); + if (rc != MBG_RET_STATUS_OK) { + device_printf(scp->device, + "mbg_gps_write(): last byte write failed\n"); + } + + if (rc != MBG_RET_STATUS_OK) { + return rc; + } else { + return retVal; + } +} + +/* + * mbg_pci_irq_op: + * enable, disable or ack the interface IRQ + * + * Returns one of MBG_RET_STATUS_* + */ +static int +mbg_pci_irq_op(struct mbg_softc *scp, enum MBG_IRQ_OP op) +{ + int32_t rc; + uint32_t ctlStatus; + + switch (op) { + case MBG_IRQ_OP_DISABLE: + rc = scp->mbg_read(scp, MBG_CMD_IRQ_NONE, NULL, 0); + if (rc != MBG_RET_STATUS_OK) { + KASSERT(0, "mbg_pci_irq_op(): can't disable IRQ\n"); + return rc; + } + ctlStatus = bus_space_read_4(scp->bt, + scp->bh, + MBG_ASIC_REG_CTLSTATUS); + bus_space_write_4(scp->bt, + scp->bh, + MBG_ASIC_REG_CTLSTATUS, + ctlStatus & ~(MBG_ASIC_PCI_IRQ_FLAG)); + break; + case MBG_IRQ_OP_ENABLE: + rc = scp->mbg_read(scp, MBG_CMD_IRQ_1_SEC, NULL, 0); + if (rc != MBG_RET_STATUS_OK) { + KASSERT(0, "mbg_pci_irq_op(): can't enable IRQ\n"); + return rc; + } + ctlStatus = bus_space_read_4(scp->bt, + scp->bh, + MBG_ASIC_REG_CTLSTATUS); + bus_space_write_4(scp->bt, + scp->bh, + MBG_ASIC_REG_CTLSTATUS, + ctlStatus | MBG_ASIC_PCI_IRQ_FLAG); + break; + case MBG_IRQ_OP_ACK: + bus_space_write_4(scp->bt, + scp->bh, + MBG_ASIC_REG_CTLSTATUS, + MBG_ASIC_PCI_IRQ_FLAG); + break; + default: + KASSERT(0, ("mbg_pci_irq_op unknown!")); + break; + } + + return MBG_RET_STATUS_OK; +} + +/* + * mbg_pci_got_intr: + * see if the interface really generated an IRQ + * + * Returns 1 if yes, 0 otherwise + */ +static int +mbg_pci_got_intr(struct mbg_softc *scp) +{ + if (bus_space_read_4(scp->bt, + scp->bh, + MBG_ASIC_REG_CTLSTATUS) & + MBG_ASIC_PCI_IRQ_FLAG) + return 1; + + return 0; +} + +/* + * mbg_tframe2str: + * Convert a time frame to a string in Meinberg Standard format + * + * Returns nothing + */ +static void +mbg_tframe2str(struct mbg_tframe *tframe, char *str) +{ + char *p = str; + + if (tframe->status & MBG_STATUS_INVALID) { + tframe->status |= MBG_STATUS_FREERUN; + tframe->status &= ~MBG_STATUS_SYNC; + } + + /* + * STX + */ + *p++ = '\02'; + + /* + * Meinberg standard time string + * + * Note: This is the time format expected by the + * NTP parse clock driver. + */ + p += sprintf(p, "D:%02d.%02d.%02d;T:%1d;U:%02d.%02d.%02d;", + tframe->mday, tframe->mon, tframe->year, tframe->wday, + tframe->hour, tframe->min, tframe->sec); + + /* + * Has clock sync'd after reset? + */ + *p++ = (tframe->status & MBG_STATUS_SYNC) ? ' ' : '#'; + + /* + * Has GPS receiver checked it position? + */ + *p++ = (tframe->status & MBG_STATUS_FREERUN) ? '*' : ' '; + + /* + * Are we using UTC, if not are we in MEZ summertime? + */ + *p++ = (tframe->status & MBG_STATUS_UTC) ? 'U' : /* UTC */ + ((tframe->status & MBG_STATUS_DST_ENA) ? 'S' : ' ' ); + + /* + * Is there a DST announcement? A leap second announcement? + */ + *p++ = (tframe->status & MBG_STATUS_DST_CHG) ? '!' : + ((tframe->status & MBG_STATUS_LEAP) ? 'A' : ' ' ); + + /* + * ETX + */ + *p++ = '\03'; + + *p = 0; +} + +/*****************************************************************************/ +/* PCI probe and attach */ +/*****************************************************************************/ + +/* + * mbg_pci_probe: + * See if the driver should be loaded based on the PCI + * vendor and device ID of the adapter. + * + * Returns: + * BUS_PROBE_DEFAULT, ENXIO on failure. + */ +static int +mbg_pci_probe(device_t device) +{ + uint16_t pci_vendor_id; + uint16_t pci_device_id; + struct mbg_pci_ids *ent = mbg_pci_ids; + + pci_vendor_id = pci_get_vendor(device); + pci_device_id = pci_get_device(device); + + while (ent->vendor_id != 0) { + if ((pci_vendor_id == ent->vendor_id) && + (pci_device_id == ent->device_id)) { + device_set_desc(device, ent->desc); + return BUS_PROBE_DEFAULT; + } + ++ent; + } + + return ENXIO; +} + +/* + * mbg_pci_attach: + * Attach a PCI device and identify card features + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_pci_attach(device_t device) +{ + int error, rc; + char firmware[MBG_ID_LEN]; + struct mbg_softc *scp = DEVICE2SOFTC(device); + + KASSERT(scp != NULL, ("mbg_pci_attach: null software carrier!")); + + bzero(scp, sizeof (*scp)); + scp->device = device; + + /* + * Do generic attach + */ + error = mbg_attach(device, scp); + if (error) { + mbg_pci_detach(device); + return error; + } + + /* + * Set up feature mask and handlers for each device. + * Obviously, only the GPS170PCI is supported here. + */ + switch (pci_get_device(device)) { + case MBG_PCI_DEVICE_GPS170PCI: + scp->features = MBG_FEAT_GPS170PCI; + scp->mbg_read = mbg_pci_read; + scp->mbg_irq_op = mbg_pci_irq_op; + scp->mbg_got_intr = mbg_pci_got_intr; + break; + default: + scp->features = 0x0; + break; + } + + /* + * Sanity check + */ + KASSERT(scp->mbg_read, "mbg_pci_attach(): no mbg_read() defined\n"); + KASSERT(scp->mbg_irq_op, "mbg_pci_attach(): no mbg_irq_op() defined\n"); + KASSERT(scp->mbg_got_intr, "mbg_pci_attach(): no mbg_got_intr() defined\n"); + + /* + * Get the firmware version + */ + rc = scp->mbg_read(scp, MBG_CMD_GET_FW_ID_1, + firmware, MBG_FIFO_LEN); + if (rc == MBG_RET_STATUS_OK) { + rc = scp->mbg_read(scp, MBG_CMD_GET_FW_ID_2, + &firmware[MBG_FIFO_LEN], MBG_FIFO_LEN); + if (rc == MBG_RET_STATUS_OK) { + firmware[MBG_ID_LEN - 1] = '\0'; + device_printf(device, "firmware: %s, ", firmware); + } + } + + /* + * Get the time from the board + */ + rc = scp->mbg_read(scp, MBG_CMD_GET_TIME, + &scp->tframe, sizeof(scp->tframe)); + if (rc == MBG_RET_STATUS_OK) { + printf("date: %d/%d/%d, time: %02d:%02d:%02d.%02d, ", + scp->tframe.mon, scp->tframe.mday, + scp->tframe.year + 2000, scp->tframe.hour, + scp->tframe.min, scp->tframe.sec, + scp->tframe.hundreds); + if (scp->tframe.status & MBG_STATUS_FREERUN) { + printf("(free running on xtal)\n"); + } else if (scp->tframe.status & MBG_STATUS_SYNC) { + printf("(synchronized)\n"); + } else if (scp->tframe.status & MBG_STATUS_INVALID) { + printf("(invalid)\n"); + } + } + + /* + * Get the GPS receiver info + */ + if (scp->features & MBG_FEAT_RECV_INFO) { + int i; + struct mbg_gps_recv_info ri; + rc = mbg_gps_read(scp, MBG_GPS_RECV_INFO, &ri, sizeof(ri)); + if (rc == MBG_RET_STATUS_OK) { + device_printf(device, "serial number: %s, ", + ri.serial); + printf("GPS features: "); + for (i = 0; i < MBG_GPS_NFEATURES; i++) { + if ((ri.features & (1 << i)) && + (i < sizeof(mbg_osc_feature_names)/sizeof(char *))) { + printf("%s, ", mbg_gps_feature_names[i]); + } + } + if (ri.features & MBG_GPS_FEAT_IRIG_TX) + scp->features |= MBG_FEAT_IRIG_TX; + if (ri.features & MBG_GPS_FEAT_SYNTH) + scp->features |= MBG_FEAT_SYNTH; + if (ri.osc_type >= sizeof(mbg_osc_feature_names)/sizeof(char *)) + ri.osc_type = 0; + printf(" oscillator: %s\n", + mbg_osc_feature_names[ri.osc_type]); + } + } + + return 0; +} + +/* + * mbg_pci_detach: + * detach a PCI device + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_pci_detach(device_t device) +{ + int error; + struct mbg_softc *scp = DEVICE2SOFTC(device); + + KASSERT(scp != NULL, ("mbg_pci_detach: null software carrier!")); + + /* + * Do generic detach + */ + error = mbg_detach(device, scp); + + return error; +} + +/*****************************************************************************/ +/* Common attachment functions */ +/*****************************************************************************/ + +/* + * mbg_attach: + * Common attach function + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_attach(device_t device, struct mbg_softc *scp) +{ + int unit = device_get_unit(device); + + /* + * make mbgntp device + */ + scp->mbgntp.ntp = make_dev(&mbgntp_cdevsw, unit, + UID_ROOT, GID_OPERATOR, + S_IRUSR | S_IWUSR, + "mbgntp"); + scp->mbgntp.ntp->si_drv1 = scp; + + /* + * make mbgclk device + */ + scp->mbgclk.clk = make_dev(&mbgclk_cdevsw, unit, + UID_ROOT, GID_OPERATOR, + S_IRUSR | S_IWUSR, + "mbgclk"); + scp->mbgclk.clk->si_drv1 = scp; + + if (mbg_allocate_resources(device)) + goto errexit; + + scp->bt = rman_get_bustag(scp->res_ioport); + scp->bh = rman_get_bushandle(scp->res_ioport); + + /* + * Setup interrupt + */ + if (scp->res_irq) { + if (!bus_setup_intr(device, scp->res_irq, + INTR_TYPE_TTY, +#if __FreeBSD_version >= 700000 + NULL, +#endif + mbg_intr, scp, &scp->intr_cookie) == 0) + goto errexit; + } + + mtx_init(&scp->mbgntp.lock, device_get_nameunit(device), + "mbgntp", MTX_DEF); + mtx_init(&scp->mbgclk.lock, device_get_nameunit(device), + "mbgclk", MTX_DEF); + + return 0; + +errexit: + /* + * Undo anything we may have done. + */ + mbg_detach(device, scp); + return ENXIO; +} + +/* + * mbg_detach: + * Common detach function + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_detach(device_t device, struct mbg_softc *scp) +{ + int retval; + + /* + * Remove interrupt handler + */ + if (scp->intr_cookie != NULL) { + if (bus_teardown_intr(device, scp->res_irq, + scp->intr_cookie) != 0) + device_printf(device, + "mbg_detach(): intr teardown failed\n"); + scp->intr_cookie = NULL; + } + + retval = mbg_deallocate_resources(device); + mtx_destroy(&scp->mbgclk.lock); + mtx_destroy(&scp->mbgntp.lock); + + return retval; +} + +/* + * mbg_allocate_resources: + * Common resource allocation + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_allocate_resources(device_t device) +{ + int error; + struct mbg_softc *scp = DEVICE2SOFTC(device); + + KASSERT(scp != NULL, ("mbg_alloc_resources: null software carrier!")); + + scp->rid_ioport = PCIR_BAR(0); + scp->res_ioport = bus_alloc_resource_any(device, SYS_RES_IOPORT, + &scp->rid_ioport, RF_ACTIVE); + if (scp->res_ioport == NULL) + goto errexit; + + scp->res_irq = bus_alloc_resource(device, SYS_RES_IRQ, + &scp->rid_irq, 0ul, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE); + if (scp->res_irq == NULL) + goto errexit; + + return 0; + +errexit: + error = ENXIO; + + /* + * Cleanup anything assigned + */ + mbg_deallocate_resources(device); + + return ENXIO; +} + +/* + * mbg_deallocate_resources: + * Common resource deallocation + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_deallocate_resources(device_t device) +{ + struct mbg_softc *scp = DEVICE2SOFTC(device); + + if (scp->res_irq != 0) { + bus_deactivate_resource(device, SYS_RES_IRQ, + scp->rid_irq, scp->res_irq); + bus_release_resource(device, SYS_RES_IRQ, + scp->rid_irq, scp->res_irq); + scp->res_irq = 0; + } + + if (scp->res_ioport != 0) { + bus_deactivate_resource(device, SYS_RES_IOPORT, + scp->rid_ioport, scp->res_ioport); + bus_release_resource(device, SYS_RES_IOPORT, + scp->rid_ioport, scp->res_ioport); + scp->res_ioport = 0; + } + + if (scp->mbgclk.clk) { + destroy_dev(scp->mbgclk.clk); + scp->mbgclk.clk = (struct cdev *)NULL; + } + + if (scp->mbgntp.ntp) { + destroy_dev(scp->mbgntp.ntp); + scp->mbgntp.ntp = (struct cdev *)NULL; + } + + return 0; +} + +/* + * mbg_pci_shutdown: + * Shutdown entry point + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_pci_shutdown(device_t dev) +{ + return 0; +} + +/* + * mbg_pci_suspend: + * Suspend device method + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_pci_suspend(device_t dev) +{ + return bus_generic_suspend(dev); +} + +/* + * mbg_pci_resume: + * Resume device method + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbg_pci_resume(device_t dev) +{ + return bus_generic_resume(dev); +} + +/*****************************************************************************/ +/* interrupt processing functions */ +/*****************************************************************************/ + +/* + * mbg_intr: + * Handle an interrupt + * + * Returns nothing + */ +static void +mbg_intr(void *arg) +{ + int rc, accessInProgress; + struct mbg_softc *scp = (struct mbg_softc *) arg; + + /* + * Was this our interrupt? + */ + if (!scp->mbg_got_intr(scp)) + return; + + /* + * Get the time + */ + mtx_lock_spin(&scp->mbgntp.spinLock); + accessInProgress = scp->mbgclk.accessInProgress; + if (!accessInProgress) + rc = scp->mbg_read(scp, MBG_CMD_GET_TIME, + &scp->tframe, sizeof(scp->tframe)); + else + rc = MBG_RET_STATUS_BUSY; + + /* + * ACK the interrupt + */ + scp->mbg_irq_op(scp, MBG_IRQ_OP_ACK); + + /* + * Wakeup readers + */ + if (rc == MBG_RET_STATUS_OK) { + scp->mbgntp.hasTimeData = 1; + wakeup(&scp->mbgntp.hasTimeData); + selwakeup(&scp->mbgntp.select); + } + mtx_unlock_spin(&scp->mbgntp.spinLock); + + return; +} + +/*****************************************************************************/ +/* mbgntp cdevsw functions */ +/*****************************************************************************/ + +/* + * mbgntp_open: + * Handle an open request on /dev/mbgntp + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbgntp_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct mbg_softc *scp = DEV2SOFTC(dev); + + /* + * Only 1 instance allowed + */ + mtx_lock(&scp->mbgntp.lock); + if (scp->mbgntp.opened) { + mtx_unlock(&scp->mbgntp.lock); + return EBUSY; + } + scp->mbgntp.opened++; + mtx_unlock(&scp->mbgntp.lock); + + mtx_init(&scp->mbgntp.spinLock, "mbgSpin", NULL, MTX_SPIN); + mtx_init(&scp->mbgntp.sema, "mbgSema", NULL, MTX_DEF); + scp->mbgntp.hasTimeData = 0; + scp->mbg_irq_op(scp, MBG_IRQ_OP_ENABLE); + + return 0; +} + +/* + * mbgntp_close: + * Handle a close request on /dev/mbgntp + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbgntp_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct mbg_softc *scp = DEV2SOFTC(dev); + + mtx_lock(&scp->mbgntp.lock); + scp->mbgntp.opened = 0; + mtx_unlock(&scp->mbgntp.lock); + + scp->mbg_irq_op(scp, MBG_IRQ_OP_DISABLE); + + return 0; +} + +/* + * mbgntp_read: + * Handle a read request on /dev/mbgntp + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbgntp_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + int ret = 0, nBytes; + struct mbg_tframe tframe; + char tstr[MBG_TSTAMP_LEN]; + struct mbg_softc *scp = DEV2SOFTC(dev); + + mtx_lock(&scp->mbgntp.lock); + if (!scp->mbgntp.opened) { + mtx_unlock(&scp->mbgntp.lock); + return EIO; + } + mtx_unlock(&scp->mbgntp.lock); + + if (uio->uio_resid < sizeof(tstr)) + return EINVAL; + + if (ioflag & O_NONBLOCK) { + if (!mtx_trylock(&scp->mbgntp.sema)) + return EAGAIN; + } else + mtx_lock(&scp->mbgntp.sema); + + while (!scp->mbgntp.hasTimeData) { + mtx_unlock(&scp->mbgntp.sema); + + if (ioflag & O_NONBLOCK) + return EAGAIN; + + ret = tsleep(&scp->mbgntp.hasTimeData, PZERO | PCATCH, "mbg", 0); + if (ret == EINTR) + return EINTR; + + mtx_lock(&scp->mbgntp.sema); + } + + mtx_lock_spin(&scp->mbgntp.spinLock); + tframe = scp->tframe; + scp->mbgntp.hasTimeData = 0; + mtx_unlock_spin(&scp->mbgntp.spinLock); + + mbg_tframe2str(&tframe, tstr); + + nBytes = min(uio->uio_resid, sizeof(tstr) - 1); + ret = uiomove(tstr, nBytes, uio); + + mtx_unlock(&scp->mbgntp.sema); + + return ret; +} + +/* + * mbgntp_write: + * Handle a write request on /dev/mbgntp + * + * Always returns EPERM + */ +static int +mbgntp_write(struct cdev *dev, struct uio *uio, int ioflag) +{ + return EPERM; +} + +/* + * mbgntp_ioctl: + * Handle an ioctl request on /dev/mbgntp + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbgntp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int flag, struct thread *td) +{ + int rc; + + switch (cmd) { + /* + * The TIOCxxx ioctl()'s are necessary because the + * NTP parse clock driver thinks the Meinberg clock + * is attached to a serial port. + */ + case TIOCGETA: + { + struct termios termios; + + termios.c_iflag = IGNBRK | IGNPAR | ISTRIP; + termios.c_cflag = CS7 | PARENB | CREAD | HUPCL | CLOCAL; + termios.c_oflag = termios.c_lflag = 0; + termios.c_ispeed = termios.c_ospeed = B9600; + bcopy(&termios, data, sizeof(termios)); + rc = MBG_RET_STATUS_OK; + } + break; + case TIOCGWINSZ: + { + struct winsize winsize = { 0, 0, 0, 0 }; + + bcopy(&winsize, data, sizeof(winsize)); + } + rc = MBG_RET_STATUS_OK; + break; + case TIOCSETA: + case TIOCFLUSH: + rc = MBG_RET_STATUS_OK; + break; + default: + rc = EINVAL; + } + + return rc; +} + +/* + * mbgntp_poll: + * Handle a poll request on /dev/mbgntp + * + * Returns number of events + */ +static int +mbgntp_poll(struct cdev *dev, int poll_events, struct thread *td) +{ + int revents = 0; + struct mbg_softc *scp = DEV2SOFTC(dev); + + if (scp->mbgntp.hasTimeData) + revents |= poll_events & (POLLIN | POLLRDNORM); + else + selrecord(td, &scp->mbgntp.select); + + return revents; +} + +/*****************************************************************************/ +/* mbgclk cdevsw functions */ +/*****************************************************************************/ + +/* + * mbgclk_open: + * Handle an open request on /dev/mbgclk + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbgclk_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct mbg_softc *scp = DEV2SOFTC(dev); + + /* + * Only 1 instance allowed + */ + mtx_lock(&scp->mbgclk.lock); + if (scp->mbgclk.opened) { + mtx_unlock(&scp->mbgclk.lock); + return EBUSY; + } + scp->mbgclk.opened++; + mtx_unlock(&scp->mbgclk.lock); + + return 0; +} + +/* + * mbgclk_close: + * Handle a close request on /dev/mbgclk + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbgclk_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct mbg_softc *scp = DEV2SOFTC(dev); + + mtx_lock(&scp->mbgclk.lock); + scp->mbgclk.opened = 0; + mtx_unlock(&scp->mbgclk.lock); + + return 0; +} + +/* + * mbgclk_access_inc: + * Increment the mbgclk accessInProcess counter + * + * Returns nothing + */ +static void +mbgclk_access_inc(struct mbgclk_struct *mbgclk) +{ + KASSERT(mbgclk->accessInProgress == 0, + ("mbgclk_access_inc: accessInProcess != 0")); + + mbgclk->accessInProgress++; +} + +/* + * mbgclk_access_dec: + * Decrement the mbgclk accessInProcess counter + * + * Returns nothing + */ +static void +mbgclk_access_dec(struct mbgclk_struct *mbgclk) +{ + KASSERT(mbgclk->accessInProgress == 1, + ("mbgclk_access_inc: accessInProcess != 1")); + + mbgclk->accessInProgress--; +} + +/* + * mbgclk_ioctl: + * Handle an ioctl request on /dev/mbgclk + * + * Returns 0 on success, non-zero otherwise + */ +static int +mbgclk_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int flag, struct thread *td) +{ + int rc = MBG_RET_STATUS_OK; + struct mbg_softc *scp = DEV2SOFTC(dev); + + switch (cmd) { + case MBG_GET_DEVICE_NAME: + { + const char *p; + + p = device_get_desc(scp->device); + rc = copyout(p, *(char **)data, strlen(p) + 1); + } + break; + case MBG_GET_FIRMWARE_ID: + { + char firmware[MBG_ID_LEN]; + + mbgclk_access_inc(&scp->mbgclk); + rc = scp->mbg_read(scp, MBG_CMD_GET_FW_ID_1, + firmware, MBG_FIFO_LEN); + if (rc == MBG_RET_STATUS_OK) { + rc = scp->mbg_read(scp, MBG_CMD_GET_FW_ID_2, + &firmware[MBG_FIFO_LEN], MBG_FIFO_LEN); + if (rc == MBG_RET_STATUS_OK) { + firmware[MBG_ID_LEN - 1] = '\0'; + rc = copyout(firmware, *(char **)data, + MBG_ID_LEN); + } else + rc = EINVAL; + } else + rc = EINVAL; + mbgclk_access_dec(&scp->mbgclk); + } + break; + case MBG_GET_TIME: + { + struct mbg_tframe tf; + + mbgclk_access_inc(&scp->mbgclk); + rc = scp->mbg_read(scp, MBG_CMD_GET_TIME, + &tf, sizeof(tf)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&tf, data, sizeof(tf)); + else + rc = EINVAL; + } + break; + case MBG_GET_HR_TIME: + { + struct mbg_hr_time ht; + + if (scp->features & MBG_FEAT_HR_TIME) { + mbgclk_access_inc(&scp->mbgclk); + rc = scp->mbg_read(scp, MBG_CMD_GET_HR_TIME, + &ht, sizeof(ht)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&ht, data, sizeof(ht)); + else + rc = EINVAL; + } else + rc = ENXIO; + } + break; + case MBG_GET_LAST_SYNC_TIME: + { + struct mbg_tframe tf; + + if (scp->features & MBG_FEAT_SYNC_TIME) { + mbgclk_access_inc(&scp->mbgclk); + rc = scp->mbg_read(scp, MBG_CMD_GET_SYNC_TIME, + &tf, sizeof(tf)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&tf, data, sizeof(tf)); + else + rc = EINVAL; + } else + rc = ENXIO; + } + break; + case MBG_GET_RECV_INFO: + { + struct mbg_gps_recv_info ri; + + if (scp->features & MBG_FEAT_RECV_INFO) { + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_read(scp, MBG_GPS_RECV_INFO, + &ri, sizeof(ri)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&ri, data, sizeof(ri)); + else + rc = EINVAL; + } else + rc = ENXIO; + } + break; + case MBG_GET_RECV_STAT: + { + struct mbg_gps_stat_info si; + + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_read(scp, MBG_GPS_STAT_INFO, + &si, sizeof(si)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&si, data, sizeof(si)); + else + rc = EINVAL; + } + break; + case MBG_GET_GPS_POSITION: + { + struct mbg_gps_position gp; + + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_read(scp, MBG_GPS_POS, + &gp, sizeof(gp)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) { + /* + * Note: Swap doubles as appropriate + */ + int i; + uint64_t u64; + + for (i = 0; i < 3; i++) { + u64 = mbg_swap64(&gp.xyz[i]); + bcopy(&u64, &gp.xyz[i], sizeof(u64)); + u64 = mbg_swap64(&gp.lla[i]); + bcopy(&u64, &gp.lla[i], sizeof(u64)); + } + u64 = mbg_swap64(&gp.longitude.sec); + bcopy(&u64, &gp.longitude.sec, sizeof(u64)); + u64 = mbg_swap64(&gp.latitude.sec); + bcopy(&u64, &gp.latitude.sec, sizeof(u64)); + bcopy(&gp, data, sizeof(gp)); + } else + rc = EINVAL; + } + break; + case MBG_GET_GPS_PORT_PARM: + { + struct mbg_gps_port_parm pp; + + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_read(scp, MBG_GPS_PORT_PARM, + &pp, sizeof(pp)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&pp, data, sizeof(pp)); + else + rc = EINVAL; + } + break; + case MBG_GET_GPS_TZDL: + { + struct mbg_gps_tzdl tz; + + if (scp->features & MBG_FEAT_TZDL) { + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_read(scp, MBG_GPS_TZDL, + &tz, sizeof(tz)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&tz, data, sizeof(tz)); + else + rc = EINVAL; + } else + rc = ENXIO; + } + break; + case MBG_GET_UCAPTURE_STATS: + { + struct mbg_ucapture_stats us; + + /* + * Note: Boards report one more max event than they + * can actually save. Adjust max accordingly. + */ + if (scp->features & MBG_FEAT_UCAP) { + mbgclk_access_inc(&scp->mbgclk); + rc = scp->mbg_read(scp, MBG_CMD_GET_CAP_STATS, + &us, sizeof(us)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) { + us.max--; + bcopy(&us, data, sizeof(us)); + } else + rc = EINVAL; + } else + rc = ENXIO; + } + break; + case MBG_GET_UCAPTURE_EVENT: + { + struct mbg_hr_time ht; + + if (scp->features & MBG_FEAT_UCAP) { + mbgclk_access_inc(&scp->mbgclk); + rc = scp->mbg_read(scp, MBG_CMD_GET_CAP_EVENT, + &ht, sizeof(ht)); + mbgclk_access_dec(&scp->mbgclk); + if (rc == MBG_RET_STATUS_OK) + bcopy(&ht, data, sizeof(ht)); + else + rc = EINVAL; + } else + rc = ENXIO; + } + break; + case MBG_SET_UCAPTURE_CLEAR: + if (scp->features & MBG_FEAT_UCAP) { + mbgclk_access_inc(&scp->mbgclk); + rc = scp->mbg_read(scp, MBG_CMD_CLEAR_CAP_BUF, + NULL, 0); + mbgclk_access_dec(&scp->mbgclk); + if (rc != MBG_RET_STATUS_OK) + rc = EINVAL; + } else + rc = ENXIO; + break; + case MBG_SET_GPS_CMD: + { + uint16_t *cmd = (uint16_t *)data; + + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_write(scp, MBG_GPS_CMD, + cmd, sizeof(*cmd)); + mbgclk_access_dec(&scp->mbgclk); + if (rc != MBG_RET_STATUS_OK) + rc = EINVAL; + } + break; + case MBG_SET_GPS_TZDL: + { + struct mbg_gps_tzdl *tz = (struct mbg_gps_tzdl *)data; + + if (scp->features & MBG_FEAT_TZDL) { + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_write(scp, MBG_GPS_TZDL, + tz, sizeof(*tz)); + mbgclk_access_dec(&scp->mbgclk); + if (rc != MBG_RET_STATUS_OK) + rc = EINVAL; + } else + rc = ENXIO; + } + break; + case MBG_SET_GPS_POS_LLA: + { + int i; + uint64_t u64; + double lla[3]; + + /* + * Note: Swap doubles as appropriate. + * Latitude and longitude are in radians. + */ + if (copyin(*(double **)data, &lla, sizeof(lla))) + rc = EFAULT; + else { + for (i = 0; i < 3; i++) { + u64 = mbg_swap64(&lla[i]); + bcopy(&u64, &lla[i], sizeof(u64)); + } + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_write(scp, MBG_GPS_POS_LLA, + lla, sizeof(lla)); + mbgclk_access_dec(&scp->mbgclk); + if (rc != MBG_RET_STATUS_OK) + rc = EINVAL; + } + } + break; + case MBG_SET_GPS_TIME: + { + struct mbg_gps_dt *dt = (struct mbg_gps_dt *)data; + + if (scp->features & MBG_FEAT_SET_TIME) { + mbgclk_access_inc(&scp->mbgclk); + rc = mbg_gps_write(scp, MBG_GPS_TIME, + dt, sizeof(*dt)); + mbgclk_access_dec(&scp->mbgclk); + if (rc != MBG_RET_STATUS_OK) + rc = EINVAL; + } else + rc = ENXIO; + } + break; + default: + rc = EINVAL; + } + + return rc; +} + diff -rn -N -U 1 usr/src.old/sys/modules/mbg/Makefile usr/src/sys/modules/mbg/Makefile --- usr/src.old/sys/modules/mbg/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ usr/src/sys/modules/mbg/Makefile 2008-01-27 12:06:15.000000000 -0800 @@ -0,0 +1,10 @@ +# MBG (Meinberg GPS170) Loadable Kernel Module +# +# $FreeBSD: $ + +.PATH: ${.CURDIR}/../../dev/mbg +KMOD = mbg +SRCS = mbg.c +SRCS += device_if.h bus_if.h pci_if.h + +.include diff -rn -N -U 1 usr/src.old/sys/sys/mbgio.h usr/src/sys/sys/mbgio.h --- usr/src.old/sys/sys/mbgio.h 1969-12-31 16:00:00.000000000 -0800 +++ usr/src/sys/sys/mbgio.h 2008-01-27 12:05:51.000000000 -0800 @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2007-2008 VMware, Inc. All rights reserved. + * + * Joe Landers + * + * 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. + * 3. Neither the name of VMware, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * with specific prior written permission. + * + * 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. + */ + +#ifndef __SYS_MBGIO_H__ +#define __SYS_MBGIO_H__ + +#ifndef KERNEL +#include +#endif +#include + +/*****************************************************************************/ +/* #defines and constants */ +/*****************************************************************************/ + +/* + * Time frame status bits (status in mbg_tframe) + */ +#define MBG_STATUS_FREERUN 0x0001 /* free running on xtal */ +#define MBG_STATUS_DST_ENA 0x0002 /* DST enabled */ +#define MBG_STATUS_SYNC 0x0004 /* clock synced at least once */ +#define MBG_STATUS_DST_CHG 0x0008 /* DST announced */ +#define MBG_STATUS_UTC 0x0010 /* using UTC */ +#define MBG_STATUS_LEAP 0x0020 /* leap second announced */ +#define MBG_STATUS_IFTM 0x0040 /* current time set from host */ +#define MBG_STATUS_INVALID 0x0080 /* time is invalid */ +#define MBG_STATUS_LEAP_SEC 0x0100 /* current sec is leap sec */ +#define MBG_STATUS_ANTENNA_FAIL 0x0200 /* antenna failure */ +#define MBG_STATUS_OVERRUN 0x2000 /* event interval too short */ +#define MBG_STATUS_BUF_FULL 0x4000 /* event read too slow */ +#define MBG_STATUS_IO_BLOCKED 0x8000 /* microprocessor busy */ + +/* + * GPS time status bits (used by status field of mbg_gps_time) + */ +#define MBG_GPS_STATUS_UTC 0x0001 /* UTC correction applied */ +#define MBG_GPS_STATUS_LOCAL 0x0002 /* converted to local time */ +#define MBG_GPS_STATUS_DL_ANN 0x0004 /* upcoming DST announcement */ +#define MBG_GPS_STATUS_DL_ENB 0x0008 /* DST enabled */ +#define MBG_GPS_STATUS_LS_ANN 0x0010 /* leap second add announcement */ +#define MBG_GPS_STATUS_LS_ENB 0x0020 /* current second is leap second */ +#define MBG_GPS_STATUS_LS_ANN_NEG 0x0040 /* leap second del (set w/ LS_ANN) */ +#define MBG_GPS_STATUS_UNUSED 0x0080 /* reserved and not used */ +#define MBG_GPS_STATUS_EXT_SYNC 0x0100 /* clock sync'd externally */ +#define MBG_GPS_STATUS_HOLDOVER 0x0200 /* holdover mode from prev sync */ +#define MBG_GPS_STATUS_ANT_SHORT 0x0400 /* antenna cable short circuited */ +#define MBG_GPS_STATUS_NO_WARM 0x0800 /* OCXO has not warmed up */ +#define MBG_GPS_STATUS_ANT_DISCONN 0x1000 /* antenna currently disconnected */ +#define MBG_GPS_STATUS_SYN_FLAG 0x2000 /* TIME_SYN output is low */ +#define MBG_GPS_STATUS_NO_SYNC 0x4000 /* time sync not verified */ +#define MBG_GPS_STATUS_NO_POS 0x8000 /* position not verified, LOCK off */ + +/* + * Signal level (signal in mbg_tframe) + */ +#define MBG_SIG_BIAS 55 +#define MBG_SIG_ERR 1 +#define MBG_SIG_MIN 20 +#define MBG_SIG_MAX 68 + +/* + * GPS receiver modes (mode in mbg_gps_stat_info) + */ +#define MBG_GPS_STAT_REMOTE 0x10 +#define MBG_GPS_STAT_BOOT 0x20 + +#define MBG_GPS_STAT_TRACK (0x01) +#define MBG_GPS_STAT_AUTO_166 (0x02) +#define MBG_GPS_STAT_WARM_166 (0x03 |MBG_GPS_STAT_BOOT) +#define MBG_GPS_STAT_COLD_166 (0x04 |MBG_GPS_STAT_BOOT) +#define MBG_GPS_STAT_AUTO_BC (0x05|MBG_GPS_STAT_REMOTE) +#define MBG_GPS_STAT_WARM_BC (0x06|MBG_GPS_STAT_REMOTE|MBG_GPS_STAT_BOOT) +#define MBG_GPS_STAT_COLD_BC (0x07|MBG_GPS_STAT_REMOTE|MBG_GPS_STAT_BOOT) +#define MBG_GPS_STAT_UPDA_166 (0x08 |MBG_GPS_STAT_BOOT) +#define MBG_GPS_STAT_UPDA_BC (0x09|MBG_GPS_STAT_REMOTE|MBG_GPS_STAT_BOOT) + +/* + * On-board oscillator DAC (bias adjustment in mbg_gps_stat_info) + */ +#define MBG_OSC_DAC_RANGE 4096UL +#define MBG_OSC_DAC_BIAS (MBG_OSC_DAC_RANGE / 2) + +/* + * GPS receiver info ID strings + */ +#define MBG_GPS_ID_SLEN 16 +#define MBG_GPS_ID_SSZ (MBG_GPS_ID_SLEN + 1) + +/* + * GPS receiver EPLD string + */ +#define MBG_GPS_EPLD_SLEN 8 +#define MBG_GSP_EPLD_SSZ (MBG_GPS_EPLD_SLEN + 1) + +/* + * GPS receivers have 2 serial ports + */ +#define MBG_NPORTS 2 + +/* + * Candidate GPS features (used as a mask in struct mbg_gps_recv_info) + */ +enum MBG_GPS_FEAT { + MBG_GPS_FEAT_PPS, /* pulse per second output */ + MBG_GPS_FEAT_PPM, /* pulse per minute output */ + MBG_GPS_FEAT_SYNTH, /* programmable synthesizer output */ + MBG_GPS_FEAT_DCFMARKS, /* DCF77 compatible time mark output */ + MBG_GPS_FEAT_IRIG_TX, /* on-board IRIG output */ + MBG_GPS_FEAT_IRIG_RX, /* on-board IRIG input */ + MBG_GPS_FEAT_LAN_IP4, /* LAN IPv4 interface */ + MBG_GPS_FEAT_MULTI_REF, /* multiple input sources with priorities */ + MBG_GPS_FEAT_RCV_TIMEOUT, /* timeout after GPS reception stopped */ + MBG_GPS_FEAT_IGNORE_LOCK, /* supports "ignore lock" */ + MBG_GPS_FEAT_5_MHZ, /* output 5 MHz rather than 100 kHz */ + MBG_GPS_FEAT_XMULTI_REF, /* extended multiple input source config */ + MBG_GPS_FEAT_OPT_SETTINGS, /* supports MBG_OPT_SETTINGS */ + MBG_GPS_NFEATURES /* the number of valid features */ +}; + +/* + * Time string modes (used in mode field of mbg_gps_port_parm) + */ +enum MBG_TSMODE { + MBG_TSMODE_ON_REQ, /* on request only */ + MBG_TSMODE_PER_SEC, /* automatically, when second changes */ + MBG_TSMODE_PER_MIN, /* automatically, when minute changes */ + MBG_TSMODE_UCAP, /* automatically, with user capture */ + MBG_TSMODE_UCAP_REQ /* only if user capture requested */ +}; + +/* + * GPS commands (used by the MBG_SET_GPS_CMD ioctl) + */ +enum MBG_GPS_CMD { + MBG_GPS_CMD_BOOT = 1, /* force the clock to boot mode */ + MBG_GPS_CMD_INIT_SYS, /* clear all system variables */ + MBG_GPS_CMD_INIT_USER, /* reset all user parameters to defaults */ + MBG_GPS_CMD_INIT_DAC /* reset the on-board oscillator DAC */ +}; + +/*****************************************************************************/ +/* structs and data types */ +/*****************************************************************************/ + +#pragma pack(1) + +/* + * Standard time frame + */ +struct mbg_tframe { + uint8_t hundreds; /* hundredths of sec, 00..99 */ + uint8_t sec; /* seconds, 00..59, (or 60 if leap) */ + uint8_t min; /* minutes, 00..59 */ + uint8_t hour; /* hours, 00..23 */ + uint8_t mday; /* day of month, 01..31 */ + uint8_t wday; /* day of week, 1..7 (1 = Monday) */ + uint8_t mon; /* month, 01..12 */ + uint8_t year; /* year of century, 00..99 */ + uint8_t status; /* status bit mask */ + uint8_t signal; /* relative signal strength */ + int8_t utc_off; /* UTC offset in hours */ +}; + +/* + * High resolution time + * Note: frac may be converted via: + * (uint32_t)((int64_t)frac * 10000000UL/(int64_t)0x100000000LL) + */ +struct mbg_hr_time { + struct mbg_time_stamp { + uint32_t sec; /* seconds since 1970 (UTC) */ + uint32_t frac; /* frac of sec (0xFFFFFFFF == 0.99999...) */ + } tstamp; /* high resolution time stamp */ + int32_t utc_offs; /* UTC offset (sec) */ + uint16_t status; /* status bit mask */ + uint8_t signal; /* for time, the relative RF signal level */ + /* for a capture event, channel number */ +}; + +/* + * GPS software version + */ +struct mbg_gps_srev { + uint16_t code; /* e.g. 0x0120 means rev. 1.20 */ + char name[MBG_GPS_ID_SSZ]; /* used to identify customized versions */ + uint8_t reserved; /* pad struct size */ +}; + +/* + * GPS fixed frequency data + */ +struct mbg_gps_finfo { + uint16_t khz_val; /* base frequency [kHz] */ + int16_t range; /* optional exponent [base 10] */ +}; + +/* + * GPS receiver info + */ +struct mbg_gps_recv_info { + uint16_t model_code; /* receiver model ID */ + struct mbg_gps_srev srev; /* software revision and ID */ + char model[MBG_GPS_ID_SSZ];/* receiver model */ + char serial[MBG_GPS_ID_SSZ];/* serial number */ + char epld[MBG_GSP_EPLD_SSZ];/* EPLD image name */ + uint8_t n_channels; /* number of receiver channels */ + uint32_t ticks_per_sec; /* resolution of fractions of seconds */ + uint32_t features; /* optional features */ + struct mbg_gps_finfo finfo;/* opt non-standard fixed freq */ + uint8_t osc_type; /* type of on-board oscillator */ + uint8_t osc_flags; /* oscillator flags */ + uint8_t n_ucaps; /* number of user time capture inputs */ + uint8_t n_com_ports; /* number of on-board serial ports */ + uint8_t n_str_type; /* max num of string types supported */ + uint8_t n_prg_out; /* number of programmable pulse outputs */ + uint16_t flags; /* additional information */ +}; + +/* + * GPS receiver status + */ +struct mbg_gps_stat_info { + uint16_t mode; /* operation mode */ + uint16_t sats_used; /* sats used */ + uint16_t sats_in_view; /* sats in view */ + int16_t dac_val; /* DAC cal (fine) - needs bias adjustment */ + int16_t dac_cal; /* DAC cal (coarse) - same as above */ +}; + +/* + * Geographic longitude or latitude in degrees, minutes, seconds + * Note: Longitude East and latitude North are positive, + * longitude West and latitude South are negative. + */ +struct mbg_dms { + uint16_t prefix; /* 'N', 'E', 'S' or 'W' */ + uint16_t deg; /* [0...90 (lat) or 0...180 (lon)] */ + uint16_t min; /* [0...59] */ + double sec; /* [0...59.999] */ +}; + +/* + * GPS receiver position + * Note: The ioctl() automatically handles word swapping in doubles. + */ +struct mbg_gps_position { + double xyz[3]; /* WGS84 ECEF coordinates */ + double lla[3]; /* depends on reference ellipsoid */ + struct mbg_dms longitude; /* longitude in degrees, minutes, seconds */ + struct mbg_dms latitude; /* latitude in degrees, minutes, seconds */ + int16_t ellipsoid; /* reference ellipsoid */ +}; + +/* + * GPS receiver serial port parameters + */ +struct mbg_gps_port_parm { + struct mbg_gps_com_parm { + int32_t speed; /* baud rate (e.g. 19200) */ + char framing[4]; /* character framing (e.g. "8N1") */ + int16_t handshake; /* no handshake currently supported */ + } com[MBG_NPORTS]; /* com0 and com1 settings */ + uint8_t mode[MBG_NPORTS]; /* com0 and com1 output mode */ +}; + +/* + * User capture statistics + * Note: Boards report one more max event than they can actually + * save. The ioctl() automatically adjusts for this behavior. + * Capture events are removed from the capture FIFO when read. + * Use MBG_GET_UCAPTURE_CLEAR to bulk clear the capture FIFO. + */ +struct mbg_ucapture_stats { + uint32_t used; /* number of saved user capture events */ + uint32_t max; /* maximum number of events (adjusted) */ +}; + +/* + * GPS receiver timezone and daylight savings time parameters + */ +struct mbg_gps_tzdl { + int32_t offs; /* offset from UTC to local time [sec] */ + int32_t offs_dl; /* addn'l offset if DST enabled [sec] */ + struct mbg_gps_time { /* Local data and time from GPS time */ + int16_t year; /* 0..9999 */ + int8_t month; /* 1..12 */ + int8_t mday; /* 1..31 */ + int16_t yday; /* 1..366 */ + int8_t wday; /* 0..6 == Sun..Sat */ + int8_t hour; /* 0..23 */ + int8_t min; /* 0..59 */ + int8_t sec; /* 0..59 */ + int32_t frac; /* frac sec; scale: 1/GPS_TICKS_PER_SEC */ + int32_t utc_offs; /* local time offset from UTC */ + uint16_t status; /* flags */ + } tm_on, /* date/time when daylight saving starts */ + tm_off; /* date/time when daylight saving ends */ + char name[2][6]; /* names without and with DST enabled */ +}; + +/* + * GPS receiver date and time + */ +struct mbg_gps_dt { + int16_t channel; /* -1 = current time; 0, 1: capture 0, 1 */ + struct mbg_gps_fmt_time { + uint16_t wn; /* GPS week number */ + uint32_t sec; /* second of the GPS week */ + uint32_t tick; /* frac sec; scale: 1/GPS_TICKS_PER_SEC */ + } gps_fmt_time; + struct mbg_gps_time time; /* data and time computed from GPS time */ +}; + +#pragma pack() + +/*****************************************************************************/ +/* ioctl handling */ +/*****************************************************************************/ + +/* + * Supported ioctls + */ +#define MBG_GET_DEVICE_NAME _IO( 'M', 0x00) +#define MBG_GET_FIRMWARE_ID _IO( 'M', 0x01) +#define MBG_GET_TIME _IOR('M', 0x02, struct mbg_tframe) +#define MBG_GET_HR_TIME _IOR('M', 0x03, struct mbg_hr_time) +#define MBG_GET_LAST_SYNC_TIME _IOR('M', 0x04, struct mbg_tframe) +#define MBG_GET_RECV_INFO _IOR('M', 0x05, struct mbg_gps_recv_info) +#define MBG_GET_RECV_STAT _IOR('M', 0x06, struct mbg_gps_stat_info) +#define MBG_GET_GPS_POSITION _IOR('M', 0x07, struct mbg_gps_position) +#define MBG_GET_GPS_PORT_PARM _IOR('M', 0x08, struct mbg_gps_port_parm) +#define MBG_GET_GPS_TZDL _IOR('M', 0x09, struct mbg_gps_tzdl) +#define MBG_GET_UCAPTURE_STATS _IOR('M', 0x0a, struct mbg_ucapture_stats) +#define MBG_GET_UCAPTURE_EVENT _IOR('M', 0x0b, struct mbg_hr_time) + +#define MBG_SET_UCAPTURE_CLEAR _IO( 'M', 0x0c) +#define MBG_SET_GPS_CMD _IOW('M', 0x0d, int) +#define MBG_SET_GPS_TZDL _IOW('M', 0x0e, struct mbg_gps_tzdl) +#define MBG_SET_GPS_POS_LLA _IO( 'M', 0x1f) +#define MBG_SET_GPS_TIME _IOW('M', 0x10, struct mbg_gps_dt) + +#endif >Release-Note: >Audit-Trail: >Unformatted: