summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorhvozda <hvozda@cvs.openbsd.org>1996-04-29 13:09:12 +0000
committerhvozda <hvozda@cvs.openbsd.org>1996-04-29 13:09:12 +0000
commitfdecada6f88b495c1afc81ad3a15c0cedffa2338 (patch)
treecd585c73f87e814d831d52c48087f539fe1fed93 /usr.sbin
parent66dbdef25563581a0d393d8d919df22c43de70c7 (diff)
Pull in John Kohl's most recent (15Apr96) APM and PCMCIA work
(original PCMCIA framework by Stefan Grefen).
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/Makefile6
-rw-r--r--usr.sbin/apm/Makefile15
-rw-r--r--usr.sbin/apm/apm.8114
-rw-r--r--usr.sbin/apm/apm.c283
-rw-r--r--usr.sbin/apmd/Makefile11
-rw-r--r--usr.sbin/apmd/apm-proto.h57
-rw-r--r--usr.sbin/apmd/apmd.8175
-rw-r--r--usr.sbin/apmd/apmd.c471
-rw-r--r--usr.sbin/apmd/apmsubr.c79
-rw-r--r--usr.sbin/apmd/pathnames.h37
-rw-r--r--usr.sbin/pcmciad/Makefile20
-rw-r--r--usr.sbin/pcmciad/README.dump-progs11
-rw-r--r--usr.sbin/pcmciad/dumpcor/Makefile10
-rw-r--r--usr.sbin/pcmciad/dumpcor/dumpcor.c70
-rw-r--r--usr.sbin/pcmciad/dumpinfo/Makefile11
-rw-r--r--usr.sbin/pcmciad/dumpinfo/dumpinfo.c659
-rw-r--r--usr.sbin/pcmciad/dumpreg/Makefile10
-rw-r--r--usr.sbin/pcmciad/dumpreg/dumpreg.c143
-rw-r--r--usr.sbin/pcmciad/pathnames.h32
-rw-r--r--usr.sbin/pcmciad/pcmciad.876
-rw-r--r--usr.sbin/pcmciad/pcmciad.c459
-rw-r--r--usr.sbin/pcmciad/test.config2
22 files changed, 2748 insertions, 3 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index c85fd5d0a21..2a4138e183b 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -1,14 +1,14 @@
# from: @(#)Makefile 5.6.1.2 (Berkeley) 5/8/91
-# $Id: Makefile,v 1.8 1996/04/18 21:32:46 deraadt Exp $
+# $Id: Makefile,v 1.9 1996/04/29 13:08:37 hvozda Exp $
# not yet done: catman
-SUBDIR= ac accton arp bootpd bootpgw bootpef bootptest \
+SUBDIR= ac accton arp apm apmd bootpd bootpgw bootpef bootptest \
chown chroot config cron dev_mkdb \
diskpart edquota gettable gspa htable inetd iostat \
ipftest ipmon ipsend kgmon \
kvm_mkdb lpr map-mbone mrinfo mrouted mtrace mtree named \
- netgroup_mkdb portmap pppd pstat pwd_mkdb quot quotaon \
+ netgroup_mkdb pcmciad portmap pppd pstat pwd_mkdb quot quotaon \
rarpd rbootd rdconfig rdate repquota rmt \
rpc.bootparamd rpc.pcnfsd rwhod \
sa sendmail sliplogin slstats spray sysctl \
diff --git a/usr.sbin/apm/Makefile b/usr.sbin/apm/Makefile
new file mode 100644
index 00000000000..6ce957a7f89
--- /dev/null
+++ b/usr.sbin/apm/Makefile
@@ -0,0 +1,15 @@
+# $Id: Makefile,v 1.1 1996/04/29 13:08:39 hvozda Exp $
+
+SRCS= apm.c apmsubr.c
+#LDADD+= -lutil
+
+.PATH: ${.CURDIR}/../apmd
+
+CFLAGS+= -g -O2 -Wmissing-prototypes -Wall -I${.CURDIR}/../apmd
+PROG= apm
+MAN= apm.8
+MLINKS= apm.8 zzz.8
+LINKS= ${BINDIR}/apm ${BINDIR}/zzz
+
+.include <bsd.prog.mk>
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/usr.sbin/apm/apm.8 b/usr.sbin/apm/apm.8
new file mode 100644
index 00000000000..e3f71882893
--- /dev/null
+++ b/usr.sbin/apm/apm.8
@@ -0,0 +1,114 @@
+.\" Copyright (c) 1996 John T. Kohl
+.\" All rights reserved.
+.\"
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+.\"
+.\" $Id: apm.8,v 1.1 1996/04/29 13:08:40 hvozda Exp $
+.\"
+.Dd March 18, 1996
+.Dt APM 8
+.Os OpenBSD
+.Sh NAME
+.Nm apm
+.Nd Advanced Power Management control program
+.Sh SYNOPSIS
+.Nm zzz
+.Op Fl S
+.Op Fl z
+.Op Fl f Ar sockname
+.Br
+.Nm apm
+.Op Fl z
+.Op Fl S
+.Op Fl s
+.Op Fl l
+.Op Fl b
+.Op Fl a
+.Op Fl v
+.Op Fl f Ar sockname
+.Sh DESCRIPTION
+.Nm
+communicates with the Advanced Power Management daemon,
+.Xr apmd 8 ,
+making requests of it for current power status or to place the system
+int a suspend or stand-by state.
+With no flags,
+.Nm
+displays the current power management state in verbose form.
+.Pp
+Available command-line flags are:
+.Bl -tag -width indent -compact
+.It Fl z
+Put the system into suspend (deep sleep) mode.
+.It Fl S
+Put the system into stand-by (light sleep) mode.
+.It Fl l
+Display the estimated battery lifetime (in percent).
+.It Fl b
+Display the battery status. 0 means high, 1 means low, 2 means
+critical, 3 means charging, 4 means absent, and 255 means unknown.
+.It Fl a
+Display the external charger (A/C status). 0 means disconnected, 1
+means connected, 2 means backup power source, and 255 means unknown.
+.It Fl v
+Request more verbose description of the displayed states.
+.It Fl f Ar sockname
+Set the name of the socket via which to contact
+.Xr apmd 8
+to
+.Pa sockname .
+.El
+.Pp
+The
+.Nm zzz
+variant on this command is an alternative for suspending the system.
+With no arguments,
+.Nm
+places the system into suspend mode.
+The command line flags serve the same purpose as for the
+.Nm apm
+variant of this command.
+.Pp
+This command does not wait for positive confirmation that the requested
+mode has been entered; to do so would mean the command does not return
+until the system resumes from its sleep state.
+.Sh FILES
+.Pa /var/run/apmdev
+is the default UNIX-domain socket used for communication with
+.Xr apm 8 .
+The
+.Fl f
+flag may be used to specify an alternate socket name.
+The protection modes on this socket govern which users may access the
+APM functions.
+.Sh SEE ALSO
+.Xr apmd 8 ,
+.Xr apm 4 .
+.Sh REFERENCES
+Advanced Power Management (APM) BIOS Interface Specification (revision
+1.1), Intel Corporation and Microsoft Corporation
+.Sh HISTORY
+The
+.Nm apm
+command appeared in OpenBSD 1.1B.
diff --git a/usr.sbin/apm/apm.c b/usr.sbin/apm/apm.c
new file mode 100644
index 00000000000..12c8deffe61
--- /dev/null
+++ b/usr.sbin/apm/apm.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1996 John T. Kohl
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <machine/apmvar.h>
+#include "pathnames.h"
+#include "apm-proto.h"
+
+#define FALSE 0
+#define TRUE 1
+
+extern char *__progname;
+extern char *optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+extern int optreset;
+
+void usage(void);
+void zzusage(void);
+int do_zzz(const char *pn, enum apm_action action);
+int open_socket(const char *pn);
+int send_command(int fd,
+ struct apm_command *cmd,
+ struct apm_reply *reply);
+
+void
+usage(void)
+{
+ fprintf(stderr,"usage: %s [-v] [-z | -S] [-slba] [-f socket]\n",
+ __progname);
+ exit(1);
+}
+
+void
+zzusage(void)
+{
+ fprintf(stderr,"usage: %s [-z | -S] [-f socket]\n",
+ __progname);
+ exit(1);
+}
+
+int
+send_command(int fd,
+ struct apm_command *cmd,
+ struct apm_reply *reply)
+{
+ /* send a command to the apm daemon */
+ cmd->vno = APMD_VNO;
+
+ if (send(fd, cmd, sizeof(*cmd), 0) == sizeof(*cmd)) {
+ if (recv(fd, reply, sizeof(*reply), 0) != sizeof(*reply)) {
+ warn("invalid reply from APM daemon\n");
+ return 1;
+ }
+ } else {
+ warn("invalid send to APM daemon");
+ return 1;
+ }
+ return 0;
+}
+
+int
+do_zzz(const char *pn, enum apm_action action)
+{
+ struct apm_command command;
+ struct apm_reply reply;
+ int fd;
+
+ switch (action) {
+ case NONE:
+ case SUSPEND:
+ command.action = SUSPEND;
+ break;
+ case STANDBY:
+ command.action = STANDBY;
+ break;
+ default:
+ zzusage();
+ }
+ fd = open_socket(pn);
+
+ if (fd == -1)
+ err(1, "cannot open connection to APM daemon");
+ printf("Suspending system...\n");
+ exit(send_command(fd, &command, &reply));
+}
+
+int
+open_socket(const char *sockname)
+{
+ int sock, errr;
+ struct sockaddr_un s_un;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1)
+ err(1, "cannot create local socket");
+
+ s_un.sun_family = AF_UNIX;
+ strncpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
+ s_un.sun_len = SUN_LEN(&s_un);
+ if (connect(sock, (struct sockaddr *)&s_un, s_un.sun_len) == -1) {
+ errr = errno;
+ close(sock);
+ errno = errr;
+ return -1;
+ }
+ return sock;
+}
+
+void
+main(int argc, char *argv[])
+{
+ char *sockname = _PATH_APM_SOCKET;
+ int ch;
+ int dostatus = FALSE;
+ int doac = FALSE;
+ int dopct = FALSE;
+ int dobstate = FALSE;
+ int fd;
+ int rval;
+ int verbose = FALSE;
+ enum apm_action action = NONE;
+ struct apm_command command;
+ struct apm_reply reply;
+
+ while ((ch = getopt(argc, argv, "lbvadsSzf:")) != -1)
+ switch(ch) {
+ case 'v':
+ verbose = TRUE;
+ break;
+ case 'f':
+ sockname = optarg;
+ break;
+ case 'z':
+ if (action != NONE)
+ usage();
+ action = SUSPEND;
+ break;
+ case 'S':
+ if (action != NONE)
+ usage();
+ action = STANDBY;
+ break;
+ case 's':
+ if (action != NONE && action != GETSTATUS)
+ usage();
+ dostatus = TRUE;
+ action = GETSTATUS;
+ break;
+ case 'b':
+ if (action != NONE && action != GETSTATUS)
+ usage();
+ dobstate = TRUE;
+ action = GETSTATUS;
+ break;
+ case 'l':
+ if (action != NONE && action != GETSTATUS)
+ usage();
+ dopct = TRUE;
+ action = GETSTATUS;
+ break;
+ case 'a':
+ if (action != NONE && action != GETSTATUS)
+ usage();
+ doac = TRUE;
+ action = GETSTATUS;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (!strcmp(__progname, "zzz")) {
+ exit(do_zzz(sockname, action));
+ }
+
+ fd = open_socket(sockname);
+
+ switch (action) {
+ case NONE:
+ verbose = doac = dopct = dobstate = dostatus = TRUE;
+ action = GETSTATUS;
+ /* fallthrough */
+ case GETSTATUS:
+ if (fd == -1) {
+ /* open the device directly and get status */
+ fd = open(_PATH_APM_NORMAL, O_RDONLY);
+ if (fd == -1) {
+ err(1, "cannot contact APM daemon and cannot open " _PATH_APM_NORMAL);
+ }
+ if (ioctl(fd, APM_IOC_GETPOWER, &reply.batterystate) == 0)
+ goto printval;
+ }
+ case SUSPEND:
+ case STANDBY:
+ command.action = action;
+ break;
+ default:
+ usage();
+ }
+
+ if ((rval = send_command(fd, &command, &reply)) == 0) {
+ switch (action) {
+ case GETSTATUS:
+ printval:
+ if (verbose) {
+ if (dobstate)
+ printf("Battery charge state: %s\n",
+ battstate(reply.batterystate.battery_state));
+ if (dopct)
+ printf("Battery remaining: %d percent\n",
+ reply.batterystate.battery_life);
+ if (doac)
+ printf("A/C adapter state: %s\n", ac_state(reply.batterystate.ac_state));
+ if (dostatus)
+ printf("Power management enabled\n");
+ } else {
+ if (dobstate)
+ printf("%d\n", reply.batterystate.battery_state);
+ if (dopct)
+ printf("%d\n", reply.batterystate.battery_life);
+ if (doac)
+ printf("%d\n", reply.batterystate.ac_state);
+ if (dostatus)
+ printf("1\n");
+ }
+ break;
+ default:
+ break;
+ }
+ switch (reply.newstate) {
+ case SUSPEND:
+ printf("System will enter suspend mode momentarily.\n");
+ break;
+ case STANDBY:
+ printf("System will enter standby mode momentarily.\n");
+ break;
+ default:
+ break;
+ }
+ } else
+ errx(rval, "cannot get reply from APM daemon\n");
+
+ exit(0);
+}
diff --git a/usr.sbin/apmd/Makefile b/usr.sbin/apmd/Makefile
new file mode 100644
index 00000000000..7b8f69640c6
--- /dev/null
+++ b/usr.sbin/apmd/Makefile
@@ -0,0 +1,11 @@
+# $Id: Makefile,v 1.1 1996/04/29 13:08:42 hvozda Exp $
+
+SRCS= apmd.c apmsubr.c
+#LDADD+= -lutil
+
+CFLAGS+= -g -O2 -Wmissing-prototypes -Wall
+PROG= apmd
+MAN= apmd.8
+
+.include <bsd.prog.mk>
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/usr.sbin/apmd/apm-proto.h b/usr.sbin/apmd/apm-proto.h
new file mode 100644
index 00000000000..818b2f2c8d8
--- /dev/null
+++ b/usr.sbin/apmd/apm-proto.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1996 John T. Kohl
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+ *
+ */
+
+enum apm_action {
+ NONE,
+ SUSPEND,
+ STANDBY,
+ GETSTATUS
+};
+
+enum apm_state {
+ NORMAL,
+ SUSPENDING,
+ STANDING_BY
+};
+
+struct apm_command {
+ int vno;
+ enum apm_action action;
+};
+
+struct apm_reply {
+ int vno;
+ enum apm_state newstate;
+ struct apm_power_info batterystate;
+};
+
+#define APMD_VNO 1
+
+extern const char *battstate __P((int state));
+extern const char *ac_state __P((int state));
diff --git a/usr.sbin/apmd/apmd.8 b/usr.sbin/apmd/apmd.8
new file mode 100644
index 00000000000..2e77d13f87c
--- /dev/null
+++ b/usr.sbin/apmd/apmd.8
@@ -0,0 +1,175 @@
+.\" Copyright (c) 1995 John T. Kohl
+.\" All rights reserved.
+.\"
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+.\"
+.\" $Id: apmd.8,v 1.1 1996/04/29 13:08:45 hvozda Exp $
+.\"
+.Dd March 24, 1996
+.Dt APMD 8
+.Os OpenBSD
+.Sh NAME
+.Nm apmd
+.Nd Advanced Power Management monitor daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl s
+.Op Fl a
+.Op Fl q
+.Op Fl t Ar seconds
+.Op Fl S Ar sockname
+.Op Fl f Ar devname
+.Sh DESCRIPTION
+.Nm
+monitors the advanced power management (APM) pseudo-device, acting on
+signaled events and upon user requests as sent by the
+.Xr apm 8
+program.
+For suspend and standby request events delivered by the BIOS, or via
+.Xr apm 8 ,
+.Nm
+runs the appropriate configuration program (if one exists),
+syncs the buffer cache to disk and initiates the requested mode.
+When resuming after suspend or standby,
+.Nm
+runs the appropriate configuration program (if one exists).
+For power status change events,
+.Nm
+fetches the current status and reports it via
+.Xr syslog 3
+with logging facility
+.Dv LOG_DAEMON .
+.Pp
+.Nm
+announces the transition to standby mode with a single high tone on the
+speaker (using the
+.Pa /dev/speaker
+device).
+Suspends are announced with two high tones.
+.Pp
+.Nm
+periodically polls the APM driver for the current power state.
+If the battery charge level changes substantially or the external power
+status changes, the new status is logged. The polling rate defaults to
+once per 10 minutes, but may be specified using the
+.Fl t
+command-line flag.
+.Pp
+If the
+.Fl s
+flag is specified, the current battery statistics are reported via
+.Xr syslog 3
+and
+.Nm
+exits without monitoring the APM status.
+.Pp
+If the
+.Fl a
+flag is specified, any BIOS-initiated suspend or standby requests are
+ignored if the system is connected to line current and not running from
+batteries (user requests are still honored).
+.Pp
+If the
+.Fl d
+flag is specified,
+.Nm
+enters debug mode, logging to facility
+.Dv LOG_LOCAL1
+and staying in the foreground on the controlling terminal.
+.Pp
+If the
+.Fl q
+flag is specified,
+.Nm
+does not announce suspend and standby requests on the speaker.
+.Pp
+When a client requests a suspend or stand-by mode,
+.Nm
+does not wait for positive confirmation that the requested
+mode has been entered before replying to the client; to do so would mean
+the client does not get a reply until the system resumes from its sleep state.
+Rather,
+.Nm
+replies with the intended state to the client and then places the system
+in the requested mode after running the configuration script and
+flushing the buffer cache.
+.Pp
+Actions can be configured for the three transitions:
+.Cm suspend ,
+.Cm standby
+and
+.Cm resume .
+The suspend and standby actions are run prior to
+.Nm
+performing any other actions (such as disk syncs) and entering the new
+mode. The resume program is run after resuming from a stand-by or
+suspended state.
+.Sh FILES
+.Pa /etc/apm/suspend ,
+.Pa /etc/apm/standby
+and
+.Pa /etc/apm/resume
+are the files that contain the host's customized actions.
+Each file must be an executable binary or shell script suitable
+for execution by the
+.Xr execve 2
+function.
+If you wish to have the same program or script control all transitions, it
+may determine which transition is in progress by examining its
+.Va argv[0]
+which is set to one of
+.Ar suspend ,
+.Ar standby ,
+or
+.Ar resume .
+.Pp
+.Pa /var/run/apmdev
+is the default UNIX-domain socket used for communication with
+.Xr apm 8 .
+The
+.Fl S
+flag may be used to specify an alternate socket name.
+The socket is protected to mode 0660, UID 0, GID 0; this protects access
+to suspend requests to authorized users only.
+.Pp
+.Pa /dev/apmctl
+is the default device used to control the APM kernel driver.
+The
+.Fl f
+flag may be used to specify an alternate device file name.
+.Sh SEE ALSO
+.Xr apm 4 ,
+.Xr apm 8 ,
+.Xr execv 2 ,
+.Xr speaker 4 ,
+.Xr syslog 3 ,
+.Xr syslogd 8 .
+.Sh REFERENCES
+Advanced Power Management (APM) BIOS Interface Specification (revision
+1.1), Intel Corporation and Microsoft Corporation.
+.Sh HISTORY
+The
+.Nm apmd
+command appeared in OpenBSD 1.1B.
diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c
new file mode 100644
index 00000000000..d7456a68f7f
--- /dev/null
+++ b/usr.sbin/apmd/apmd.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 1995, 1996 John T. Kohl
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <machine/apmvar.h>
+#include <err.h>
+#include "pathnames.h"
+#include "apm-proto.h"
+
+#define MAX(a,b) (a > b ? a : b)
+#define TRUE 1
+#define FALSE 0
+
+const char apmdev[] = _PATH_APM_CTLDEV;
+const char sockfile[] = _PATH_APM_SOCKET;
+
+static int debug = 0;
+
+extern char *__progname;
+extern char *optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+extern int optreset;
+
+void usage (void);
+int power_status (int fd, int force, struct apm_power_info *pinfo);
+int bind_socket (const char *sn);
+enum apm_state handle_client(int sock_fd, int ctl_fd);
+void suspend(int ctl_fd);
+void stand_by(int ctl_fd);
+void resume(int ctl_fd);
+void sigexit(int signo);
+void make_noise(int howmany);
+void do_etc_file(const char *file);
+
+void
+sigexit(int signo)
+{
+ exit(1);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,"usage: %s [-d] [-t timo] [-s] [-a] [-f devfile] [-S sockfile]\n", __progname);
+ exit(1);
+}
+
+
+int
+power_status(int fd, int force, struct apm_power_info *pinfo)
+{
+ struct apm_power_info bstate;
+ static struct apm_power_info last;
+ int acon = 0;
+
+ if (ioctl(fd, APM_IOC_GETPOWER, &bstate) == 0) {
+ /* various conditions under which we report status: something changed
+ enough since last report, or asked to force a print */
+ if (bstate.ac_state == APM_AC_ON)
+ acon = 1;
+ if (force ||
+ bstate.ac_state != last.ac_state ||
+ bstate.battery_state != last.battery_state ||
+ (bstate.minutes_left && bstate.minutes_left < 15) ||
+ abs(bstate.battery_life - last.battery_life) > 20) {
+ if (bstate.minutes_left)
+ syslog(LOG_NOTICE,
+ "battery status: %s. external power status: %s. "
+ "estimated battery life %d%% (%d minutes)",
+ battstate(bstate.battery_state),
+ ac_state(bstate.ac_state), bstate.battery_life,
+ bstate.minutes_left);
+ else
+ syslog(LOG_NOTICE,
+ "battery status: %s. external power status: %s. "
+ "estimated battery life %d%%",
+ battstate(bstate.battery_state),
+ ac_state(bstate.ac_state), bstate.battery_life);
+ last = bstate;
+ }
+ if (pinfo)
+ *pinfo = bstate;
+ } else
+ syslog(LOG_ERR, "cannot fetch power status: %m");
+ return acon;
+}
+
+static char *socketname;
+
+static void sockunlink(void);
+
+static void
+sockunlink(void)
+{
+ if (socketname)
+ (void) remove(socketname);
+}
+
+int
+bind_socket(const char *sockname)
+{
+ int sock;
+ struct sockaddr_un s_un;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1)
+ err(1, "cannot create local socket");
+
+ s_un.sun_family = AF_UNIX;
+ strncpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
+ s_un.sun_len = SUN_LEN(&s_un);
+ /* remove it if present, we're moving in */
+ (void) remove(sockname);
+ if (bind(sock, (struct sockaddr *)&s_un, s_un.sun_len) == -1)
+ err(1, "cannot connect to APM socket");
+ if (chmod(sockname, 0660) == -1 || chown(sockname, 0, 0) == -1)
+ err(1, "cannot set socket mode/owner/group to 666/0/0");
+ listen(sock, 1);
+ socketname = strdup(sockname);
+ atexit(sockunlink);
+ return sock;
+}
+
+enum apm_state
+handle_client(int sock_fd, int ctl_fd)
+{
+ /* accept a handle from the client, process it, then clean up */
+ int cli_fd;
+ struct sockaddr_un from;
+ int fromlen;
+ struct apm_command cmd;
+ struct apm_reply reply;
+
+ cli_fd = accept(sock_fd, (struct sockaddr *)&from, &fromlen);
+ if (cli_fd == -1) {
+ syslog(LOG_INFO, "client accept failure: %m");
+ return NORMAL;
+ }
+ if (recv(cli_fd, &cmd, sizeof(cmd), 0) != sizeof(cmd)) {
+ (void) close(cli_fd);
+ syslog(LOG_INFO, "client size botch");
+ return NORMAL;
+ }
+ if (cmd.vno != APMD_VNO) {
+ close(cli_fd); /* terminate client */
+ /* no error message, just drop it. */
+ return NORMAL;
+ }
+ power_status(ctl_fd, 0, &reply.batterystate);
+ switch (cmd.action) {
+ default:
+ reply.newstate = NORMAL;
+ break;
+ case SUSPEND:
+ reply.newstate = SUSPENDING;
+ break;
+ case STANDBY:
+ reply.newstate = STANDING_BY;
+ break;
+ }
+ reply.vno = APMD_VNO;
+ if (send(cli_fd, &reply, sizeof(reply), 0) != sizeof(reply)) {
+ syslog(LOG_INFO, "client reply botch");
+ }
+ close(cli_fd);
+ return reply.newstate;
+}
+
+static int speaker_ok = TRUE;
+
+void
+make_noise(howmany)
+int howmany;
+{
+ int spkrfd;
+ int trycnt;
+
+ if (!speaker_ok) /* don't bother after sticky errors */
+ return;
+
+ for (trycnt = 0; trycnt < 3; trycnt++) {
+ spkrfd = open(_PATH_DEV_SPEAKER, O_WRONLY);
+ if (spkrfd == -1) {
+ switch (errno) {
+ case EBUSY:
+ usleep(500000);
+ errno = EBUSY;
+ continue;
+ case ENOENT:
+ case ENODEV:
+ case ENXIO:
+ case EPERM:
+ case EACCES:
+ syslog(LOG_INFO,
+ "speaker device " _PATH_DEV_SPEAKER " unavailable: %m");
+ speaker_ok = FALSE;
+ return;
+ }
+ } else
+ break;
+ }
+ if (spkrfd == -1) {
+ syslog(LOG_WARNING, "cannot open " _PATH_DEV_SPEAKER ": %m");
+ return;
+ }
+ syslog(LOG_DEBUG, "sending %d tones to speaker\n", howmany);
+ write (spkrfd, "o4cc", 2 + howmany);
+ close(spkrfd);
+ return;
+}
+
+
+void
+suspend(int ctl_fd)
+{
+ do_etc_file(_PATH_APM_ETC_SUSPEND);
+ sync();
+ make_noise(2);
+ sync();
+ sync();
+ sleep(1);
+ ioctl(ctl_fd, APM_IOC_SUSPEND, 0);
+}
+
+void
+stand_by(int ctl_fd)
+{
+ do_etc_file(_PATH_APM_ETC_STANDBY);
+ sync();
+ make_noise(1);
+ sync();
+ sync();
+ sleep(1);
+ ioctl(ctl_fd, APM_IOC_STANDBY, 0);
+}
+
+#define TIMO (10*60) /* 10 minutes */
+
+void
+resume(int ctl_fd)
+{
+ do_etc_file(_PATH_APM_ETC_RESUME);
+}
+
+void
+main(int argc, char *argv[])
+{
+ const char *fname = apmdev;
+ int ctl_fd, sock_fd, ch, ready;
+ int statonly = 0;
+ fd_set devfds;
+ fd_set selcopy;
+ struct apm_event_info apmevent;
+ int suspends, standbys, resumes;
+ int noacsleep = 0;
+ struct timeval tv = {TIMO, 0}, stv;
+ const char *sockname = sockfile;
+
+ while ((ch = getopt(argc, argv, "qadsf:t:S:")) != -1)
+ switch(ch) {
+ case 'q':
+ speaker_ok = FALSE;
+ break;
+ case 'a':
+ noacsleep = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ fname = optarg;
+ break;
+ case 'S':
+ sockname = optarg;
+ break;
+ case 't':
+ tv.tv_sec = strtoul(optarg, 0, 0);
+ if (tv.tv_sec == 0)
+ usage();
+ break;
+ case 's': /* status only */
+ statonly = 1;
+ break;
+ case '?':
+
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if ((ctl_fd = open(fname, O_RDWR)) == -1) {
+ (void)err(1, "cannot open device file `%s'", fname);
+ }
+ if (debug) {
+ openlog(__progname, LOG_CONS, LOG_LOCAL1);
+ } else {
+ openlog(__progname, LOG_CONS, LOG_DAEMON);
+ setlogmask(LOG_UPTO(LOG_NOTICE));
+ daemon(0, 0);
+ }
+ power_status(ctl_fd, 1, 0);
+ if (statonly)
+ exit(0);
+ (void) signal(SIGTERM, sigexit);
+ (void) signal(SIGHUP, sigexit);
+ (void) signal(SIGINT, sigexit);
+
+ sock_fd = bind_socket(sockname);
+
+ FD_ZERO(&devfds);
+ FD_SET(ctl_fd, &devfds);
+ FD_SET(sock_fd, &devfds);
+
+ for (selcopy = devfds, errno = 0, stv = tv;
+ (ready = select(MAX(ctl_fd,sock_fd)+1, &selcopy, 0, 0, &stv)) >= 0 ||
+ errno == EINTR;
+ selcopy = devfds, errno = 0, stv = tv) {
+ if (errno == EINTR)
+ continue;
+ if (ready == 0) {
+ /* wakeup for timeout: take status */
+ power_status(ctl_fd, 0, 0);
+ }
+ if (FD_ISSET(ctl_fd, &selcopy)) {
+ suspends = standbys = resumes = 0;
+ while (ioctl(ctl_fd, APM_IOC_NEXTEVENT, &apmevent) == 0) {
+ syslog(LOG_DEBUG, "apmevent %04x index %d", apmevent.type,
+ apmevent.index);
+ switch (apmevent.type) {
+ case APM_SUSPEND_REQ:
+ case APM_USER_SUSPEND_REQ:
+ case APM_CRIT_SUSPEND_REQ:
+ case APM_BATTERY_LOW:
+ suspends++;
+ break;
+ case APM_USER_STANDBY_REQ:
+ case APM_STANDBY_REQ:
+ standbys++;
+ break;
+#if 0
+ case APM_CANCEL:
+ suspends = standbys = 0;
+ break;
+#endif
+ case APM_NORMAL_RESUME:
+ case APM_CRIT_RESUME:
+ case APM_SYS_STANDBY_RESUME:
+ resumes++;
+ break;
+ case APM_POWER_CHANGE:
+ power_status(ctl_fd, 1, 0);
+ break;
+ default:
+ break;
+ }
+ }
+ if ((standbys || suspends) && noacsleep &&
+ power_status(ctl_fd, 0, 0)) {
+ syslog(LOG_DEBUG, "not sleeping cuz AC is connected");
+ } else if (suspends) {
+ suspend(ctl_fd);
+ } else if (standbys) {
+ stand_by(ctl_fd);
+ } else if (resumes) {
+ resume(ctl_fd);
+ syslog(LOG_NOTICE, "system resumed from APM sleep");
+ }
+ ready--;
+ }
+ if (ready == 0)
+ continue;
+ if (FD_ISSET(sock_fd, &selcopy)) {
+ switch (handle_client(sock_fd, ctl_fd)) {
+ case NORMAL:
+ break;
+ case SUSPENDING:
+ suspend(ctl_fd);
+ break;
+ case STANDING_BY:
+ stand_by(ctl_fd);
+ break;
+ }
+ }
+ }
+ syslog(LOG_ERR, "select failed: %m");
+ exit(1);
+}
+
+void
+do_etc_file(const char *file)
+{
+ pid_t pid;
+ int status;
+ const char *prog;
+
+ /* If file doesn't exist, do nothing. */
+ if (access(file, X_OK|R_OK)) {
+ syslog(LOG_DEBUG, "do_etc_file(): cannot access file %s", file);
+ return;
+ }
+
+ prog = strrchr(file, '/');
+ if (prog)
+ prog++;
+ else
+ prog = file;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ syslog(LOG_ERR, "failed to fork(): %m");
+ return;
+ case 0:
+ /* We are the child. */
+ execl(file, prog, NULL);
+ _exit(-1);
+ /* NOTREACHED */
+ default:
+ /* We are the parent. */
+ wait4(pid, &status, 0, 0);
+ if (WIFEXITED(status))
+ syslog(LOG_DEBUG, "%s exited with status %d", file,
+ WEXITSTATUS(status));
+ else {
+ syslog(LOG_ERR, "%s exited abnormally.", file);
+ }
+ break;
+ }
+}
diff --git a/usr.sbin/apmd/apmsubr.c b/usr.sbin/apmd/apmsubr.c
new file mode 100644
index 00000000000..014361ec20c
--- /dev/null
+++ b/usr.sbin/apmd/apmsubr.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1995,1996 John T. Kohl
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <machine/apmvar.h>
+#include <err.h>
+#include "apm-proto.h"
+
+const char *
+battstate(int state)
+{
+ switch (state) {
+ case APM_BATT_HIGH:
+ return "high";
+ case APM_BATT_LOW:
+ return "low";
+ case APM_BATT_CRITICAL:
+ return "CRITICAL";
+ case APM_BATT_CHARGING:
+ return "charging";
+ case APM_BATTERY_ABSENT:
+ return "absent";
+ case APM_BATT_UNKNOWN:
+ return "unknown (absent?)";
+ default:
+ return "invalid battery state";
+ }
+}
+
+const char *
+ac_state(int state)
+{
+ switch (state) {
+ case APM_AC_OFF:
+ return "not connected";
+ case APM_AC_ON:
+ return "connected";
+ case APM_AC_BACKUP:
+ return "backup power source";
+ case APM_AC_UNKNOWN:
+ return "not known";
+ default:
+ return "invalid AC status";
+ }
+}
diff --git a/usr.sbin/apmd/pathnames.h b/usr.sbin/apmd/pathnames.h
new file mode 100644
index 00000000000..393f19f9151
--- /dev/null
+++ b/usr.sbin/apmd/pathnames.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1996 John T. Kohl
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+ *
+ */
+
+#define _PATH_APM_SOCKET "/var/run/apmdev"
+#define _PATH_APM_CTLDEV "/dev/apmctl"
+#define _PATH_APM_ETC_DIR "/etc/apm"
+#define _PATH_APM_ETC_SUSPEND _PATH_APM_ETC_DIR"/suspend"
+#define _PATH_APM_ETC_STANDBY _PATH_APM_ETC_DIR"/standby"
+#define _PATH_APM_ETC_RESUME _PATH_APM_ETC_DIR"/resume"
+#define _PATH_APM_NORMAL "/dev/apm"
+#define _PATH_DEV_SPEAKER "/dev/speaker"
diff --git a/usr.sbin/pcmciad/Makefile b/usr.sbin/pcmciad/Makefile
new file mode 100644
index 00000000000..99a28031105
--- /dev/null
+++ b/usr.sbin/pcmciad/Makefile
@@ -0,0 +1,20 @@
+# $Id: Makefile,v 1.1 1996/04/29 13:08:51 hvozda Exp $
+
+SRCS= pcmciad.c pcmcia_conf.c
+#LIBS= -lutil
+VPATH= ${.CURDIR}/../../sys/dev/pcmcia
+
+CFLAGS+= -g -Wmissing-prototypes -I${.CURDIR}/../../sys
+PROG= pcmciad
+MAN= pcmciad.8
+
+pcmcia_conf.o: pcmcia_conf.c
+ $(CC) $(CFLAGS) -D_KERNEL -c $<
+clean::
+ rm -f a.out [Ee]rrs mklog core *.core pcmciad pcmcia_conf.o pcmciad.o
+
+
+SUBDIR=dumpcor dumpinfo dumpreg
+.include <bsd.subdir.mk>
+.include <bsd.prog.mk>
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/usr.sbin/pcmciad/README.dump-progs b/usr.sbin/pcmciad/README.dump-progs
new file mode 100644
index 00000000000..99aa9f75845
--- /dev/null
+++ b/usr.sbin/pcmciad/README.dump-progs
@@ -0,0 +1,11 @@
+The progams here are ports of Barry Jaspan's stuff.
+
+From the original README:
+
+Copyright 1993 Barry Jaspan. Everything contained in this release
+(with the exception of some #defines in pcmcia.h) is original work.
+Permission is granted to distribute this system under the terms of the
+GNU General Public License.
+
+Barry Jaspan, bjaspan@gza.com
+
diff --git a/usr.sbin/pcmciad/dumpcor/Makefile b/usr.sbin/pcmciad/dumpcor/Makefile
new file mode 100644
index 00000000000..58b43551a75
--- /dev/null
+++ b/usr.sbin/pcmciad/dumpcor/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.1 1996/04/29 13:09:02 hvozda Exp $
+
+SRCS = dumpcor.c
+
+CFLAGS += -g -I/sys
+PROG=dumpcor
+NOMAN=
+
+.include <bsd.prog.mk>
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/usr.sbin/pcmciad/dumpcor/dumpcor.c b/usr.sbin/pcmciad/dumpcor/dumpcor.c
new file mode 100644
index 00000000000..662e86cac31
--- /dev/null
+++ b/usr.sbin/pcmciad/dumpcor/dumpcor.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include <dev/ic/i82365reg.h>
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmcia_ioctl.h>
+
+
+struct reg_t {
+ int addr;
+ char *name;
+ int width;
+};
+
+struct reg_t cor_regs[] = {
+ {PCMCIA_COR , "Configuration and Option Register", 1,},
+ {PCMCIA_CCSR, "Card Configuration and Status Register", 1,},
+ {PCMCIA_PIR, "Pin Replacement Register", 1,},
+ {PCMCIA_SCR,"Socket and Copy Register", 1,},
+ {0, NULL, 0,}
+};
+
+struct pcmcia_info data;
+
+main(int argc, char **argv)
+{
+ int i, j, idx;
+ int fd;
+ char nmbuf[80];
+
+ if (argc < 2)
+ exit(1);
+ idx=atoi(argv[1]);
+
+ sprintf(nmbuf,"/dev/pcmcia/slot%d",idx);
+
+ if((fd=open(nmbuf,O_RDWR))<0) {
+ perror("open");
+ exit(1);
+ }
+
+ if(ioctl(fd,PCMCIAIO_READ_COR,&data)<0) {
+ perror("ioctl");
+ exit(1);
+ }
+
+ dump_cor_regs(&data.cis_data);
+ printf("\n");
+ return 0;
+}
+
+dump_cor_regs(unsigned char *data)
+{
+ int i;
+
+ for(;;) {
+ unsigned int idx=*data++;
+ unsigned int val=*data++;
+ if(idx==0xff)
+ return;
+ if(idx<4)
+ printf("%s: 0x%x\n", cor_regs[idx].name,val);
+ else
+ printf("unkown 0x%x: %x\n", idx,val);
+ }
+}
diff --git a/usr.sbin/pcmciad/dumpinfo/Makefile b/usr.sbin/pcmciad/dumpinfo/Makefile
new file mode 100644
index 00000000000..51a82e34885
--- /dev/null
+++ b/usr.sbin/pcmciad/dumpinfo/Makefile
@@ -0,0 +1,11 @@
+# $Id: Makefile,v 1.1 1996/04/29 13:09:05 hvozda Exp $
+
+SRCS = dumpinfo.c
+#dumpreg.c
+
+CFLAGS += -g
+PROG=dumpinfo
+NOMAN=
+
+.include <bsd.prog.mk>
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/usr.sbin/pcmciad/dumpinfo/dumpinfo.c b/usr.sbin/pcmciad/dumpinfo/dumpinfo.c
new file mode 100644
index 00000000000..79c2070e489
--- /dev/null
+++ b/usr.sbin/pcmciad/dumpinfo/dumpinfo.c
@@ -0,0 +1,659 @@
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmcia_ioctl.h>
+
+void parse_device(u_char *, int, int);
+void parse_ver1(u_char *, int, int);
+void parse_config(u_char *, int, int);
+void parse_cfent_dumpinfo(u_char *, int, int);
+void read_extended_speed(u_char *, int *);
+const char *tuple_name(int), *dtype_name(int), *dsize_name(int),
+ *dspeed_name(int);
+void parse_tuples(u_char *buf, int print_tuples);
+dbuf(u_char *buf,int len,int verb) {
+ int i;
+ for(i=0;i<len;i++) {
+ if(i%8==0)
+ printf("%04x : ",i);
+ printf(" %02x(%c)",buf[i],buf[i]<' '||buf[i]>126?'.':buf[i]);
+ if(i%8==7)
+ printf("\n");
+ }
+ if(i%16!=0)
+ printf("\n");
+}
+
+main(int argc,char **argv) {
+ int fd;
+ char namebuf[64];
+ int sockid=-1;
+ int verb=0;
+ struct pcmcia_status stbuf;
+ struct pcmcia_info inbuf;
+ char *file;
+
+ if(argc<2) {
+ exit(1);
+ }
+
+ if(*(argv[1])<'0' || *(argv[1])>'9') {
+ file=argv[1];
+ } else {
+ sockid=atoi(argv[1]);
+ }
+ if(argc>2) {
+ verb=atoi(argv[2]);
+ }
+
+ if(sockid>=0) {
+ sprintf(namebuf,"/dev/pcmcia/slot%d",sockid);
+
+ if((fd=open(namebuf,O_RDWR))<0) {
+ printf("errno %d\n",errno);
+ perror("open");
+ exit(1);
+ }
+ printf("open ok\n",stbuf.slot,stbuf.status);
+ if(ioctl(fd,PCMCIAIO_GET_STATUS,&stbuf)<0) {
+ printf("errno %d\n",errno);
+ perror("ioctl PCMCIAIO_GET_STATUS");
+ exit(1);
+ }
+ printf("Status slot %d %x\n",stbuf.slot,stbuf.status);
+ if(!(stbuf.status&PCMCIA_CARD_PRESENT)) {
+ printf("No card in slot %d\n",stbuf.slot);
+ exit(1);
+ }
+ if(!(stbuf.status&PCMCIA_POWER)) {
+ int pw=PCMCIA_POWER_5V;
+ printf("Card in slot %d no power\n",stbuf.slot);
+ if(ioctl(fd,PCMCIAIO_SET_POWER,&pw)<0) {
+ printf("errno %d\n",errno);
+ perror("ioctl PCMCIAIO_SET_POWER");
+ exit(1);
+ }
+ /*exit(1);/**/
+ }
+ if(!(stbuf.status&PCMCIA_READY)) {
+ printf("Card in slot %d not ready\n",stbuf.slot);
+ /*exit(1);/**/
+ }
+ if(ioctl(fd,PCMCIAIO_GET_INFO,&inbuf)<0) {
+ printf("errno %d\n",errno);
+ perror("ioctl PCMCIAIO_GET_INFO");
+ exit(1);
+ }
+ } else if(file) {
+ fd=open(file,O_RDONLY);
+ if(fd==-1) {
+ perror("Can't open file");
+ exit(1);
+ }
+ if(read(fd,inbuf.cis_data,512)==-1) {
+ perror("Can't read file");
+ exit(1);
+ }
+ }
+ if(verb)
+ dbuf(inbuf.cis_data,512,verb);/**/
+ parse_tuples(inbuf.cis_data,1);
+ exit(0);
+
+}
+
+void parse_tuples(u_char *buf, int print_tuples)
+{
+ u_char code, len,*tbuf;
+ int done;
+
+ done = 0;
+ while (!done) {
+ code=*buf++;
+ if (code == CIS_NULL) {
+ if (print_tuples)
+ printf("NULL tuple\n");
+ continue;
+ }
+
+ len=*buf++;
+ if(code!=CIS_END)
+ if (print_tuples)
+ printf("%s (%d):\n", tuple_name(code), len);
+
+
+ tbuf=buf;
+ buf+=len;
+ switch (code) {
+ case CIS_NULL:
+ if (print_tuples)
+ printf("NULL\n");
+ break;
+ case CIS_END:
+ done = 1;
+ break;
+ case CIS_DEVICE:
+ case CIS_DEVICE_A:
+ if (!print_tuples)
+ break;
+ parse_device(tbuf, len, print_tuples);
+ break;
+ case CIS_VER1:
+ parse_ver1(tbuf, len, print_tuples);
+ break;
+ case CIS_CFG_INFO:
+ parse_config(tbuf, len, print_tuples);
+ break;
+ case CIS_CFG_ENT:
+ parse_cfent_dumpinfo(tbuf, len, print_tuples);
+ break;
+ default:
+ if (print_tuples)
+ printf("\tskpping\n");
+ break;
+ }
+ }
+}
+
+void parse_device(u_char *tbuf, int tlen, int print_tuples)
+{
+ int i, idx, addr_units;
+
+ i = 0;
+ while (i < tlen) {
+ /* last info structure? */
+ if (tbuf[i] == 0xff) {
+ break;
+ }
+
+ /* device id */
+ idx = (tbuf[i] & CIS_DEVICE_TYPE) >> CIS_DEVICE_TYPE_SHIFT;
+ printf("\tType %s, ", dtype_name(idx));
+ printf("WPS %s, ", tbuf[i] & CIS_DEVICE_WPS ? "set" : "clear");
+ idx = tbuf[i] & CIS_DEVICE_SPEED;
+ printf("Speed %s, ", dspeed_name(idx));
+
+ /* device size */
+ i++;
+ if (tbuf[i] != 0xff) {
+ addr_units = ((tbuf[i] & CIS_DEVICE_ADDRS) >>
+ CIS_DEVICE_ADDRS_SHIFT) + 1;
+ idx = tbuf[i] & CIS_DEVICE_SIZE;
+ printf("Size %s * %d", dsize_name(idx), addr_units);
+ } else {
+ printf("IGNORED");
+ /* ignore this device info entry */
+ }
+
+ printf("\n");
+
+ i++;
+ }
+}
+
+void parse_ver1(u_char *tbuf, int len, int print_tuples)
+{
+ int i;
+ char manufacturer[33],prod_name[33],addl_info1[33],addl_info2[33];
+
+ i = 0;
+ if (tbuf[i++] != 0x04) {
+ if (print_tuples)
+ fprintf(stderr, "Major version != 0x04\n");
+ return;
+ }
+ if (tbuf[i++] != 0x01) {
+ if (print_tuples)
+ fprintf(stderr, "Minor version != 0x01\n");
+ return;
+ }
+ strncpy(manufacturer, &tbuf[i], sizeof(manufacturer)-1);
+ i += strlen(manufacturer) + 1;
+ strncpy(prod_name, &tbuf[i], sizeof(prod_name)-1);
+ i += strlen(&tbuf[i]) + 1;
+ if(tbuf[i]==0xff)
+ addl_info1[0]=0;
+ else {
+ strncpy(addl_info1, &tbuf[i], sizeof(addl_info1)-1);
+ i += strlen(&tbuf[i]) + 1;
+ }
+ if(tbuf[i]==0xff)
+ addl_info2[0]=0;
+ else {
+ strncpy(addl_info2, &tbuf[i], sizeof(addl_info2)-1);
+ i += strlen(&tbuf[i]) + 1;
+ }
+ if (print_tuples) {
+ printf("\tManufacturer: %s\n", manufacturer);
+ printf("\tProduct name: %s\n", prod_name);
+ printf("\tAddl info 1: %s\n", addl_info1);
+ printf("\tAddl info 2: %s\n", addl_info2);
+ }
+ if (tbuf[i] != 0xff) {
+ fprintf(stderr, "Tuple not ended by 0xff!\n");
+ return;
+ }
+}
+
+void parse_config(u_char *tbuf, int len, int print_tuples)
+{
+ int i, rasz, rmsz,config_midx,base_addr,regmask[4];
+
+ i = 0;
+ rasz = (tbuf[i] & TPCC_RASZ) >> TPCC_RASZ_SHIFT;
+ rmsz = (tbuf[i] & TPCC_RMSZ) >> TPCC_RMSZ_SHIFT;
+ if (print_tuples)
+ printf("\tRASZ %d, RMSZ %d, ", rasz+1, rmsz+1);
+
+ i++;
+ config_midx = (tbuf[i] & TPCC_LAST) >> TPCC_LAST_SHIFT;
+ if (print_tuples)
+ printf("last idx %d, ", config_midx);
+
+ i++;
+ base_addr = 0;
+ switch (rasz) {
+ case 3:
+ base_addr |= (tbuf[i+3] << 24);
+ case 2:
+ base_addr |= (tbuf[i+2] << 16);
+ case 1:
+ base_addr |= (tbuf[i+1] << 8);
+ case 0:
+ base_addr |= tbuf[i];
+ }
+ if (print_tuples)
+ printf("base addr 0x%08x\n", base_addr);
+
+ i += rasz + 1;
+ regmask[0] = regmask[1] = 0;
+ regmask[2] = regmask[3] = 0;
+ switch (rmsz) {
+ case 15:
+ regmask[3] |= (tbuf[i+15] << 24);
+ case 14:
+ regmask[3] |= (tbuf[i+14] << 16);
+ case 13:
+ regmask[3] |= (tbuf[i+13] << 8);
+ case 12:
+ regmask[3] |= tbuf[i+12];
+ case 11:
+ regmask[2] |= (tbuf[i+11] << 24);
+ case 10:
+ regmask[2] |= (tbuf[i+10] << 16);
+ case 9:
+ regmask[2] |= (tbuf[i+9] << 8);
+ case 8:
+ regmask[2] |= tbuf[i+8];
+ case 7:
+ regmask[1] |= (tbuf[i+7] << 24);
+ case 6:
+ regmask[1] |= (tbuf[i+6] << 16);
+ case 5:
+ regmask[1] |= (tbuf[i+5] << 8);
+ case 4:
+ regmask[1] |= tbuf[i+4];
+ case 3:
+ regmask[0] |= (tbuf[i+3] << 24);
+ case 2:
+ regmask[0] |= (tbuf[i+2] << 16);
+ case 1:
+ regmask[0] |= (tbuf[i+1] << 8);
+ case 0:
+ regmask[0] |= tbuf[i+0];
+ break;
+ }
+ if (print_tuples)
+ printf("\treg mask 0x%04x%04x%04x%04x, ",
+ regmask[3], regmask[2],
+ regmask[1], regmask[0]);
+
+ i += rmsz + 1;
+ if (print_tuples)
+ printf("\n\t%d bytes in subtuples\n", len - i);
+}
+
+void parse_cfent_dumpinfo(u_char *tbuf, int len, int print_tuples)
+{
+ int i, j, k, intface, ftrsm,idx,defp,iop,io_16,ios,ftrs;
+ int pwr_desc, wait_scale, rdy_scale, rsv_scale;
+ int io_block_len, io_block_size;
+ int host_addr_p, addr_size, len_size,elen;
+
+
+ i = 0;
+ intface = (tbuf[i] & TPCE_INDX_INT);
+ idx = (tbuf[i] & TPCE_INDX_ENTRY);
+ defp = (tbuf[i] & TPCE_INDX_DEF);
+ if (print_tuples)
+ printf("\tEntry %d, %sdefault, %sinterface\n", idx,
+ defp ? "" : "not ", intface ? "" : "no ");
+ if (intface) {
+ i++;
+ if (print_tuples)
+ printf("\ttype %d, BVD %d, WP %d, RdyBsy %d, "
+ "wait sig %d\n",
+ tbuf[i] & TPCE_IF_TYPE,
+ !!tbuf[i] & TPCE_IF_BVD,
+ !!tbuf[i] & TPCE_IF_WP,
+ !!tbuf[i] & TPCE_IF_RDYBSY,
+ !!tbuf[i] & TPCE_IF_MWAIT);
+ }
+ i++;
+
+ ftrs = tbuf[i++];
+ if (print_tuples)
+ printf("\tfeatures 0x%02x (%x)\n", ftrs,ftrs&TPCE_FS_PWR);
+
+ /* XXX skip all power description structures */
+ for (j = 0; j < (ftrs & TPCE_FS_PWR); j++) {
+ pwr_desc = tbuf[i++];
+ printf("PWR %x\n",pwr_desc);
+ /* for each struct, skip all parameter defns */
+ for (k = 0; k < 8; pwr_desc >>= 1, k++) {
+ if (pwr_desc & 0x01) {
+ printf("%d: ",k);
+ /* skip bytes until non-ext found */
+ printf("%x ",tbuf[i]);
+ while (tbuf[i++] & 0x80)
+ printf("%x ",tbuf[i]);
+ ;/**/
+ printf("\n");
+ }
+ }
+ }
+
+ if (ftrs & TPCE_FS_TD) {
+ wait_scale = tbuf[i] & TPCE_FS_TD_WAIT;
+ rdy_scale = (tbuf[i] & TPCE_FS_TD_RDY) >>
+ TPCE_FS_TD_RDY_SHIFT;
+ rsv_scale = (tbuf[i] & TPCE_FS_TD_RSV) >>
+ TPCE_FS_TD_RSV_SHIFT;
+ i++;
+ if (wait_scale != 3) {
+ read_extended_speed(tbuf, &i);
+ if (print_tuples)
+ printf("\twait scale %d\n", wait_scale);
+ }
+ if (rdy_scale != 7) {
+ read_extended_speed(tbuf, &i);
+ if (print_tuples)
+ printf("\tReady/Busy scale %d\n", rdy_scale);
+ }
+ if (rsv_scale != 7) {
+ read_extended_speed(tbuf, &i);
+ if (print_tuples)
+ printf("\tReserved scale %d\n", rsv_scale);
+ }
+ }
+
+ if (ftrs & TPCE_FS_IO) {
+ int io_addrs[16],io_lens[16];
+ int iptr,ilen,ranges;
+ iop = 1;
+ io_16 = tbuf[i] & TPCE_FS_IO_BUS16;
+ if (print_tuples)
+ printf("\tIO lines %x, bus8 %d, bus16 %d, range %d\n",
+ (tbuf[i] & TPCE_FS_IO_LINES),
+ !!(tbuf[i] & TPCE_FS_IO_BUS8),
+ !!io_16,
+ ranges=!!(tbuf[i] & TPCE_FS_IO_RANGE));
+ i++;
+ if(ranges) {
+ io_block_len = (tbuf[i] & TPCE_FS_IO_LEN) >>
+ TPCE_FS_IO_LEN_SHIFT;
+ io_block_size = (tbuf[i] & TPCE_FS_IO_SIZE) >>
+ TPCE_FS_IO_SIZE_SHIFT;
+ ios = (tbuf[i] & TPCE_FS_IO_NUM) + 1;
+ elen=io_block_len+(io_block_len==3?1:0)+
+ io_block_size+(io_block_size==3?1:0);
+ i++;
+ if((ftrs & TPCE_FS_IRQ)!=0) {
+ iptr=(ios*elen)+i;
+ if((tbuf[iptr]&(TPCE_FS_IRQ_PULSE|TPCE_FS_IRQ_LEVEL))==0) {
+ if(((tbuf[iptr-elen]) &(TPCE_FS_IRQ_PULSE|TPCE_FS_IRQ_LEVEL))!=0) {
+ iptr-=elen;
+ }
+ }
+ if((tbuf[iptr]&TPCE_FS_IRQ_MASK)!=0) {
+ ilen=2;
+ } else {
+ ilen=1;
+ }
+ } else {
+ ilen=0;
+ }
+ if((i+(ios*elen)+ilen)>len) {
+ if (print_tuples)
+ printf("Warning: CIS range info doesn't fit in entry!"
+ " Reducing # of ranges by 1\n");
+ printf("%d %d %d %d %d\n",i,ios,ilen,ios*elen,len);
+ ios--;
+ }
+ if (print_tuples)
+ printf("\t# ranges %d, length size %d, "
+ "addr size %d\n", ios,
+ io_block_len, io_block_size);
+ for (j = 0; j < ios; j++) {
+ io_addrs[j] = io_lens[j] = 0;
+ switch (io_block_size) {
+ case 3:
+ io_addrs[j] |= tbuf[i+3] << 24;
+ io_addrs[j] |= tbuf[i+2] << 16;
+ case 2:
+ io_addrs[j] |= tbuf[i+1] << 8;
+ case 1:
+ io_addrs[j] |= tbuf[i];
+ break;
+ }
+ i += io_block_size + (io_block_size == 3 ? 1
+ : 0);
+ switch (io_block_len) {
+ case 3:
+ io_lens[j] |= tbuf[i+3] << 24;
+ io_lens[j] |= tbuf[i+2] << 16;
+ case 2:
+ io_lens[j] |= tbuf[i+1] << 8;
+ case 1:
+ io_lens[j] |= tbuf[i];
+ break;
+ }
+ io_lens[j]++;
+ i += io_block_len + (io_block_len == 3 ? 1
+ : 0);
+
+ if (print_tuples)
+ if(io_lens[j]&1)
+ printf("\taddr %08x, len %d (Assuming incorect CIS entry"
+ " CIS value == %d)\n",
+ io_addrs[j],
+ io_lens[j]-1,
+ io_lens[j]);
+ else
+ printf("\taddr %08x, len %d\n",
+ io_addrs[j],
+ io_lens[j]);
+ }
+ }
+ }
+
+ if (ftrs & TPCE_FS_IRQ) {
+ int irq_mask,irqp,irq;
+ irqp = 1;
+ if (print_tuples)
+ printf("\tIRQ: share %d, pulse %d, level %d, ",
+ !!(tbuf[i] & TPCE_FS_IRQ_SHARE),
+ !!(tbuf[i] & TPCE_FS_IRQ_PULSE),
+ !!(tbuf[i] & TPCE_FS_IRQ_LEVEL));
+ if (tbuf[i] & TPCE_FS_IRQ_MASK) {
+ irq_mask = (tbuf[i+2] << 8) + tbuf[i+1];
+ if (print_tuples)
+ printf("VEND %d, BERR %d, IOCK %d, NMI %d\n"
+ "\t mask 0x%04x\n",
+ !!(tbuf[i] & TPCE_FS_IRQ_VEND),
+ !!(tbuf[i] & TPCE_FS_IRQ_BERR),
+ !!(tbuf[i] & TPCE_FS_IRQ_IOCK),
+ !!(tbuf[i] & TPCE_FS_IRQ_NMI),
+ irq_mask);
+ i += 2;
+ } else {
+ irq = tbuf[i] & TPCE_FS_IRQ_IRQN;
+ if (print_tuples)
+ printf("IRQ %d\n", irq);
+ }
+
+ i++;
+ }
+
+ if (ftrs & TPCE_FS_MEM) {
+ int memp,mems,mem_lens[16],mem_caddrs[16],mem_haddrs[16];
+ memp = 1;
+ switch ((ftrs & TPCE_FS_MEM) >> TPCE_FS_MEM_SHIFT) {
+ case 1:
+ mems = 1;
+ mem_lens[0] = (tbuf[i+1] << 8) + tbuf[i];
+ mem_lens[0] <<= 8;
+ printf("\tmem: len %d\n", mem_lens[0]);
+
+ break;
+ case 2:
+ mems = 1;
+ mem_lens[0] = (tbuf[i+1] << 8) + tbuf[i];
+ mem_caddrs[0] = mem_haddrs[0] =
+ (tbuf[i+3] << 8) + tbuf[i+2];
+
+ mem_lens[0] <<= 8;
+ mem_caddrs[0] <<= 8;
+
+ if (print_tuples)
+ printf("\tmem: len %d, addr %d\n",
+ mem_lens[0],
+ mem_caddrs[0]);
+ break;
+ case 3:
+ host_addr_p = tbuf[i] & TPCE_FS_MEM_HOST;
+ addr_size = (tbuf[i] & TPCE_FS_MEM_ADDR) >>
+ TPCE_FS_MEM_ADDR_SHIFT;
+ len_size = (tbuf[i] & TPCE_FS_MEM_LEN) >>
+ TPCE_FS_MEM_LEN_SHIFT;
+ mems = (tbuf[i] & TPCE_FS_MEM_WINS) + 1;
+ if (print_tuples)
+ printf("\tmem (%x): host %d, addr size %d, len "
+ "size %d, # wins %d\n", tbuf[i],
+ !!host_addr_p, addr_size,
+ len_size, mems);
+ i++;
+ for (j = 0; j < mems; j++) {
+ mem_lens[j] = 0;
+ mem_caddrs[j] = 0;
+ mem_haddrs[j] = 0;
+ switch (len_size) {
+ case 3:
+ mem_lens[j] |= (tbuf[i+2] << 16);
+ case 2:
+ mem_lens[j] |= (tbuf[i+1] << 8);
+ case 1:
+ mem_lens[j] |= tbuf[i];
+ }
+ i += len_size;
+ switch (addr_size) {
+ case 3:
+ mem_caddrs[j] |= (tbuf[i+2] << 16);
+ case 2:
+ mem_caddrs[j] |= (tbuf[i+1] << 8);
+ case 1:
+ mem_caddrs[j] |= tbuf[i];
+ }
+ i += addr_size;
+ if (host_addr_p) {
+ switch (addr_size) {
+ case 3:
+ mem_haddrs[j] |=
+ (tbuf[i+2] << 16);
+ case 2:
+ mem_haddrs[j] |=
+ (tbuf[i+1] << 8);
+ case 1:
+ mem_haddrs[j] |=
+ tbuf[i];
+ }
+ i += addr_size;
+ }
+
+ mem_lens[j] <<= 8;
+ mem_caddrs[j] <<= 8;
+ mem_haddrs[j] <<= 8;
+
+ if (print_tuples)
+ printf("\t\twin %d: len %d, card addr "
+ "%x, host addr %x\n", j,
+ mem_lens[j],
+ mem_caddrs[j],
+ mem_haddrs[j]);
+ }
+ }
+ }
+}
+
+void read_extended_speed(u_char *tbuf, int *i)
+{
+ *i += 1;
+ /* fprintf(stderr, "\tXXX read_extended_speed not implemented!\n"); */
+}
+
+const char *tuple_name(int code)
+{
+#define MAX_TUPLE_NAME (sizeof(tuple_names) / sizeof(char *))
+ static const char *tuple_names[] = {
+ "NULL", "DEVICE",
+ "reserved", "reserved", "reserved", "reserved",
+ "reserved", "reserved", "reserved", "reserved",
+ "reserved", "reserved", "reserved", "reserved",
+ "reserved", "reserved",
+ "CHECKSUM", "LONGLINK_A", "LONGLINK_C", "LINKTARGET",
+ "NO_LINK", "VERS_1", "ALTSTR", "DEVICE_A",
+ "JEDEC_C", "JEDEC_A", "CONFIG", "CFTABLE_ENTRY",
+ "DEVICE_OC", "DEVICE_OA",
+ };
+ if(code==CIS_END)
+ return "END";
+
+ return (code < MAX_TUPLE_NAME) ? tuple_names[code] : "UNKNOWN";
+}
+
+const char *dtype_name(int idx)
+{
+#define MAX_DTYPE_NAME (sizeof(dtype_names) / sizeof(char *))
+ static const char *dtype_names[] = {
+ "NULL", "ROM", "OTPROM", "EPROM",
+ "EEPROM", "FLASH", "SRAM", "DRAM",
+ };
+
+ return (idx < MAX_DTYPE_NAME) ? dtype_names[idx] : "INVALID";
+}
+
+const char *dspeed_name(int idx)
+{
+#define MAX_DSPEED_NAME (sizeof(dspeed_names) / sizeof(char *))
+ static const char *dspeed_names[] = {
+ "NULL", "250NS", "200NS", "150NS",
+ "100NS", "reserved", "reserved", "extended",
+ };
+
+ return (idx < MAX_DSPEED_NAME) ? dspeed_names[idx] : "INVALID";
+}
+
+const char *dsize_name(int idx)
+{
+#define MAX_DSIZE_NAME (sizeof(dsize_names) / sizeof(char *))
+ static const char *dsize_names[] = {
+ "512b", "2k", "8k", "32k", "128k", "512k", "2M", "reserved",
+ };
+ return (idx < MAX_DSIZE_NAME) ? dsize_names[idx] : "INVALID";
+}
diff --git a/usr.sbin/pcmciad/dumpreg/Makefile b/usr.sbin/pcmciad/dumpreg/Makefile
new file mode 100644
index 00000000000..e57d843e9ab
--- /dev/null
+++ b/usr.sbin/pcmciad/dumpreg/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.1 1996/04/29 13:09:09 hvozda Exp $
+
+SRCS = dumpreg.c
+
+CFLAGS += -g
+PROG=dumpreg
+NOMAN=
+
+.include <bsd.prog.mk>
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/usr.sbin/pcmciad/dumpreg/dumpreg.c b/usr.sbin/pcmciad/dumpreg/dumpreg.c
new file mode 100644
index 00000000000..de1a0a7ebbe
--- /dev/null
+++ b/usr.sbin/pcmciad/dumpreg/dumpreg.c
@@ -0,0 +1,143 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include <dev/ic/i82365reg.h>
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmcia_ioctl.h>
+
+
+struct reg_t {
+ char addr;
+ char *name;
+ int width;
+};
+
+struct reg_t pcic_regs[] = {
+ PCIC_ID_REV, "Identification and Revision", 1,
+ PCIC_STATUS, "Interface Status", 1,
+ PCIC_POWER, "Power and RESETDRV control", 1,
+ PCIC_INT_GEN, "Interrupt and General Control", 1,
+ PCIC_STAT_CHG, "Card Status Change", 1,
+ PCIC_STAT_INT, "Card Status Change Interrupt Config", 1,
+ PCIC_ADDRWINE, "Address Window Enable", 1,
+ PCIC_IOCTL, "I/O Control", 1,
+ PCIC_IO0_STL, "I/O Address 0 Start", 2,
+ PCIC_IO0_SPL, "I/O Address 0 Stop", 2,
+ PCIC_IO1_STL, "I/O Address 1 Start", 2,
+ PCIC_IO1_SPL, "I/O Address 1 Stop", 2,
+ PCIC_SM0_STL, "System Memory Address 0 Mapping Start", 2,
+ PCIC_SM0_SPL, "System Memory Address 0 Mapping Stop", 2,
+ PCIC_CM0_L, "Card Memory Offset Address 0", 2,
+ PCIC_SM1_STL, "System Memory Address 1 Mapping Start", 2,
+ PCIC_SM1_SPL, "System Memory Address 1 Mapping Stop", 2,
+ PCIC_CM1_L, "Card Memory Offset Address 1", 2,
+ PCIC_SM2_STL, "System Memory Address 2 Mapping Start", 2,
+ PCIC_SM2_SPL, "System Memory Address 2 Mapping Stop", 2,
+ PCIC_CM2_L, "Card Memory Offset Address 2", 2,
+ PCIC_SM3_STL, "System Memory Address 3 Mapping Start", 2,
+ PCIC_SM3_SPL, "System Memory Address 3 Mapping Stop", 2,
+ PCIC_CM3_L, "Card Memory Offset Address 3", 2,
+ PCIC_SM4_STL, "System Memory Address 4 Mapping Start", 2,
+ PCIC_SM4_SPL, "System Memory Address 4 Mapping Stop", 2,
+ PCIC_CM4_L, "Card Memory Offset Address 4", 2,
+ 0, NULL, 0,
+};
+
+#if 0
+struct reg_t pcmcia_regs[] = {
+ PCMCIA_COR, "Configuration Option Register", 1,
+ PCMCIA_CCSR, "Card Configuration Status Register", 1,
+ PCMCIA_PIR, "Pin Replacement Register", 1,
+ PCMCIA_SCR, "Socket and Copy Register", 1,
+ 0, NULL, 0,
+};
+#endif
+
+struct pcmcia_regs data;
+struct pcic_regs *pcic = (struct pcic_regs *)&data.chip_data[0];
+
+main(int argc, char **argv)
+{
+ int i, j, idx;
+ int fd;
+ char nmbuf[80];
+
+ if (argc < 2)
+ exit(1);
+
+ idx=atoi(argv[1]);
+
+ for(i=0;i<128;i++) {
+ pcic->reg[i].addr=i;
+ }
+ pcic->cnt=128;
+
+ sprintf(nmbuf,"/dev/pcmcia/chip%d",idx/2);
+
+ if((fd=open(nmbuf,O_RDWR))<0) {
+ perror("open");
+ exit(1);
+ }
+
+ if(ioctl(fd,PCMCIAIO_READ_REGS,&data)<0) {
+ perror("ioctl");
+ exit(1);
+ }
+
+
+
+ dump_pcic_regs((idx&1)?0x40:0);
+ printf("\n");
+ /*if (argc == 2)
+ dump_pcmcia_regs(&pcic_socks[i]);/**/
+
+ return 0;
+}
+
+dump_pcic_regs(int off)
+{
+ int i;
+
+ for (i = 0; pcic_regs[i].name; i++) {
+ printf("%s: ", pcic_regs[i].name);
+ switch (pcic_regs[i].width) {
+ case 1:
+ printf("%#x", pcic->reg[pcic_regs[i].addr+off].val);
+ break;
+ case 2:
+ printf("%#x", (pcic->reg[pcic_regs[i].addr+off+1].val<<8)|pcic->reg[pcic_regs[i].addr+off].val);
+ break;
+ }
+ printf("\n");
+ }
+}
+#if 0
+dump_pcmcia_regs(struct pcic_sock *sock)
+{
+ int i;
+ u_char v;
+
+ pcic_map_attr(sock, PHYS_ADDR, 1);
+ for (i = 0; pcmcia_regs[i].name; i++) {
+ printf("%s: ", pcmcia_regs[i].name);
+
+ if (lseek(mem_fd, PHYS_ADDR + sock->base_addr +
+ pcmcia_regs[i].addr, SEEK_SET) < 0) {
+ perror("seeking to tuple memory");
+ exit(1);
+ }
+ if (read(mem_fd, &v, 1) < 0) {
+ perror("reading tuple memory");
+ exit(1);
+ }
+
+ printf("%#x\n", v);
+ }
+
+ pcic_map_attr(sock, PHYS_ADDR, 0);
+}
+#endif
diff --git a/usr.sbin/pcmciad/pathnames.h b/usr.sbin/pcmciad/pathnames.h
new file mode 100644
index 00000000000..f28474271e9
--- /dev/null
+++ b/usr.sbin/pcmciad/pathnames.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1996 John T. Kohl
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+ *
+ */
+
+#define _PATH_DEV_SPEAKER "/dev/speaker"
+#define _PATH_PCMCIA_CONF "/etc/pcmciad.conf"
+
diff --git a/usr.sbin/pcmciad/pcmciad.8 b/usr.sbin/pcmciad/pcmciad.8
new file mode 100644
index 00000000000..439e01459af
--- /dev/null
+++ b/usr.sbin/pcmciad/pcmciad.8
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1995 John T. Kohl
+.\" All rights reserved.
+.\"
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 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.
+.\"
+.\" $Id: pcmciad.8,v 1.1 1996/04/29 13:08:56 hvozda Exp $
+.\"
+.Dd October 29, 1995
+.Dt PCMCIAD 8
+.Os OpenBSD
+.Sh NAME
+.Nm pcmciad
+.Nd PC-CARD slot monitor daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl q
+.Op Fl c Ar conf-file
+.Sh DESCRIPTION
+.Nm
+monitors a set of PCMCIA slots for card change events, and acts on those
+events.
+It reads the configuration file specified with the
+.Fl c
+flag, or, if no file is specified, the default configuration file
+.Pa /etc/pcmciad.conf .
+.Pp
+Each line of the configuration file contains a list of PCMCIA slot
+device files to monitor and a command name for that slot.
+Whenever a card event occurs on a monitored slot, pcmciad attempts to
+configure (upon insertion) or unconfigure (upon removal) the card. If
+the action succeeds, the slot's command is run, passed the slot number,
+event type (insert or delete), and device type name.
+.Pp
+.Nm
+announces card insertion/configuration/removal events on the speaker
+(using the
+.Pa /dev/speaker
+device). A short high note announces a card insertion. A low note
+followed by a high note indicates successful card configuration; a
+single low note indicates an unknown card or configuration failure. A
+high note followed by a low note indicates a card removal.
+.Pp
+When the
+.Fl q
+flag is specified, the card insertion/removal events are not announced
+on the speaker.
+.Sh SEE ALSO
+.Xr pcmcia_cntrl 8 ,
+.Xr config_slot 8 ,
+.Xr speaker 4 .
+.Sh HISTORY
+The
+.Nm pcmciad
+command appeared in OpenBSD 1.1B.
diff --git a/usr.sbin/pcmciad/pcmciad.c b/usr.sbin/pcmciad/pcmciad.c
new file mode 100644
index 00000000000..b744928ca5b
--- /dev/null
+++ b/usr.sbin/pcmciad/pcmciad.c
@@ -0,0 +1,459 @@
+/* $NetBSD$ */
+/*
+ * Copyright (c) 1995 John T. Kohl. All rights reserved.
+ * Copyright (c) 1993, 1994 Stefan Grefen. All rights reserved.
+ *
+ * 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 dipclaimer.
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Stefan Grefen.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmcia_ioctl.h>
+#include "pathnames.h"
+
+#define PCMCIABUS_UNIT(a) (minor(a))
+#define PCMCIABUS_SLOT(a) (a&0x7)
+
+#define HAS_POWER(a) ISSET(a,PCMCIA_POWER)
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~(f)
+#define ISSET(t, f) ((t) & (f))
+
+extern const char *__progname;
+
+extern char *optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+extern int optreset;
+
+const char conffile[] = _PATH_PCMCIA_CONF;
+
+enum speaker_tones {
+ SINGLE_HIGH,
+ SHORT_HIGH,
+ LOW_HIGH,
+ HIGH_LOW,
+ SINGLE_LOW
+};
+
+
+void make_noise __P((enum speaker_tones));
+void child_death __P((int));
+void usage __P((void));
+void handle_fd __P((int fd));
+
+extern int read_conf(u_char *buf,
+ int blen,
+ int cfidx,
+ struct pcmcia_conf *pc_cf);
+
+void
+usage(void)
+{
+ fprintf(stderr,"usage: %s [-d] [-c configfile]\n", __progname);
+ exit(1);
+}
+
+dev_t devices_opened[64]; /* XXX fixed size */
+int slot_status[64]; /* XXX fixed size */
+
+int speaker_ok = 1;
+
+void /* XXX */
+main(int argc,
+ char *argv[])
+{
+ int fd, ch, maxfd = 0, ready;
+ int debug = 0;
+ FILE *infile = NULL;
+ const char *fname = conffile;
+ char confline[128];
+ fd_set sockets;
+ fd_set selcopy;
+ struct stat statb;
+
+ while ((ch = getopt(argc, argv, "qdc:")) != -1)
+ switch(ch) {
+ case 'q':
+ speaker_ok = 0;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'c':
+ fname = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((infile = fopen(fname, "r")) == NULL) {
+ (void)err(1, "cannot open config file `%s'", fname);
+ }
+ if (debug) {
+ openlog(__progname, LOG_CONS, LOG_LOCAL1);
+ } else {
+ openlog(__progname, LOG_CONS, LOG_DAEMON);
+ setlogmask(LOG_UPTO(LOG_NOTICE));
+ daemon(0, 0);
+ }
+ syslog(LOG_DEBUG, "opened config file %s", fname);
+ FD_ZERO(&sockets);
+ while (fgets(confline, sizeof(confline), infile) != NULL) {
+ if (confline[strlen(confline)-1] == '\n')
+ confline[strlen(confline)-1] = '\0';
+ fd = open(confline, O_RDWR);
+ if (fd != -1) {
+ struct pcmcia_status stbuf;
+ if (ioctl(fd, PCMCIAIO_GET_STATUS, &stbuf) < 0) {
+ syslog(LOG_ERR,"ioctl PCMCIAIO_GET_STATUS %s: %m",
+ confline);
+ close(fd);
+ } else {
+ FD_SET(fd, &sockets);
+ syslog(LOG_DEBUG, "%s is fd %d\n", confline, fd);
+ if (fstat(fd, &statb) == -1) {
+ syslog(LOG_ERR, "cannot fstat %s: %m",
+ confline);
+ (void) close(fd);
+ } else {
+ maxfd = MAX(fd, maxfd);
+ devices_opened[fd] = statb.st_rdev;
+ }
+ slot_status[fd] = ISSET(stbuf.status,
+ PCMCIA_CARD_PRESENT);
+ if (ISSET(stbuf.status, PCMCIA_CARD_INUSE))
+ make_noise(LOW_HIGH);
+ else if (ISSET(stbuf.status,
+ PCMCIA_CARD_IS_MAPPED))
+ make_noise(SINGLE_LOW);
+ else if (ISSET(stbuf.status,
+ PCMCIA_CARD_PRESENT))
+ handle_fd(fd);
+
+ }
+ } else
+ syslog(LOG_DEBUG, "%s: %m", confline);
+ }
+ fclose(infile);
+
+ if (maxfd == 0) {
+ syslog(LOG_ERR, "no files to monitor");
+ exit(1);
+ }
+ syslog(LOG_DEBUG, "maxfd = %d\n", maxfd);
+
+ signal(SIGCHLD, child_death);
+ while (1) {
+ for (selcopy = sockets;
+ (ready = select(maxfd+1, 0, 0, &selcopy, 0)) > 0;
+ selcopy = sockets) {
+ register int i;
+ syslog(LOG_DEBUG, "%d ready descriptors\n", ready);
+ for (i = 0; ready && i <= maxfd; i++) {
+ if (FD_ISSET(i, &selcopy)) {
+ syslog(LOG_DEBUG, "fd %d is exceptionally ready\n", i);
+ /* sleep to let it settle */
+ sleep(2);
+ ready--;
+ handle_fd(i);
+ }
+ }
+ }
+ if (ready == -1) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "select failed: %m");
+ continue;
+ } else {
+ syslog(LOG_ERR, "leaving with ready == 0?");
+ break;
+ }
+ }
+}
+
+void
+handle_fd(int fd)
+{
+ struct pcmcia_status stbuf;
+ struct pcmcia_info inbuf;
+ struct pcmcia_conf pc_cf;
+ int status;
+ int pw;
+ int first=1;
+ char manu[MAX_CIS_NAMELEN];
+ char model[MAX_CIS_NAMELEN];
+ char addinf1[MAX_CIS_NAMELEN];
+ char addinf2[MAX_CIS_NAMELEN];
+ char cmd[64];
+
+ if (ioctl(fd, PCMCIAIO_GET_STATUS, &stbuf) < 0) {
+ syslog(LOG_ERR,"ioctl PCMCIAIO_GET_STATUS: %m");
+ return;
+ }
+ status = ISSET(stbuf.status, PCMCIA_CARD_PRESENT);
+ if (!status) {
+ syslog(LOG_INFO,"No card in slot %d\n",stbuf.slot);
+ if (ISSET(stbuf.status, PCMCIA_CARD_INUSE) ||
+ ISSET(stbuf.status, PCMCIA_CARD_IS_MAPPED)) {
+ if (ioctl(fd,
+ ISSET(stbuf.status, PCMCIA_CARD_INUSE) ?
+ PCMCIAIO_UNCONFIGURE : PCMCIAIO_UNMAP, 0) < 0)
+ syslog(LOG_ERR,
+ "ioctl PCMCIAIO_UNCONFIGURE slot %d: %m",
+ stbuf.slot);
+ else {
+ if (status != slot_status[fd]) {
+ make_noise(HIGH_LOW);
+ }
+ }
+ slot_status[fd] = status;
+ if (ioctl(fd, PCMCIAIO_GET_STATUS, &stbuf) < 0) {
+ syslog(LOG_ERR, "ioctl PCMCIAIO_GET_STATUS: %m");
+ make_noise(SINGLE_LOW);
+ return;
+ }
+ } else {
+ syslog(LOG_DEBUG,"Card in slot %d is not mapped\n",
+ stbuf.slot);
+ if (status != slot_status[fd]) {
+ make_noise(HIGH_LOW);
+ }
+ slot_status[fd] = status;
+ return;
+ }
+ if (ISSET(stbuf.status, PCMCIA_POWER)) {
+ pw = PCMCIASIO_POWER_OFF;
+ if (ioctl(fd, PCMCIAIO_SET_POWER, &pw) < 0)
+ syslog(LOG_ERR,"ioctl PCMCIAIO_SET_POWER slot %d: %m",
+ stbuf.slot);
+ }
+#if 0
+ sprintf(cmd, "/sbin/pcmcia_cntrl -f %d %d unmapforce", fd,
+ PCMCIABUS_SLOT(PCMCIABUS_UNIT(devices_opened[fd])));
+#endif
+ return;
+ } else {
+ if (status != slot_status[fd])
+ make_noise(SHORT_HIGH);
+ if (ISSET(stbuf.status, PCMCIA_CARD_INUSE)) {
+ syslog(LOG_INFO,
+ "Card in slot %d is attached, can't probe it",
+ stbuf.slot);
+ /* make_noise(SINGLE_LOW); */
+ return;
+ }
+ /* unmap the card to clean up. */
+ if (ISSET(stbuf.status, PCMCIA_CARD_IS_MAPPED) &&
+ ioctl(fd, PCMCIAIO_UNMAP, 0) == -1) {
+ syslog(LOG_NOTICE,
+ "cannot unmap card in slot %d: %m", stbuf.slot);
+ make_noise(SINGLE_LOW);
+ return;
+ }
+
+tryagain:
+ pw = PCMCIASIO_POWER_OFF;
+ if (ioctl(fd, PCMCIAIO_SET_POWER, &pw) == -1) {
+ syslog(LOG_ERR,"ioctl PCMCIAIO_SET_POWER slot %d: %m",
+ stbuf.slot);
+ make_noise(SINGLE_LOW);
+ return;
+ }
+ pw = PCMCIASIO_POWER_AUTO;
+ if (ioctl(fd, PCMCIAIO_SET_POWER, &pw) == -1) {
+ syslog(LOG_ERR,"ioctl PCMCIAIO_SET_POWER slot %d: %m",
+ stbuf.slot);
+ make_noise(SINGLE_LOW);
+ return;
+ }
+ if (ioctl(fd, PCMCIAIO_GET_INFO, &inbuf) < 0)
+ syslog(LOG_ERR, "ioctl PCMCIAIO_GETINFO: %m");
+ else {
+ syslog(LOG_DEBUG, "config: %s", inbuf.cis_data);
+ }
+ memset(&pc_cf, 0, sizeof(pc_cf));
+ if (pcmcia_get_cf(0, inbuf.cis_data, 512, CFGENTRYMASK,
+ &pc_cf)) {
+ syslog(LOG_ERR, "can't interpret config info");
+ } else {
+ if (pcmcia_get_cisver1(0, (u_char *)&inbuf.cis_data,
+ 512, manu, model, addinf1,
+ addinf2) == 0) {
+ syslog(LOG_INFO,"<%s, %s, %s, %s>",
+ manu, model, addinf1, addinf2);
+ } else {
+ syslog(LOG_ERR, "can't get CIS info\n");
+ if (first) {
+ first = 0;
+ goto tryagain;
+ }
+ }
+ }
+#if 0
+ sprintf(cmd, "/sbin/pcmcia_cntrl -f %d %d probe", fd,
+ PCMCIABUS_SLOT(PCMCIABUS_UNIT(devices_opened[fd])));
+#endif
+#if 0 || 0
+ if (ioctl(fd, PCMCIAIO_CONFIGURE, &pc_cf) == -1) {
+ syslog(LOG_ERR, "ioctl PCMCIAIO_CONFIGURE: %m");
+ return;
+ }
+#endif
+ memset(&pc_cf, 0, sizeof(pc_cf));
+ if (ioctl(fd, PCMCIAIO_CONFIGURE, &pc_cf) == -1) {
+ syslog(LOG_ERR, "ioctl PCMCIAIO_CONFIGURE: %m");
+ make_noise(SINGLE_LOW);
+ } else
+ if (status != slot_status[fd])
+ make_noise(LOW_HIGH);
+ slot_status[fd] = status;
+ return;
+ }
+#if 0
+ syslog(LOG_DEBUG, "execing `%s'", cmd);
+ status = system(cmd);
+ if (status == -1)
+ syslog(LOG_ERR, "cannot run %s", cmd);
+ else if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ syslog(LOG_ERR,
+ "%s returned %d\n", cmd, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ syslog(LOG_ERR,
+ "%s died from signal %d", cmd, WTERMSIG(status));
+ }
+ return;
+#endif
+}
+
+/*
+ * Insert/remove tones. We do the same tones as Wildboar (BSD/OS kit):
+ *
+ * single high note: suspend/resume (look at apmd instead)
+ * short high note: card insertion noticed
+ * low then high note: successful attach
+ * high then low note: card eject noticed
+ * single low note: unknown card or attach failure
+ *
+ * we do the sound via /dev/speaker.
+ */
+
+const char *tone_string[] = {
+/* SINGLE_HIGH*/"o4c",
+/* SHORT_HIGH,*/"t240o4c",
+/* LOW_HIGH,*/ "t180o2co4c",
+/* HIGH_LOW,*/ "t180o4co2c",
+/* SINGLE_LOW*/ "t180o2c"
+};
+
+void
+make_noise(tones)
+ enum speaker_tones tones;
+{
+ pid_t pid;
+ int spkrfd;
+ int trycnt;
+
+ if (!speaker_ok) /* don't bother after sticky errors */
+ return;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ syslog(LOG_ERR, "cannot fork for speaker tones: %m");
+ return;
+ case 0:
+ /* child */
+ for (trycnt = 0; trycnt < 3; trycnt++) {
+ spkrfd = open(_PATH_DEV_SPEAKER, O_WRONLY);
+ if (spkrfd == -1) {
+ switch (errno) {
+ case EBUSY:
+ usleep(1000000);
+ errno = EBUSY;
+ continue;
+ case ENOENT:
+ case ENODEV:
+ case ENXIO:
+ case EPERM:
+ case EACCES:
+ syslog(LOG_INFO,
+ "speaker device " _PATH_DEV_SPEAKER " unavailable: %m");
+ exit(2);
+ break;
+ }
+ } else
+ break;
+ }
+ if (spkrfd == -1) {
+ syslog(LOG_WARNING,
+ "cannot open " _PATH_DEV_SPEAKER ": %m");
+ exit(1);
+ }
+ syslog(LOG_DEBUG,
+ "sending %s to speaker\n", tone_string[tones]);
+ write (spkrfd, tone_string[tones], strlen(tone_string[tones]));
+ exit(0);
+ default:
+ /* parent */
+ return;
+ }
+}
+
+void
+child_death(sig)
+ int sig;
+{
+ int status;
+ if (wait(&status) == -1) {
+ syslog(LOG_ERR, "wait error for signaled child: %m");
+ return;
+ }
+ if (WEXITSTATUS(status) == 2)
+ speaker_ok = 0;
+}
diff --git a/usr.sbin/pcmciad/test.config b/usr.sbin/pcmciad/test.config
new file mode 100644
index 00000000000..4d78866480e
--- /dev/null
+++ b/usr.sbin/pcmciad/test.config
@@ -0,0 +1,2 @@
+/dev/pcmcia/slot0
+/dev/pcmcia/slot1