diff options
author | hvozda <hvozda@cvs.openbsd.org> | 1996-04-29 14:17:54 +0000 |
---|---|---|
committer | hvozda <hvozda@cvs.openbsd.org> | 1996-04-29 14:17:54 +0000 |
commit | 7a9ddc83f934914d39af72bf24b67290a9e5700f (patch) | |
tree | d4e40de8eec73b77be31c346455984224213ff31 /sys | |
parent | fdecada6f88b495c1afc81ad3a15c0cedffa2338 (diff) |
Pull in John Kohl's [jtk@netbsd.org] most recent (15Apr96) APM and PCMCIA work
(original PCMCIA framework by Stefan Grefen [grefen@convex.com]).
Diffstat (limited to 'sys')
42 files changed, 4808 insertions, 922 deletions
diff --git a/sys/arch/i386/apm_init/Makefile b/sys/arch/i386/apm_init/Makefile new file mode 100644 index 00000000000..44e521eba13 --- /dev/null +++ b/sys/arch/i386/apm_init/Makefile @@ -0,0 +1,56 @@ +# +# LP (Laptop Package) +# +# (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> +# +# This software may be used, modified, copied, and distributed in +# both source and binary form provided that the above copyright and +# these terms are retained. Under no circumstances is the author +# responsible for the proper functioning of this software, nor does +# the author assume any responsibility for damages incurred with its +# use. +# +# Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) +# Oct., 1994 NetBSD port (1.0 BETA 10/2) by ukai +# Dec., 1995 NetBSD 1.1 kernel build retrofit, jtk@netbsd.org +# + +DIR=${APMREL}${APMDIR} + +.if exists (${DIR}/arch/${MACHINE_ARCH}/Makefile.inc) +.PATH: ${DIR}/arch/${MACHINE_ARCH} +.include "${DIR}/arch/${MACHINE_ARCH}/Makefile.inc" +.endif + +.PATH: ${DIR} + +CC = ${APMCC} +CFLAGS += -DINITIALIZER -I${DIR} -DKERNEL \ + ${APMCFLAGS:S@-I.@-I${KERNREL}.@g} + +OBJS = apm_init.o real_prot.o table.o + +#.SUFFIXES: .c .S .o +# +#.c.o: +# $(CC) $(CFLAGS) $(OPTFLAGS) $(INC) -c $< +# +.S.o: + $(CC) $(CFLAGS) $(INC) -c $< + +apm_init.inc: apm_init + sh ${DIR}/bin2asm.sh apm_init > apm_init.inc + +apm_init: $(OBJS) + $(LD) -Bstatic -N -T 0 -o apm_init $(OBJS) + cp apm_init apm_init.sym + @strip apm_init + @sh ${DIR}/rmaouthdr apm_init apm_init.tmp + @mv -f apm_init.tmp apm_init + +#allclean: clean +# @rm -f apm_init.inc +clean: + rm -f *.o apm_init apm_init.sym apm_init.inc + +#.include <bsd.prog.mk> diff --git a/sys/arch/i386/apm_init/Makefile.inc b/sys/arch/i386/apm_init/Makefile.inc new file mode 100644 index 00000000000..715da6093c5 --- /dev/null +++ b/sys/arch/i386/apm_init/Makefile.inc @@ -0,0 +1,39 @@ +# $NetBSD: Makefile.inc,v 1.12 1995/10/07 09:56:55 mycroft Exp $ +# +# NOTE: $S must correspond to the top of the 'sys' tree + +APMDIR= ${I386}/apm_init + +APMDST= lib/apm_init +APMREL?= ../../ +APMINC?= ${APMDST}/apm_init.inc + +APMDEPS= \ + ${APMDIR}/Makefile \ + ${APMDIR}/apm_bios.h \ + ${APMDIR}/apm_init.S \ + ${APMDIR}/apm_segments.h \ + ${APMDIR}/bin2asm.sh \ + ${APMDIR}/real_prot.S \ + ${APMDIR}/real_prot.h \ + ${APMDIR}/rmaouthdr \ + ${APMDIR}/table.c + +${APMINC}: ${APMDEPS} ${APMDST} + @echo making sure the apm grappling hook is up to date... + @(cd ${APMDST} && ${MAKE} -f ${APMREL}${APMDIR}/Makefile \ + APMCC="${CC}" \ + APMCFLAGS="${CFLAGS}" \ + APMREL="${APMREL}" \ + APMDIR="${APMDIR}" apm_init.inc) + +clean:: .NOTMAIN __always_make_apmlib + @echo cleaning the apm grappling hook objects + @(cd ${APMDST} && ${MAKE} -f ${APMREL}${APMDIR}/Makefile \ + APMCC="${CC}" \ + APMCFLAGS="${CFLAGS}" \ + APMREL="${APMREL}" \ + APMDIR="${APMDIR}" clean) + +${APMDST} __always_make_apmlib: .NOTMAIN + @([ -d ${APMDST} ] || mkdir -p ${APMDST}) diff --git a/sys/arch/i386/apm_init/apm_bios.h b/sys/arch/i386/apm_init/apm_bios.h new file mode 100644 index 00000000000..5354b9ccfb7 --- /dev/null +++ b/sys/arch/i386/apm_init/apm_bios.h @@ -0,0 +1,185 @@ +/* + * Advanced Power Management (APM) BIOS driver for laptop PCs. + * + * Copyright (c) 1994 by HOSOKAWA Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, and distributed, in + * both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Aug, 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + * Oct, 1994 NetBSD port (1.0 BETA 10/2) by ukai + */ + +#ifndef APM_BIOS_H +#define APM_BIOS_H 1 + +#ifdef KERNEL + +/* BIOS id */ +#define APM_BIOS 0x53 +#define SYSTEM_BIOS 0x15 + +/* APM flags */ +#define APM_16BIT_SUPPORT 0x01 +#define APM_32BIT_SUPPORT 0x02 +#define APM_CPUIDLE_SLOW 0x04 +#define APM_DISABLED 0x08 +#define APM_DISENGAGED 0x10 + +/* APM initializer physical address */ +#define APM_OURADDR 0x00080000 + +/* Error code of APM initializer */ +#define APMINI_CANTFIND 0xffffffff +#define APMINI_NOT32BIT 0xfffffffe +#define APMINI_CONNECTERR 0xfffffffd +#define APMINI_BADVER 0xfffffffc + +/* APM functions */ +#define APM_INSTCHECK 0x00 +#define APM_REALCONNECT 0x01 +#define APM_PROT16CONNECT 0x02 +#define APM_PROT32CONNECT 0x03 +#define APM_DISCONNECT 0x04 +#define APM_CPUIDLE 0x05 +#define APM_CPUBUSY 0x06 +#define APM_SETPWSTATE 0x07 +#define APM_ENABLEDISABLEPM 0x08 +#define APM_RESTOREDEFAULT 0x09 +#define APM_GETPWSTATUS 0x0a +#define APM_GETPMEVENT 0x0b +#define APM_GETPWSTATE 0x0c +#define APM_ENABLEDISABLEDPM 0x0d +#define APM_DRVVERSION 0x0e +#define APM_ENGAGEDISENGAGEPM 0x0f +#define APM_OEMFUNC 0x80 + +/* error code */ +#define APME_OK 0x00 +#define APME_PMDISABLED 0x01 +#define APME_REALESTABLISHED 0x02 +#define APME_NOTCONNECTED 0x03 +#define APME_PROT16ESTABLISHED 0x05 +#define APME_PROT16NOTSUPPORTED 0x06 +#define APME_PROT32ESTABLISHED 0x07 +#define APME_PROT32NOTDUPPORTED 0x08 +#define APME_UNKNOWNDEVICEID 0x09 +#define APME_OUTOFRANGE 0x0a +#define APME_NOTENGAGED 0x0b +#define APME_CANTENTERSTATE 0x60 +#define APME_NOPMEVENT 0x80 +#define APME_NOAPMPRESENT 0x86 + + +/* device code */ +#define PMDV_APMBIOS 0x0000 +#define PMDV_ALLDEV 0x0001 +#define PMDV_DISP0 0x0100 +#define PMDV_DISP1 0x0101 +#define PMDV_2NDSTORAGE0 0x0200 +#define PMDV_2NDSTORAGE1 0x0201 +#define PMDV_2NDSTORAGE2 0x0202 +#define PMDV_2NDSTORAGE3 0x0203 +#define PMDV_PARALLEL0 0x0300 +#define PMDV_PARALLEL1 0x0301 +#define PMDV_SERIAL0 0x0400 +#define PMDV_SERIAL1 0x0401 +#define PMDV_SERIAL2 0x0402 +#define PMDV_SERIAL3 0x0403 +#define PMDV_SERIAL4 0x0404 +#define PMDV_SERIAL5 0x0405 +#define PMDV_SERIAL6 0x0406 +#define PMDV_SERIAL7 0x0407 +#define PMDV_NET0 0x0500 +#define PMDV_NET1 0x0501 +#define PMDV_NET2 0x0502 +#define PMDV_NET3 0x0503 +#define PMDV_PCMCIA0 0x0600 +#define PMDV_PCMCIA1 0x0601 +#define PMDV_PCMCIA2 0x0602 +#define PMDV_PCMCIA3 0x0603 +/* 0x0700 - 0xdfff Reserved */ +/* 0xe000 - 0xefff OEM-defined power device IDs */ +/* 0xf000 - 0xffff Reserved */ + +/* Power state */ +#define PMST_APMENABLED 0x0000 +#define PMST_STANDBY 0x0001 +#define PMST_SUSPEND 0x0002 +#define PMST_OFF 0x0003 +#define PMST_LASTREQNOTIFY 0x0004 +#define PMST_LASTREQREJECT 0x0005 +/* 0x0006 - 0x001f Reserved system states */ +/* 0x0020 - 0x003f OEM-defined system states */ +/* 0x0040 - 0x007f OEM-defined device states */ +/* 0x0080 - 0xffff Reserved device states */ + +#if !defined(ASM) && !defined(INITIALIZER) + +/* C definitions */ +typedef struct apm_hook_func { + struct apm_hook_func *next; /* Linked list */ + int (*func)(void); + const char *name; + int order; +} *apm_hook_func_t; + +apm_hook_func_t apm_resume_hook_init(int (*func)(void), char *name, int order); +void apm_resume_hook_delete(apm_hook_func_t delete_func); +apm_hook_func_t apm_suspend_hook_init(int (*func)(void), char *name, int order); +void apm_suspend_hook_delete(apm_hook_func_t delete_func); +void apm_suspend_resume(void); +void apm_cpu_idle(void); +void apm_cpu_busy(void); + +#endif /* !ASM && !INITIALIZER */ + +#define APM_MIN_ORDER 0x00 +#define APM_MID_ORDER 0x80 +#define APM_MAX_ORDER 0xff + +#endif /* KERNEL */ + +/* power management event code */ +#define PMEV_NOEVENT 0x0000 +#define PMEV_STANDBYREQ 0x0001 +#define PMEV_SUSPENDREQ 0x0002 +#define PMEV_NORMRESUME 0x0003 +#define PMEV_CRITRESUME 0x0004 +#define PMEV_BATTERYLOW 0x0005 +#define PMEV_POWERSTATECHANGE 0x0006 +#define PMEV_UPDATETIME 0x0007 +#define PMEV_CRITSUSPEND 0x0008 +#define PMEV_USERSTANDBYREQ 0x0009 +#define PMEV_USERSUSPENDREQ 0x000a +#define PMEV_STANDBYRESUME 0x000b +/* 0x000c - 0x00ff Reserved system events */ +/* 0x0100 - 0x01ff Reserved device events */ +/* 0x0200 - 0x02ff OEM-defined APM events */ +/* 0x0300 - 0xffff Reserved */ +#define PMEV_DEFAULT 0xffffffff /* used for customization */ + +#if !defined(ASM) && !defined(INITIALIZER) + +typedef struct apm_info { + u_int ai_major; /* APM major version */ + u_int ai_minor; /* APM minor version */ + u_int ai_acline; /* AC line status */ + u_int ai_batt_stat; /* Battery status */ + u_int ai_batt_life; /* Remaining battery life */ +} *apm_info_t; + +#define APMIO_SUSPEND _IO('P', 1) +#define APMIO_GETINFO _IOR('P', 2, struct apm_info) +#define APMIO_ENABLE _IO('P', 3) +#define APMIO_DISABLE _IO('P', 4) +#define APMIO_HALTCPU _IO('P', 5) +#define APMIO_NOTHALTCPU _IO('P', 6) + +#endif /* !ASM && !INITIALIZER */ + +#endif /* APM_BIOS_H */ diff --git a/sys/arch/i386/apm_init/apm_init.S b/sys/arch/i386/apm_init/apm_init.S new file mode 100644 index 00000000000..945c251446a --- /dev/null +++ b/sys/arch/i386/apm_init/apm_init.S @@ -0,0 +1,215 @@ +/* + * LP (Laptop Package) + * + * Copyright (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, and distributed, in + * both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + * Oct., 1994 NetBSD port (1.0 BETA 10/2) by ukai + */ + +/* + * If you want to know the specification of APM BIOS, see the following + * documentations, + * + * [1] Intel Corporation and Microsoft Corporation, "Advanced Power + * Management, The Next Generation, Version 1.0", Feb.,1992. + * + * [2] Intel Corporation and Microsoft Corporation, "Advanced Power + * Management (APM) BIOS Interface Specification Revision 1.1", + * Sep.,1993, Intel Order Number: 241704-001, Microsoft Part + * Number: 781-110-X01 + * + * or contact + * + * APM Support Desk (Intel Corporation, US) + * TEL: (800)628-8686 + * FAX: (916)356-6100. + */ + + .file "apm_init.S" + +#define ASM + +#include "real_prot.h" +#include <apm_bios.h> +#include <apm_segments.h> + +/* + * APM BIOS initializer + * + * Return value: + * %eax 0xfffffff Can't find APM BIOS + * 0xffffffe Don't support 32bit connection + * 0xffffffd Connection error + * otherwise APM version (16bit BCD format) + * %ebx APM cs entry offset (32bit) + * %ecx lower 16bit APM 16bit cs base (real mode segment) + * upper 16bit APM 32bit cs base (real mode segment) + * %edx lower 16bit APM ds limit (real mode segment) + * upper 16bit [Reserved] + * %esi lower 16bit APM cs limit (APM 1.1 or later) + * upper 16bit APM ds limit (APM 1.1 or later) + * %edi bit0 = 1 16bit protected mode interface supported + * bit1 = 1 32bit protected mode interface supported + * bit2 = 1 "CPU idle" call slows processor clock speed + * bit3 = 1 APM BIOS Power Management disabled + * bit4 = 1 APM BIOS Power Management disengaged + */ + + .text +ENTRY(apm_init) + cli /* disable interrupt */ + push %ebp /* save original base pointer */ + /* ebp is used as a register variable */ + /* + * save old data segments: We assume that %ds == %es && %ds == %ss + */ + push %fs + movw %ds, %ax + movw %ax, %fs + movw $(APM_INIT_DS_SEL), %ax /* initializer data segment */ + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movl %esp, old_esp /* save original stack pointer */ + movl $0xf000, %esp /* setup temporary stack */ + /* (note that it isn't 0x00000000) */ + + sidt EXT(Idtr_prot) /* save current IDT */ + call EXT(prot_to_real) /* return to real mode */ + + /* + * APM installation check + */ + movb $(APM_BIOS), %ah + movb $(APM_INSTCHECK), %al + data32 + movl $(PMDV_APMBIOS), %ebx + sti + int $(SYSTEM_BIOS) /* call system BIOS */ + cli + + jnc 1f /* if found, goto 1f */ + + data32 + call EXT(real_to_prot) /* come back again to protected mode */ + movl $(APMINI_CANTFIND), apm_version + /* can't find APM BIOS */ + jmp finish + +1: + movl %eax, %edx /* actually, movw %ax, %dx */ + /* save the value of %ax */ + data32 + call EXT(real_to_prot) /* come back again to protected mode */ + cmpb $0x50, %bh /* %bh == 'P'? */ + jnz 1f + cmpb $0x4d, %bl /* %bl == 'M'? */ + jz 2f + +1: + movl $(APMINI_BADVER), apm_version + /* can't find APM BIOS */ + jmp finish + +2: + testl $(APM_32BIT_SUPPORT), %ecx + /* supports 32bit connection? */ + jnz 1f + + movl $(APMINI_NOT32BIT), apm_version + /* don't support 32bit connection */ + jmp finish +1: + movl %edx, apm_version + andl $0x0000ffff, %ecx + movl %ecx, apm_flags + + /* + * APM Protected Mode 32-bit Interface Connect + */ + call EXT(prot_to_real) /* return to real mode */ + + movb $(APM_BIOS), %ah + movb $(APM_DISCONNECT), %al /* just in case bootloader connected*/ + data32 + movl $(PMDV_APMBIOS), %ebx + sti + int $(SYSTEM_BIOS) + cli + movb $(APM_BIOS), %ah + movb $(APM_PROT32CONNECT), %al + data32 + movl $(PMDV_APMBIOS), %ebx + sti + int $(SYSTEM_BIOS) + cli + jnc 1f /* if successed, go to 1f */ + data32 + call EXT(real_to_prot) + movl $(APMINI_CONNECTERR), apm_version + /* connection error */ + jmp finish +1: + /* save PM 32bit code segment into %bp */ + movl %eax, %ebp /* actually, movw %ax, %bp */ + data32 + call EXT(real_to_prot) + movl $0x0000ffff, %eax + andl %eax, %ebp /* 32bit cs base */ + andl %eax, %ecx /* 16bit cs base */ + andl %eax, %edx /* ds base */ + andl %eax, %esi /* cs length (APM 1.1 or later) */ + andl %eax, %edi /* ds length (APM 1.1 or later) */ + /* %ebx is code offset */ + /* pack 32bit cs and 16bit cs into %ecx */ + shll $16, %ebp + orl %ebp, %ecx + /* pack cs length and ds length into %esi */ + shll $16, %edi + orl %edi, %esi +finish: + cli + lidt EXT(Idtr_prot) /* restore old IDTR */ + movl old_esp, %esp /* restore old stack pointer */ + movl apm_version, %ebp /* stored to %eax later */ + movl apm_flags, %edi +#if 0 + movw $(BOOTSTRAP_DS_SEL), %ax + /* restore old data segments */ +#else + movw %fs, %ax +#endif + movw %ax, %ss + movw %ax, %es + movw %ax, %ds + movl %ebp, %eax + pop %fs + popl %ebp /* restore old base pointer */ + lret /* restore old code segment */ + + .data + + .globl EXT(ouraddr) +LEXT(ouraddr) + .long APM_OURADDR + +old_esp: + .long 0 +apm_version: + .long 0 +apm_flags: + .long 0 +old_ds: + .word 0 +old_es: + .word 0 +old_ss: + .word 0 diff --git a/sys/arch/i386/apm_init/apm_segments.h b/sys/arch/i386/apm_init/apm_segments.h new file mode 100644 index 00000000000..5e5fcdbe7e4 --- /dev/null +++ b/sys/arch/i386/apm_init/apm_segments.h @@ -0,0 +1,30 @@ +/* + * LP (Laptop Package) + * + * Copyright (C) 1994 by HOSOKAWA Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, and distributed, in + * both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + */ + +#define SIZEOF_GDT 8 +#define BOOTSTRAP_GDT_NUM 9 + +#define APM_INIT_CS_INDEX (BOOTSTRAP_GDT_NUM - 3) +#define APM_INIT_DS_INDEX (BOOTSTRAP_GDT_NUM - 2) +#define APM_INIT_CS16_INDEX (BOOTSTRAP_GDT_NUM - 1) +#define APM_INIT_CS_SEL (APM_INIT_CS_INDEX << 3) +#define APM_INIT_DS_SEL (APM_INIT_DS_INDEX << 3) +#define APM_INIT_CS16_SEL (APM_INIT_CS16_INDEX << 3) + +#define CS32_ATTRIB 0x4F9e +#define CS16_ATTRIB 0x009e +#define DS32_ATTRIB 0x4F92 + +#define BOOTSTRAP_DS_SEL 0x10 diff --git a/sys/arch/i386/apm_init/apm_setup.h b/sys/arch/i386/apm_init/apm_setup.h new file mode 100644 index 00000000000..fdc380e50be --- /dev/null +++ b/sys/arch/i386/apm_init/apm_setup.h @@ -0,0 +1,24 @@ +/* + * LP (Laptop Package) + * + * Copyright (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, distributed, and sold, + * in both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + * Oct., 1994 NetBSD port (1.0 BETA 10/2) by ukai + */ + +extern u_long apm_version; +extern u_long apm_cs_entry; +extern u_short apm_cs32_base; +extern u_short apm_cs16_base; +extern u_short apm_ds_base; +extern u_short apm_cs_limit; +extern u_short apm_ds_limit; +extern u_short apm_flags; diff --git a/sys/arch/i386/apm_init/bin2asm.sh b/sys/arch/i386/apm_init/bin2asm.sh new file mode 100644 index 00000000000..6694b49d9b5 --- /dev/null +++ b/sys/arch/i386/apm_init/bin2asm.sh @@ -0,0 +1,21 @@ +#!/bin/sh - +# bin2asm (binary to asm) shell script version by ukai +# +# +if [ $# -lt 1 ]; then + echo 'usage: $0 [in]' + exit 1 +fi +in=$1 +size=`ls -l ${in} | awk '{print $5}'` +# Oops, must 8 byte align +len=`expr \( $size + 8 \) / 8 \* 8` + +echo "/* This file is automatically generated by bin2asm.sh */" +echo "/* Original file is '${in}' */" +echo +dd if=${in} bs=${len} conv=sync |\ + hexdump -v -e '" .byte " 7/1 "0x%02x, " 1/1 " 0x%02x" "\n"' +echo +echo "/* Total size = $size -> $len */" +echo "/* End of File */" diff --git a/sys/arch/i386/apm_init/real_prot.S b/sys/arch/i386/apm_init/real_prot.S new file mode 100644 index 00000000000..5f9e97ecf68 --- /dev/null +++ b/sys/arch/i386/apm_init/real_prot.S @@ -0,0 +1,186 @@ +/* + * Mach Operating System + * Copyright (c) 1992, 1991 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.2 92/04/04 11:34:13 rpd + * $Id: real_prot.S,v 1.1 1996/04/29 14:15:43 hvozda Exp $ + */ + + +/* + Copyright 1988, 1989, 1990, 1991, 1992 + by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * LP (Laptop Package) + * + * Copyright (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, and distributed, in + * both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + * Oct., 1994 NetBSD port (1.0 BETA 10/2) by ukai + */ + +/* + * Modified for APM BIOS initializer by HOSOKAWA Tatsumi + * + * See also locore.s. It supports these functions works correctly. + */ + + .file "real_prot.S" + +#include "real_prot.h" +#include "apm_segments.h" + +CR0_PE_ON = 0x1 +CR0_PE_OFF = 0xfffffffe + +.globl _ouraddr + .text + +/* + * + * real_to_prot() + * transfer from real mode to protected mode. + */ + +ENTRY(real_to_prot) + /* guarantee that interrupt is disabled when in prot mode */ + cli + + /* + * deleted for APM initializer by HOSOKAWA Tatsumi + * <hosoakwa@mt.cs.keio.ac.jp> + */ +#if 0 + /* load the gdtr */ + addr32 + data32 + lgdt EXT(Gdtr) +#endif + + /* set the PE bit of CR0 */ + mov %cr0, %eax + + data32 + or $CR0_PE_ON, %eax + mov %eax, %cr0 + + /* + * make intrasegment jump to flush the processor pipeline and + * reload CS register + */ + data32 + ljmp $(APM_INIT_CS_SEL), $xprot + +xprot: + /* + * we are in USE32 mode now + * set up the protected mode segment registers : DS, SS, ES + */ + mov $(APM_INIT_DS_SEL), %eax + movw %ax, %ds + movw %ax, %ss + movw %ax, %es + + /* load idtr so we can debug */ + lidt EXT(Idtr_prot) + + ret + +/* + * + * prot_to_real() + * transfer from protected mode to real mode + * + */ + +ENTRY(prot_to_real) + + /* set up a dummy stack frame for the second seg change. */ + movl _ouraddr, %eax + sarl $4, %eax + pushw %ax + movw $xreal, %ax /* gas botches pushw $xreal - extra bytes 0, 0*/ + pushw %ax /* decode to add %al, (%eax) (%al usually 0) */ + + /* Change to use16 mode. */ + ljmp $(APM_INIT_CS16_SEL), $x16 + +x16: + /* clear the PE bit of CR0 */ + mov %cr0, %eax + data32 + and $CR0_PE_OFF, %eax + mov %eax, %cr0 + + /* + * make intersegment jmp to flush the processor pipeline + * using the fake stack frame set up earlier + * and reload CS register + */ + lret + +xreal: + /* + * we are in real mode now + * set up the real mode segment registers : DS, SS, ES + */ + movw %cs, %ax + movw %ax, %ds + movw %ax, %ss + movw %ax, %es + + /* load idtr so we can debug */ + addr32 + data32 + lidt EXT(Idtr_real) + + data32 + ret diff --git a/sys/arch/i386/apm_init/real_prot.h b/sys/arch/i386/apm_init/real_prot.h new file mode 100644 index 00000000000..51e91cf05d9 --- /dev/null +++ b/sys/arch/i386/apm_init/real_prot.h @@ -0,0 +1,57 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + * + * from: Mach, Revision 2.7 92/02/29 15:33:41 rpd + * $Id: real_prot.h,v 1.1 1996/04/29 14:15:14 hvozda Exp $ + */ + +/* + * LP (Laptop Package) + * + * Copyright (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, and distributed, in + * both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + * Oct., 1994 NetBSD port (1.0 BETA 10/2) by ukai + */ + +/* + * Modified to APM BIOS initializer by HOSOKAWA, Tatsumi + */ + +#define ALIGN 4 +#define EXT(x) _ ## x +#define LEXT(x) _ ## x ## : + +#define addr32 .byte 0x67 +#define data32 .byte 0x66 + +#define ENTRY(x) .globl EXT(x); .align ALIGN; LEXT(x) diff --git a/sys/arch/i386/apm_init/rmaouthdr b/sys/arch/i386/apm_init/rmaouthdr new file mode 100644 index 00000000000..608715015fe --- /dev/null +++ b/sys/arch/i386/apm_init/rmaouthdr @@ -0,0 +1,6 @@ +#!/bin/csh -f +# +# from: Mach, Revision 2.2 92/04/04 11:36:01 rpd +# $Id: rmaouthdr,v 1.1 1996/04/29 14:15:35 hvozda Exp $ +# +dd if=$1 of=$2 ibs=32 skip=1 obs=1024b diff --git a/sys/arch/i386/apm_init/table.c b/sys/arch/i386/apm_init/table.c new file mode 100644 index 00000000000..d3cc1d98670 --- /dev/null +++ b/sys/arch/i386/apm_init/table.c @@ -0,0 +1,25 @@ +/* + * LP (Laptop Package) + * + * Copyright (C) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, and distributed, in + * both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + * Oct., 1994 NetBSD port (1.0 BETA 10/2) by ukai + */ + +#include <apm_bios.h> + +struct pseudo_desc { + unsigned short limit; + unsigned long base __attribute__ ((packed)); +}; + +struct pseudo_desc Idtr_prot = { 0, 0 }; /* filled on run time */ +struct pseudo_desc Idtr_real = { 0x400 - 1, 0x0 }; diff --git a/sys/arch/i386/conf/HELIOS_PCMCIA b/sys/arch/i386/conf/HELIOS_PCMCIA index b412cdfb0ad..2b91d0e76f8 100644 --- a/sys/arch/i386/conf/HELIOS_PCMCIA +++ b/sys/arch/i386/conf/HELIOS_PCMCIA @@ -1,4 +1,4 @@ -# $OpenBSD: HELIOS_PCMCIA,v 1.3 1996/04/18 18:55:35 niklas Exp $ +# $OpenBSD: HELIOS_PCMCIA,v 1.4 1996/04/29 14:12:01 hvozda Exp $ # # HELIOS_PCMCIA -- Eric Hvozda's notebook # @@ -25,6 +25,7 @@ options KTRACE # system call tracing, a la ktrace(1) options COMPAT_NOMID # compatibility with 386BSD, BSDI, NetBSD 0.8, options COMPAT_09 # NetBSD 0.9, options COMPAT_10 # NetBSD 1.0, +options COMPAT_11 # NetBSD 1.1, options COMPAT_43 # and 4.3BSD options LKM # loadable kernel modules @@ -48,26 +49,46 @@ options FDSCRIPTS # secure setuid scripts options INET # IP + ICMP + TCP + UDP -config bsd root on wd0a swap on wd0b and vnd0b dumps on wd0b +config bsd root on wd0a swap on wd0b dumps on wd0b mainbus0 at root isa0 at mainbus0 pci0 at mainbus0 +#apm0 at mainbus0 npx0 at isa? port 0xf0 irq 13 # math coprocessor pc0 at isa? port 0x60 irq 1 # generic PC console device - -pcic0 at isa? port 0x3E0 flags 0 -pcmcia0 at pcic? iomem 0xd4000 iosiz 4096 -com2 at pcmcia? port 0x3e8 irq 12 -ed2 at pcmcia? port 0x300 iomem 0xcc000 irq 10 +#spkr0 at pckbd? port 0x61 # PC speaker + +# Multiple controllers need some testing. Some laptops have multiple PCIC +# controllers instead of two-slot controllers. +# The i82365 (pcic) controller uses the same ports for the first two +# controllers and for the second two controllers. + +# IRQ 2/9 doesn't seem to work for status change interrupts, so use one +# of the higher ones. +pcicmaster0 at isa? port 0x3E0 size 2 +pcic0 at pcicmaster0 irq 11 iomem 0xd4000 iosiz 4096 +pcic1 at pcicmaster0 irq 12 iomem 0xd5000 iosiz 4096 +pcicmaster1 at isa? port 0x3E2 size 2 +pcic2 at pcicmaster1 irq 11 iomem 0xd6000 iosiz 4096 +pcic3 at pcicmaster1 irq 12 iomem 0xd7000 iosiz 4096 + +pcmcia* at pcic? + +#ed0 at pcmcia? port 0x300 iomem 0xd8000 iosiz 8192 irq 10 +ed0 at pcmcia? port 0x300 size 0x20 irq 10 slot ? +#ep0 at pcmcia? port 0x300 size 0x10 irq 10 slot ? +#com1 at pcmcia? port 0x2f8 size 8 irq 5 slot ? +com2 at pcmcia? port 0x3e8 size 8 irq 5 slot ? +#com3 at pcmcia? port 0x2e8 size 8 irq 3 slot ? com0 at isa? port 0x3f8 irq 4 # standard PC serial ports com1 at isa? port 0x2f8 irq 3 -lpt0 at isa? port 0x378 irq 7 # standard PC parallel ports +lpt0 at isa? port 0x378 # standard PC parallel ports fdc0 at isa? port 0x3f0 irq 6 drq 2 # standard PC floppy controllers fd0 at fdc? drive 0 @@ -75,7 +96,7 @@ fd0 at fdc? drive 0 wdc0 at isa? port 0x1f0 irq 14 # ST506, ESDI, and IDE controllers wd0 at wdc? drive 0 -sb0 at isa? port 0x240 irq 5 drq 1 # SoundBlaster +sb0 at isa? port 0x240 irq 7 drq 1 # SoundBlaster pseudo-device loop 1 # network loopback pseudo-device bpfilter 8 # packet filter diff --git a/sys/arch/i386/conf/Makefile.i386 b/sys/arch/i386/conf/Makefile.i386 index aee08091430..0edf4047ae1 100644 --- a/sys/arch/i386/conf/Makefile.i386 +++ b/sys/arch/i386/conf/Makefile.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile.i386,v 1.8 1996/04/24 12:05:17 mickey Exp $ +# $OpenBSD: Makefile.i386,v 1.9 1996/04/29 14:12:10 hvozda Exp $ # $NetBSD: Makefile.i386,v 1.66 1996/02/29 20:56:16 cgd Exp $ # Makefile for OpenBSD @@ -145,10 +145,14 @@ links: sed 's,../.*/\(.*.o\),rm -f \1; ln -s ../GENERIC/\1 \1,' > makelinks sh makelinks && rm -f dontlink +# depend on APM +.include "${I386}/apm_init/Makefile.inc" +locore.o: ${APMINC} + SRCS= ${I386}/i386/locore.s \ param.c ioconf.c ${CFILES} ${SFILES} depend: .depend -.depend: ${SRCS} assym.h param.c +.depend: ${SRCS} assym.h param.c ${APMINC} mkdep ${AFLAGS} ${CPPFLAGS} ${I386}/i386/locore.s mkdep -a ${CFLAGS} ${CPPFLAGS} param.c ioconf.c ${CFILES} mkdep -a ${AFLAGS} ${CPPFLAGS} ${SFILES} diff --git a/sys/arch/i386/conf/PCMCIA b/sys/arch/i386/conf/PCMCIA new file mode 100644 index 00000000000..2544e06c62c --- /dev/null +++ b/sys/arch/i386/conf/PCMCIA @@ -0,0 +1,138 @@ +# $Id: PCMCIA,v 1.1 1996/04/29 14:12:24 hvozda Exp $ +# $Source: /cvs/OpenBSD/src/sys/arch/i386/conf/Attic/PCMCIA,v $ +# +# ATHENA-AHA -- ATHENA kernel for Adaptec & others... +# +# from: GENERIC -- everything that's currently supported +# NetBSD: GENERIC,v 1.12 1995/02/21 01:43:02 brezak Exp +# + +machine i386 # architecture, used by config; REQUIRED + +options I586_CPU +options I486_CPU +options I386_CPU +options INSECURE +options MATH_EMULATE + +options DUMMY_NOPS # speed hack; recommanded +options XSERVER,UCONSOLE +options MACHINE_NONCONTIG + +maxusers 32 # estimated number of users +options TIMEZONE=300 # time zone to adjust RTC time by +options DST=1 # daylight savings time used by RTC + +options SWAPPAGER # paging; REQUIRED +options VNODEPAGER # mmap() of files +options DEVPAGER # mmap() of devices + +options DDB # in-kernel debugger +#makeoptions DEBUG="-g" # compile full symbol table +#options DIAGNOSTIC # internally consistency checks +options KTRACE # system call tracing, a la ktrace(1) + +options SYSVMSG # System V-like message queues +options SYSVSEM # System V-like semaphores +options SYSVSHM # System V-like memory sharing +#options SHMMAXPGS=1024 # 1024 pages is the default + +options COMPAT_NOMID # compatibility with 386BSD, BSDI, NetBSD 0.8, +options COMPAT_09 # NetBSD 0.9, +options COMPAT_10 # NetBSD 1.0, +options COMPAT_43 # and 4.3BSD +options TCP_COMPAT_42 # TCP bug compatibility with 4.2BSD + +options COMPAT_SVR4 # binary compatibility with SVR4 +options COMPAT_IBCS2 # binary compatibility with SCO and ISC +options COMPAT_LINUX # binary compatibility with Linux + +options USER_LDT # user-settable LDT; used by WINE +options LKM # loadable kernel modules + +options FFS #,QUOTA # UFS and quotas +#options LFS # log-structured file system +options MFS # memory file system + +options NFSCLIENT # Network File System client +options NFSSERVER # Network File System server +options HAS_VOPLEASE # XXX + +options APM_NOIDLE +#options PCIVERBOSE +#options PCMCIA_DEBUG +#options PCMCIA_ISA_DEBUG + +### SCSI: +#options CD9660 # ISO 9660 + Rock Ridge file system +### +options MSDOSFS # MS-DOS file system +options FIFO # FIFOs; RECOMMENDED +options PROCFS # /proc + +#options GATEWAY # packet forwarding +options INET # IP + ICMP + TCP + UDP +#options NS # XNS +#options ISO,TPIP # OSI +#options EON # OSI tunneling over IP +#options CCITT,LLC,HDLC # X.25 + +config netbsd swap generic +options GENERIC + +# Local Athena options + +options PCVT_CTRL_ALT_DEL # For screwed-over Linux weenies + +mainbus0 at root +isa0 at mainbus0 +pci0 at mainbus0 bus 0 + +npx0 at isa? port 0xf0 irq 13 # math coprocessor + +vt0 at isa? port 0x60 irq 1 + +com0 at isa? port 0x3f8 irq 4 # standard PC serial ports +com1 at isa? port 0x2f8 irq 3 +com2 at isa? port 0x3e8 irq 5 + +lpt0 at isa? port 0x378 irq 7 # standard PC parallel ports + +pms0 at pckbd? irq 12 # PS/2 auxiliary port mouse + +fdc0 at isa? port 0x3f0 irq 6 drq 2 # standard PC floppy controllers +fd0 at fdc0 drive 0 + +wdc0 at isa? port 0x1f0 irq 14 # ST506, ESDI, and IDE controllers +wd0 at wdc0 drive 0 + +spkr0 at pckbd? port 0x61 +#apm0 at mainbus? + +# Multiple controllers need some testing. Some laptops have multiple PCIC +# controllers instead of two-slot controllers. +# The i82365 (pcic) controller uses the same ports for the first two +# controllers and for the second two controllers. + +# IRQ 2/9 doesn't seem to work for status change interrupts, so use one +# of the higher ones. +pcicmaster0 at isa? port 0x3E0 size 2 +pcic0 at pcicmaster0 irq 11 iomem 0xd4000 iosiz 4096 +pcic1 at pcicmaster0 irq 12 iomem 0xd5000 iosiz 4096 +pcicmaster1 at isa? port 0x3E2 size 2 +pcic2 at pcicmaster1 irq 11 iomem 0xd6000 iosiz 4096 +pcic3 at pcicmaster1 irq 12 iomem 0xd7000 iosiz 4096 + +pcmcia* at pcic? + +#ed0 at pcmcia? port 0x300 iomem 0xd8000 iosiz 8192 irq 10 +ed0 at pcmcia? port 0x300 size 0x20 irq 10 slot ? +ep0 at pcmcia? port 0x300 size 0x10 irq 10 slot ? +#com1 at pcmcia? port 0x2f8 size 8 irq 5 slot ? +#com2 at pcmcia? port 0x3e8 size 8 irq 5 slot ? +com3 at pcmcia? port 0x2e8 size 8 irq 3 slot ? + +pseudo-device loop 1 # network loopback +pseudo-device bpfilter 4 # packet filter +pseudo-device ppp 2 # PPP +pseudo-device pty 64 # pseudo-terminals diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index c434f132f04..2bd8ee7cb59 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: files.i386,v 1.13 1996/04/28 17:02:01 mickey Exp $ +# $OpenBSD: files.i386,v 1.14 1996/04/29 14:12:16 hvozda Exp $ # $NetBSD: files.i386,v 1.72 1996/04/09 22:59:03 cgd Exp $ # # new style config file for i386 architecture @@ -52,7 +52,8 @@ major {rd = 17} # System bus types # -device mainbus: isabus, eisabus, pcibus +define mainbus { } +device mainbus: isabus, eisabus, pcibus, mainbus attach mainbus at root file arch/i386/i386/mainbus.c mainbus @@ -69,6 +70,12 @@ file arch/i386/pci/pci_machdep.c pci file arch/i386/pci/pci_compat.c pci # XXX compatibility # +# Pcmcia, before ISA (to define device stuff) +# + +include "../../../dev/pcmcia/files.pcmcia" + +# # ISA and mixed ISA+EISA or ISA+PCI or ISA+PCMCIA drivers # @@ -77,10 +84,6 @@ major {mcd = 7} major {wd = 0} major {wt = 3} -#define pcic here until config issues are resolved -#device pcic at isa: pcmciabus -#file dev/isa/pcmcia_pcic.c pcic - include "../../../dev/isa/files.isa" file arch/i386/isa/isa_machdep.c isabus @@ -138,9 +141,9 @@ file arch/i386/isa/spkr.c spkr needs-flag # National Semiconductor DS8390/WD83C690-based boards # (WD/SMC 80x3 family, SMC Ultra [8216], 3Com 3C503, NE[12]000, and clones) # XXX conflicts with other ports; can't be in files.isa -device ed: ether, ifnet -attach ed at isa -file dev/isa/if_ed.c ed +#device ed: ether, ifnet +#attach ed at isa +#file dev/isa/if_ed.c ed # AMD am7990 (Lance) -based boards # (BICC Isolan, NE2100, DEPCA) @@ -183,3 +186,7 @@ file arch/i386/i386/linux_machdep.c compat_linux # FreeBSD binary compatibility (COMPAT_FREEBSD) include "../../../compat/freebsd/files.freebsd" file arch/i386/i386/freebsd_machdep.c compat_freebsd + +device apm +attach apm at mainbus +file arch/i386/i386/apm.c apm needs-count diff --git a/sys/arch/i386/i386/apm.c b/sys/arch/i386/i386/apm.c new file mode 100644 index 00000000000..03483c7a868 --- /dev/null +++ b/sys/arch/i386/i386/apm.c @@ -0,0 +1,844 @@ +/* $NetBSD $ */ + +/*- + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + */ + +#include "apm.h" +#if NAPM > 0 + +#if NAPM > 1 +#error only one APM device may be configured +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/signalvar.h> +#include <sys/kernel.h> +#include <sys/map.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/ioctl.h> + +#include <machine/cpu.h> +#include <machine/cpufunc.h> +#include <machine/gdt.h> +#include <machine/psl.h> + +#include <dev/isa/isareg.h> +#include <i386/isa/isa_machdep.h> +#include <i386/isa/nvram.h> +#include <dev/isa/isavar.h> + +#include <machine/apmvar.h> + +#if defined(DEBUG) || defined(APMDEBUG) +#define DPRINTF(x) printf x +#define STATIC /**/ +#else +#define DPRINTF(x) /**/ +#define STATIC static +#endif + +int apmprobe __P((struct device *, void *, void *)); +void apmattach __P((struct device *, struct device *, void *)); + +#define APM_NEVENTS 16 + +struct apm_softc { + struct device sc_dev; + struct selinfo sc_rsel; + struct selinfo sc_xsel; + int sc_flags; + int event_count; + int event_ptr; + struct apm_event_info event_list[APM_NEVENTS]; +}; +#define SCFLAG_OREAD 0x0000001 +#define SCFLAG_OWRITE 0x0000002 +#define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE) + +#define APMUNIT(dev) (minor(dev)&0xf0) +#define APMDEV(dev) (minor(dev)&0x0f) +#define APMDEV_NORMAL 0 +#define APMDEV_CTL 8 + +struct cfattach apm_ca = { + sizeof(struct apm_softc), apmprobe, apmattach +}; + +struct cfdriver apm_cd = { + NULL, "apm", DV_DULL +}; + +struct apm_connect_info apminfo = { 0 }; +u_char apm_majver; +u_char apm_minver; +u_short apminited; +int apm_dobusy; + +STATIC int apm_get_powstat __P((struct apmregs *)); +STATIC void apm_power_print __P((struct apm_softc *, struct apmregs *)); +STATIC void apm_event_handle __P((struct apm_softc *, struct apmregs *)); +STATIC int apm_get_event __P((struct apmregs *)); +STATIC void apm_set_ver __P((struct apm_softc *)); +STATIC void apm_periodic_check __P((void *)); +STATIC void apm_disconnect __P((void *)); +STATIC void apm_perror __P((const char *, struct apmregs *)); +STATIC void apm_powmgt_enable __P((int onoff)); +STATIC void apm_powmgt_engage __P((int onoff, u_int devid)); +STATIC void apm_devpowmgt_enable __P((int onoff, u_int devid)); +STATIC void apm_suspend __P((void)); +STATIC void apm_standby __P((void)); +STATIC int apm_record_event __P((struct apm_softc *sc, u_int event_type)); + +STATIC const char * +apm_err_translate(code) +int code; +{ + switch(code) { + case APM_ERR_PM_DISABLED: + return "power management disabled"; + case APM_ERR_REALALREADY: + return "real mode interface already connected"; + case APM_ERR_NOTCONN: + return "interface not connected"; + case APM_ERR_16ALREADY: + return "16-bit interface already connected"; + case APM_ERR_16NOTSUPP: + return "16-bit interface not supported"; + case APM_ERR_32ALREADY: + return "32-bit interface already connected"; + case APM_ERR_32NOTSUPP: + return "32-bit interface not supported"; + case APM_ERR_UNRECOG_DEV: + return "unrecognized device ID"; + case APM_ERR_ERANGE: + return "parameter out of range"; + case APM_ERR_NOTENGAGED: + return "interface not engaged"; + case APM_ERR_UNABLE: + return "unable to enter requested state"; + case APM_ERR_NOEVENTS: + return "No pending events"; + case APM_ERR_NOT_PRESENT: + return "No APM present"; + default: + return "unknown error code?"; + } +} + +STATIC void +apm_perror(str, regs) +const char *str; +struct apmregs *regs; +{ + printf("APM %s: %s (%d)\n", str, + apm_err_translate(APM_ERR_CODE(regs)), + APM_ERR_CODE(regs)); +} + +STATIC void +apm_power_print (sc, regs) +struct apm_softc *sc; +struct apmregs *regs; +{ + if (BATT_LIFE(regs) != APM_BATT_LIFE_UNKNOWN) { + printf("%s: battery life expectancy %d%%\n", + sc->sc_dev.dv_xname, + BATT_LIFE(regs)); + } + printf("%s: A/C state: ", sc->sc_dev.dv_xname); + switch (AC_STATE(regs)) { + case APM_AC_OFF: + printf("off\n"); + break; + case APM_AC_ON: + printf("on\n"); + break; + case APM_AC_BACKUP: + printf("backup power\n"); + break; + default: + case APM_AC_UNKNOWN: + printf("unknown\n"); + break; + } + printf("%s: battery charge state:", sc->sc_dev.dv_xname); + if (apm_minver == 0) + switch (BATT_STATE(regs)) { + case APM_BATT_HIGH: + printf("high\n"); + break; + case APM_BATT_LOW: + printf("low\n"); + break; + case APM_BATT_CRITICAL: + printf("critical\n"); + break; + case APM_BATT_CHARGING: + printf("charging\n"); + break; + case APM_BATT_UNKNOWN: + printf("unknown\n"); + break; + default: + printf("undecoded state %x\n", BATT_STATE(regs)); + break; + } + else if (apm_minver >= 1) { + if (BATT_FLAGS(regs) & APM_BATT_FLAG_NOBATTERY) + printf(" no battery"); + else { + if (BATT_FLAGS(regs) & APM_BATT_FLAG_HIGH) + printf(" high"); + if (BATT_FLAGS(regs) & APM_BATT_FLAG_LOW) + printf(" low"); + if (BATT_FLAGS(regs) & APM_BATT_FLAG_CRITICAL) + printf(" critical"); + if (BATT_FLAGS(regs) & APM_BATT_FLAG_CHARGING) + printf(" charging"); + } + printf("\n"); + if (BATT_REM_VALID(regs)) + printf("%s: estimated %d:%02d minutes\n", + sc->sc_dev.dv_xname, + BATT_REMAINING(regs) / 60, + BATT_REMAINING(regs)%60); + } + return; +} + +int apm_standbys = 0; +int apm_userstandbys = 0; +int apm_suspends = 0; +int apm_battlow = 0; + +STATIC void +apm_get_powstate(dev) +u_int dev; +{ + struct apmregs regs; + int rval; + regs.bx = dev; + rval = apmcall(APM_GET_POWER_STATE, ®s); + if (rval == 0) { + printf("apm dev %04x state %04x\n", dev, regs.cx); + } +} + +STATIC void +apm_suspend() +{ + (void) apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_SUSPEND); +} + +STATIC void +apm_standby() +{ + (void) apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_STANDBY); +} + +static int apm_evindex = 0; + +STATIC int +apm_record_event(sc, event_type) +struct apm_softc *sc; +u_int event_type; +{ + struct apm_event_info *evp; + + if ((sc->sc_flags & SCFLAG_OPEN) == 0) + return 1; /* no user waiting */ + if (sc->event_count == APM_NEVENTS) + return 1; /* overflow */ + evp = &sc->event_list[sc->event_ptr]; + sc->event_count++; + sc->event_ptr++; + sc->event_ptr %= APM_NEVENTS; + evp->type = event_type; + evp->index = ++apm_evindex; + selwakeup(&sc->sc_rsel); + return (sc->sc_flags & SCFLAG_OWRITE) ? 0 : 1; /* user may handle */ +} + +STATIC void +apm_event_handle(sc, regs) +struct apm_softc *sc; +struct apmregs *regs; +{ + int error; + struct apmregs nregs; + + switch(regs->bx) { + case APM_USER_STANDBY_REQ: + DPRINTF(("user wants STANDBY--fat chance\n")); + (void) apm_set_powstate(APM_DEV_ALLDEVS, APM_LASTREQ_REJECTED); + (void) apm_record_event(sc, regs->bx); + apm_userstandbys++; + break; + case APM_STANDBY_REQ: + DPRINTF(("standby requested\n")); + if (apm_standbys || apm_suspends) + DPRINTF(("damn fool BIOS did not wait for answer\n")); + if (apm_record_event(sc, regs->bx)) { + (void) apm_set_powstate(APM_DEV_ALLDEVS, + APM_LASTREQ_INPROG); + apm_standbys++; + } else + (void) apm_set_powstate(APM_DEV_ALLDEVS, + APM_LASTREQ_REJECTED); + break; + case APM_USER_SUSPEND_REQ: + DPRINTF(("user wants suspend--fat chance!\n")); + (void) apm_set_powstate(APM_DEV_ALLDEVS, + APM_LASTREQ_REJECTED); + apm_suspends++; + apm_record_event(sc, regs->bx); + break; + case APM_SUSPEND_REQ: + DPRINTF(("suspend requested\n")); + if (apm_standbys || apm_suspends) + DPRINTF(("damn fool BIOS did not wait for answer\n")); + if (apm_record_event(sc, regs->bx)) { + (void) apm_set_powstate(APM_DEV_ALLDEVS, + APM_LASTREQ_INPROG); + apm_suspends++; + } else + (void) apm_set_powstate(APM_DEV_ALLDEVS, + APM_LASTREQ_REJECTED); + break; + case APM_POWER_CHANGE: + DPRINTF(("power status change\n")); + error = apm_get_powstat(&nregs); + if (error == 0) + apm_power_print(sc, &nregs); + apm_record_event(sc, regs->bx); + break; + case APM_NORMAL_RESUME: + DPRINTF(("system resumed\n")); + inittodr(time.tv_sec); + apm_record_event(sc, regs->bx); + break; + case APM_CRIT_RESUME: + DPRINTF(("system resumed without us!\n")); + inittodr(time.tv_sec); + apm_record_event(sc, regs->bx); + break; + case APM_SYS_STANDBY_RESUME: + DPRINTF(("system standby resume\n")); + inittodr(time.tv_sec); + apm_record_event(sc, regs->bx); + break; + case APM_UPDATE_TIME: + DPRINTF(("update time, please\n")); + inittodr(time.tv_sec); + apm_record_event(sc, regs->bx); + break; + case APM_CRIT_SUSPEND_REQ: + DPRINTF(("suspend required immediately\n")); + apm_record_event(sc, regs->bx); + apm_suspend(); + break; + case APM_BATTERY_LOW: + DPRINTF(("Battery low!\n")); + apm_battlow++; + apm_record_event(sc, regs->bx); + break; + default: + printf("APM nonstandard event code %x\n", regs->bx); + } +} + +STATIC int +apm_get_event(regs) +struct apmregs *regs; +{ + return apmcall(APM_GET_PM_EVENT, regs); +} + +STATIC void +apm_periodic_check(arg) +void *arg; +{ + struct apmregs regs; + struct apm_softc *sc = arg; + while (apm_get_event(®s) == 0) { + apm_event_handle(sc, ®s); + }; + if (APM_ERR_CODE(®s) != APM_ERR_NOEVENTS) + apm_perror("get event", ®s); + if (apm_suspends /*|| (apm_battlow && apm_userstandbys)*/) { + /* stupid TI TM5000! */ + apm_suspend(); + } else if (apm_standbys || apm_userstandbys) { + apm_standby(); + } + apm_suspends = apm_standbys = apm_battlow = apm_userstandbys =0; + timeout(apm_periodic_check, sc, hz); +} + +STATIC void +apm_powmgt_enable(onoff) +int onoff; +{ + struct apmregs regs; + regs.bx = apm_minver == 0 ? APM_MGT_ALL : APM_DEV_APM_BIOS; + regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE; + if (apmcall(APM_PWR_MGT_ENABLE, ®s) != 0) + apm_perror("power management enable", ®s); +} + +STATIC void +apm_powmgt_engage(onoff, dev) +int onoff; +u_int dev; +{ + struct apmregs regs; + if (apm_minver == 0) + return; + regs.bx = dev; + regs.cx = onoff ? APM_MGT_ENGAGE : APM_MGT_DISENGAGE; + if (apmcall(APM_PWR_MGT_ENGAGE, ®s) != 0) + printf("APM power mgmt engage (device %x): %s (%d)\n", + dev, apm_err_translate(APM_ERR_CODE(®s)), + APM_ERR_CODE(®s)); +} + +STATIC void +apm_devpowmgt_enable(onoff, dev) +int onoff; +u_int dev; +{ + struct apmregs regs; + if (apm_minver == 0) + return; + regs.bx = dev; + /* enable is auto BIOS managment. + * disable is program control. + */ + regs.cx = onoff ? APM_MGT_ENABLE : APM_MGT_DISABLE; + if (apmcall(APM_DEVICE_MGMT_ENABLE, ®s) != 0) + printf("APM device engage (device %x): %s (%d)\n", + dev, apm_err_translate(APM_ERR_CODE(®s)), + APM_ERR_CODE(®s)); +} + +int +apm_set_powstate(dev, state) +u_int dev, state; +{ + struct apmregs regs; + if (!apminited || (apm_minver == 0 && state > APM_SYS_OFF)) + return EINVAL; + regs.bx = dev; + regs.cx = state; + if (apmcall(APM_SET_PWR_STATE, ®s) != 0) { + apm_perror("set power state", ®s); + return EIO; + } + return 0; +} + +#ifdef APM_NOIDLE +int apmidleon = 0; +#else +int apmidleon = 1; +#endif + +void +apm_cpu_busy() +{ + struct apmregs regs; + if (!apminited) + return; + if ((apminfo.apm_detail & APM_IDLE_SLOWS) && + apmcall(APM_CPU_BUSY, ®s) != 0) + apm_perror("set CPU busy", ®s); +} + +void +apm_cpu_idle() +{ + struct apmregs regs; + if (!apminited || !apmidleon) + return; + if (apmcall(APM_CPU_IDLE, ®s) != 0) + apm_perror("set CPU idle", ®s); +} + +void *apm_sh; + +#ifdef APM_V10_ONLY +int apm_v11_enabled = 0; +#else +int apm_v11_enabled = 1; +#endif + +STATIC void +apm_set_ver(self) +struct apm_softc *self; +{ + struct apmregs regs; + int error; + + regs.cx = 0x0101; /* APM Version 1.1 */ + regs.bx = APM_DEV_APM_BIOS; + + if (apm_v11_enabled && + (error = apmcall(APM_DRIVER_VERSION, ®s)) == 0) { + apm_majver = APM_CONN_MAJOR(®s); + apm_minver = APM_CONN_MINOR(®s); + } else { + apm_majver = 1; + apm_minver = 0; + } + printf(": Power Management spec V%d.%d", + apm_majver, apm_minver); + apminited = 1; + if (apminfo.apm_detail & APM_IDLE_SLOWS) { +#ifdef DEBUG + /* not relevant much */ + printf(" (slowidle)"); +#endif + apm_dobusy = 1; + } else + apm_dobusy = 0; +#ifdef DIAGNOSTIC + if (apminfo.apm_detail & APM_BIOS_PM_DISABLED) + printf(" (BIOS mgmt disabled)"); + if (apminfo.apm_detail & APM_BIOS_PM_DISENGAGED) + printf(" (BIOS managing devices)"); +#endif + printf("\n"); +} + +STATIC int +apm_get_powstat(regs) +struct apmregs *regs; +{ + regs->bx = APM_DEV_ALLDEVS; + return apmcall(APM_POWER_STATUS, regs); +} + +STATIC void +apm_disconnect(xxx) +void *xxx; +{ + struct apmregs regs; + regs.bx = apm_minver == 1 ? APM_DEV_ALLDEVS : APM_DEFAULTS_ALL; + if (apmcall(APM_SYSTEM_DEFAULTS, ®s)) + apm_perror("system defaults failed", ®s); + + regs.bx = APM_DEV_APM_BIOS; + if (apmcall(APM_DISCONNECT, ®s)) + apm_perror("disconnect failed", ®s); + else + printf("APM disconnected\n"); +} + +int +apmprobe(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct apm_attach_args *aaa = aux; + + if (apminited) + return 0; + if ((apminfo.apm_detail & APM_32BIT_SUPPORTED) && + strcmp(aaa->aaa_busname, "apm") == 0) { + return 1; + } + return 0; +} + +void +apmattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct apm_softc *apmsc = (void *)self; + struct apmregs regs; + int error; + /* + * set up GDT descriptors for APM + */ + if (apminfo.apm_detail & APM_32BIT_SUPPORTED) { + apminfo.apm_segsel = GSEL(GAPM32CODE_SEL,SEL_KPL); + apminfo.apm_code32_seg_base <<= 4; + apminfo.apm_code16_seg_base <<= 4; + apminfo.apm_data_seg_base <<= 4; + /* something is still amiss in the limit-fetch in the boot + loader; it returns incorrect (too small) limits. + for now, force them to max size. */ + apminfo.apm_code32_seg_len = 65536; + apminfo.apm_data_seg_len = 65536; +#if 0 + switch ((APM_MAJOR_VERS(apminfo.apm_detail) << 8) + + APM_MINOR_VERS(apminfo.apm_detail)) { + case 0x0100: + apminfo.apm_code32_seg_len = 65536; + apminfo.apm_data_seg_len = 65536; + break; + default: + if (apminfo.apm_data_seg_len == 0) + apminfo.apm_data_seg_len = 65536; + break; + } +#endif + setsegment(&dynamic_gdt[GAPM32CODE_SEL].sd, + (void *)ISA_HOLE_VADDR(apminfo.apm_code32_seg_base), + apminfo.apm_code32_seg_len-1, + SDT_MEMERA, SEL_KPL, 1, 0); + setsegment(&dynamic_gdt[GAPM16CODE_SEL].sd, + (void *)ISA_HOLE_VADDR(apminfo.apm_code16_seg_base), + 65536-1, /* just in case */ + SDT_MEMERA, SEL_KPL, 0, 0); + setsegment(&dynamic_gdt[GAPMDATA_SEL].sd, + (void *)ISA_HOLE_VADDR(apminfo.apm_data_seg_base), + apminfo.apm_data_seg_len-1, + SDT_MEMRWA, SEL_KPL, 1, 0); +#if defined(DEBUG) || defined(APMDEBUG) + printf(": detail %x 32b:%x/%x/%x 16b:%x/%x data %x/%x/%x ep %x (%x:%x)\n%s", + apminfo.apm_detail, + apminfo.apm_code32_seg_base, + ISA_HOLE_VADDR(apminfo.apm_code32_seg_base), + apminfo.apm_code32_seg_len, + apminfo.apm_code16_seg_base, + ISA_HOLE_VADDR(apminfo.apm_code16_seg_base), + apminfo.apm_data_seg_base, + ISA_HOLE_VADDR(apminfo.apm_data_seg_base), + apminfo.apm_data_seg_len, + apminfo.apm_entrypt, + apminfo.apm_segsel, + apminfo.apm_entrypt+ISA_HOLE_VADDR(apminfo.apm_code32_seg_base), + apmsc->sc_dev.dv_xname); +#endif + apm_set_ver(apmsc); + /* + * Engage cooperative power mgt (we get to do it) + * on all devices (v1.1). + */ + apm_powmgt_engage(1, APM_DEV_ALLDEVS); +#if 0 + /* doesn't seem to work, sigh. */ + apm_powmgt_engage(1, APM_DEV_DISPLAY(APM_DEV_ALLUNITS)); + apm_powmgt_engage(1, APM_DEV_DISK(APM_DEV_ALLUNITS)); + apm_powmgt_engage(1, APM_DEV_PARALLEL(APM_DEV_ALLUNITS)); + apm_powmgt_engage(1, APM_DEV_NETWORK(APM_DEV_ALLUNITS)); + apm_powmgt_engage(1, APM_DEV_PCMCIA(APM_DEV_ALLUNITS)); +#endif + error = apm_get_powstat(®s); + if (error == 0) { + apm_power_print(apmsc, ®s); + } else + apm_perror("get power status", ®s); + apm_cpu_busy(); + apm_periodic_check(apmsc); + } else { + dynamic_gdt[GAPM32CODE_SEL] = dynamic_gdt[GNULL_SEL]; + dynamic_gdt[GAPM16CODE_SEL] = dynamic_gdt[GNULL_SEL]; + dynamic_gdt[GAPMDATA_SEL] = dynamic_gdt[GNULL_SEL]; + } +} + +int +apmopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = APMUNIT(dev); + int ctl = APMDEV(dev); + struct apm_softc *sc; + + if (unit >= apm_cd.cd_ndevs) + return ENXIO; + sc = apm_cd.cd_devs[unit]; + if (!sc) + return ENXIO; + + switch (ctl) { + case APMDEV_CTL: + if (!(flag & FWRITE)) + return EINVAL; + if (sc->sc_flags & SCFLAG_OWRITE) + return EBUSY; + sc->sc_flags |= SCFLAG_OWRITE; + break; + case APMDEV_NORMAL: + if (!(flag & FREAD) || (flag & FWRITE)) + return EINVAL; + sc->sc_flags |= SCFLAG_OREAD; + break; + default: + return ENXIO; + break; + } + return 0; +} + +int +apmclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)]; + int ctl = APMDEV(dev); + + DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode)); + switch (ctl) { + case APMDEV_CTL: + sc->sc_flags &= ~SCFLAG_OWRITE; + break; + case APMDEV_NORMAL: + sc->sc_flags &= ~SCFLAG_OREAD; + break; + } + if ((sc->sc_flags & SCFLAG_OPEN) == 0) { + sc->event_count = 0; + sc->event_ptr = 0; + } + return 0; +} + +int +apmioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int error; + struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)]; + struct apm_power_info *powerp; + struct apm_event_info *evp; + struct apmregs regs; + register int i; + struct apm_ctl *actl; + + switch (cmd) { + /* some ioctl names from linux */ + case APM_IOC_STANDBY: + if ((flag & FWRITE) == 0) + return EBADF; + apm_userstandbys++; + return 0; + case APM_IOC_SUSPEND: + if ((flag & FWRITE) == 0) + return EBADF; + apm_suspends++; + return 0; + case APM_IOC_DEV_CTL: + actl = (struct apm_ctl *)data; + if ((flag & FWRITE) == 0) + return EBADF; + apm_get_powstate(actl->dev); /* XXX */ + return apm_set_powstate(actl->dev, actl->mode); + case APM_IOC_NEXTEVENT: + if (sc->event_count) { + evp = (struct apm_event_info *)data; + i = sc->event_ptr + APM_NEVENTS - sc->event_count; + i %= APM_NEVENTS; + *evp = sc->event_list[i]; + sc->event_count--; + return 0; + } else + return EAGAIN; + case APM_IOC_GETPOWER: + powerp = (struct apm_power_info *)data; + error = apm_get_powstat(®s); + if (error == 0) { + bzero(powerp, sizeof(*powerp)); + if (BATT_LIFE(®s) != APM_BATT_LIFE_UNKNOWN) + powerp->battery_life = BATT_LIFE(®s); + powerp->ac_state = AC_STATE(®s); + switch (apm_minver) { + case 0: + powerp->battery_state = BATT_STATE(®s); + break; + case 1: + default: + powerp->battery_state = APM_BATT_UNKNOWN; + if (BATT_FLAGS(®s) & APM_BATT_FLAG_HIGH) + powerp->battery_state = APM_BATT_HIGH; + else if (BATT_FLAGS(®s) & APM_BATT_FLAG_LOW) + powerp->battery_state = APM_BATT_LOW; + else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CRITICAL) + powerp->battery_state = APM_BATT_CRITICAL; + else if (BATT_FLAGS(®s) & APM_BATT_FLAG_CHARGING) + powerp->battery_state = APM_BATT_CHARGING; + else if (BATT_FLAGS(®s) & APM_BATT_FLAG_NOBATTERY) + powerp->battery_state = APM_BATTERY_ABSENT; + if (BATT_REM_VALID(®s)) + powerp->minutes_left = BATT_REMAINING(®s); + } + } else { + apm_perror("ioctl get power status", ®s); + error = EIO; + } + break; + + default: + return ENOTTY; + } + return 0; +} + +int +apmselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + struct apm_softc *sc = apm_cd.cd_devs[APMUNIT(dev)]; + + switch (rw) { + case FREAD: + if (sc->event_count) + return 1; + selrecord(p, &sc->sc_rsel); + break; + case FWRITE: + case 0: + return 0; + } + return 0; +} + +#endif /* NAPM > 0 */ diff --git a/sys/arch/i386/i386/conf.c b/sys/arch/i386/i386/conf.c index c56ad7712fd..9b68b21902b 100644 --- a/sys/arch/i386/i386/conf.c +++ b/sys/arch/i386/i386/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.12 1996/04/21 22:16:23 deraadt Exp $ */ +/* $OpenBSD: conf.c,v 1.13 1996/04/29 14:12:41 hvozda Exp $ */ /* $NetBSD: conf.c,v 1.74 1996/03/30 07:30:33 mycroft Exp $ */ /* @@ -124,6 +124,13 @@ int nblkdev = sizeof(bdevsw) / sizeof(bdevsw[0]); (dev_type_mmap((*))) enodev } #define cdev_joy_init cdev_ss_init +/* open, close, ioctl, select -- XXX should be a generic device */ +#define cdev_ocis_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ + (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ + (dev_type_stop((*))) enodev, 0, dev_init(c,n,select), \ + (dev_type_mmap((*))) enodev, 0 } + cdev_decl(cn); cdev_decl(ctty); #define mmread mmrw @@ -161,6 +168,8 @@ cdev_decl(ch); dev_decl(filedesc,open); #include "bpfilter.h" cdev_decl(bpf); +#include "pcmcia.h" +cdev_decl(pcmcia); #include "spkr.h" cdev_decl(spkr); #ifdef LKM @@ -187,6 +196,8 @@ cdev_decl(svr4_net); cdev_decl(ccd); #include "joy.h" cdev_decl(joy); +#include "apm.h" +cdev_decl(apm); #include "rnd.h" cdev_decl(rnd); @@ -222,11 +233,11 @@ struct cdevsw cdevsw[] = cdev_disk_init(NCCD,ccd), /* 18: concatenated disk driver */ cdev_ss_init(NSS,ss), /* 19: SCSI scanner */ cdev_notdef(), /* 20 */ - cdev_notdef(), /* 21 */ + cdev_ocis_init(NAPM,apm), /* 21: Advancded Power Management */ cdev_fd_init(1,filedesc), /* 22: file descriptor pseudo-device */ cdev_bpftun_init(NBPFILTER,bpf),/* 23: Berkeley packet filter */ cdev_notdef(), /* 24 */ - cdev_notdef(), /* 25 */ + cdev_ocis_init(NPCMCIA,pcmcia), /* 25: PCMCIA Bus */ cdev_joy_init(NJOY,joy), /* 26: joystick */ cdev_spkr_init(NSPKR,spkr), /* 27: PC speaker */ cdev_lkm_init(NLKM,lkm), /* 28: loadable module driver */ diff --git a/sys/arch/i386/i386/gdt.c b/sys/arch/i386/i386/gdt.c index a511ab550b7..37361fba4a2 100644 --- a/sys/arch/i386/i386/gdt.c +++ b/sys/arch/i386/i386/gdt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gdt.c,v 1.7 1996/04/18 19:18:08 niklas Exp $ */ +/* $OpenBSD: gdt.c,v 1.8 1996/04/29 14:12:48 hvozda Exp $ */ /* $NetBSD: gdt.c,v 1.7 1996/02/27 22:45:01 jtc Exp $ */ /*- @@ -214,6 +214,16 @@ gdt_get_slot() if (gdt_next != gdt_count) panic("gdt_get_slot botch 1"); if (gdt_next >= gdt_size) { + /* + * gdt_size is clamped by maxproc, set in + * /sys/conf/param.c and clamped in init386(). + * It's held there to (MAXGDTSIZ - NGDT) if no + * user LDTs, or half that if user LDTs are + * allowed. It's important to count that + * correctly, because by the time we get here, + * it's too late to abort the fork operation + * -- we must have a GDT slot available. + */ if (gdt_size >= MAXGDTSIZ) panic("gdt_get_slot botch 2"); if (dynamic_gdt == gdt) diff --git a/sys/arch/i386/i386/genassym.c b/sys/arch/i386/i386/genassym.c index d3e01c4094d..8bba187b0d9 100644 --- a/sys/arch/i386/i386/genassym.c +++ b/sys/arch/i386/i386/genassym.c @@ -51,6 +51,11 @@ #include <machine/pmap.h> #include <machine/vmparam.h> +#include "apm.h" +#if NAPM > 0 +#include <machine/apmvar.h> +#endif + #ifdef COMPAT_SVR4 #include <compat/svr4/svr4_ucontext.h> #endif @@ -150,6 +155,21 @@ main() off("IH_COUNT", struct intrhand, ih_count); off("IH_NEXT", struct intrhand, ih_next); #endif +#if NAPM > 0 + off("APM_CODE32", struct apm_connect_info, apm_code32_seg_base); + off("APM_CODE16", struct apm_connect_info, apm_code16_seg_base); + off("APM_DATA", struct apm_connect_info, apm_data_seg_base); + off("APM_CODE32_LEN", struct apm_connect_info, apm_code32_seg_len); + off("APM_DATA_LEN", struct apm_connect_info, apm_data_seg_len); + off("APM_ENTRY", struct apm_connect_info, apm_entrypt); + off("APM_DETAIL", struct apm_connect_info, apm_detail); + off("APM_CALL", struct apm_connect_info, apm_entrypt); + def("APM_SIZE", sizeof(struct apm_connect_info)); + off("APMREG_AX", struct apmregs, ax); + off("APMREG_BX", struct apmregs, bx); + off("APMREG_CX", struct apmregs, cx); + off("APMREG_DX", struct apmregs, dx); +#endif exit(0); } diff --git a/sys/arch/i386/i386/locore.s b/sys/arch/i386/i386/locore.s index e7af2889ce0..65f33efa92e 100644 --- a/sys/arch/i386/i386/locore.s +++ b/sys/arch/i386/i386/locore.s @@ -41,6 +41,7 @@ #include "npx.h" #include "assym.h" +#include "apm.h" #include <sys/errno.h> #include <sys/syscall.h> @@ -142,6 +143,16 @@ .globl _cpu,_cpu_vendor,_cold,_esym,_boothowto,_bootdev,_atdevbase .globl _cyloffset,_proc0paddr,_curpcb,_PTDpaddr,_dynamic_gdt +#if NAPM > 0 +#include <machine/apmvar.h> + .globl _apminfo + .globl _apm_current_gdt_pdesc /* current GDT pseudo desc. */ + .globl _bootstrap_gdt +_apm_current_gdt_pdesc: + .word 0, 0, 0 +_bootstrap_gdt: + .space SIZEOF_GDTE * BOOTSTRAP_GDT_NUM +#endif _cpu: .long 0 # are we 386, 386sx, or 486 _cpu_vendor: .space 16 # vendor string returned by `cpuid' instruction _cold: .long 1 # cold till we are not @@ -178,6 +189,102 @@ start: movw $0x1234,0x472 # warm boot addl $KERNBASE,%eax 1: movl %eax,RELOC(_esym) +#if NAPM > 0 + + /* + * Setup APM BIOS: + * + * APM BIOS initialization should be done from real mode or V86 mode. + * + * (by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>) + */ + + /* + * Cleanup %fs and %gs: + * + * Some BIOS bootstrap routine store junk value into %fs + * and %gs. + */ + + xorl %eax, %eax + movw %ax, %fs + movw %ax, %gs + + /* get GDT base */ + sgdt RELOC(_apm_current_gdt_pdesc) + + /* copy GDT to _bootstrap_gdt */ + xorl %ecx, %ecx + movw RELOC(_apm_current_gdt_pdesc), %cx + movl RELOC(_apm_current_gdt_pdesc)+2, %esi + lea RELOC(_bootstrap_gdt), %edi + cld + rep + movsb + + /* setup GDT pseudo descriptor */ + movw $(SIZEOF_GDTE*BOOTSTRAP_GDT_NUM), %ax + movw %ax, RELOC(_apm_current_gdt_pdesc) + leal RELOC(_bootstrap_gdt), %eax + movl %eax, RELOC(_apm_current_gdt_pdesc)+2 + + /* load new GDTR */ + lgdt RELOC(_apm_current_gdt_pdesc) + + /* + * Copy APM initializer under 1MB boundary: + * + * APM initializer program must switch the CPU to real mode. + * But NetBSD kernel runs above 1MB boundary. So we must + * copy the initializer code to conventional memory. + */ + movl RELOC(_apm_init_image_size), %ecx /* size */ + lea RELOC(_apm_init_image), %esi /* source */ + movl $ APM_OURADDR, %edi /* destination */ + cld + rep + movsb + + /* setup GDT for APM initializer */ + lea RELOC(_bootstrap_gdt), %ecx + movl $(APM_OURADDR), %eax /* use %ax for 15..0 */ + movl %eax, %ebx + shrl $16, %ebx /* use %bl for 23..16 */ + /* use %bh for 31..24 */ +#define APM_SETUP_GDT(index, attrib) \ + movl $(index), %si ; \ + lea 0(%ecx,%esi,8), %edx ; \ + movw $0xffff, (%edx) ; \ + movw %ax, 2(%edx) ; \ + movb %bl, 4(%edx) ; \ + movw $(attrib), 5(%edx) ; \ + movb %bh, 7(%edx) + + APM_SETUP_GDT(APM_INIT_CS_INDEX , CS32_ATTRIB) + APM_SETUP_GDT(APM_INIT_DS_INDEX , DS32_ATTRIB) + APM_SETUP_GDT(APM_INIT_CS16_INDEX, CS16_ATTRIB) + + /* + * Call the initializer: + * + * direct intersegment call to conventional memory code + */ + .byte 0x9a /* actually, lcall $APM_INIT_CS_SEL, $0 */ + .long 0 + .word APM_INIT_CS_SEL + + movw %ax,RELOC(_apminfo+APM_DETAIL) + movw %di,RELOC(_apminfo+APM_DETAIL)+2 + movl %ebx,RELOC(_apminfo+APM_ENTRY) + movw %cx,RELOC(_apminfo+APM_CODE32) + shrl $16, %ecx + movw %cx,RELOC(_apminfo+APM_CODE16) + movw %dx,RELOC(_apminfo+APM_DATA) + movw %si,RELOC(_apminfo+APM_CODE32_LEN) + shrl $16, %esi + movw %si,RELOC(_apminfo+APM_DATA_LEN) +#endif /* APM */ + /* First, reset the PSL. */ pushl $PSL_MBO popfl @@ -363,7 +470,7 @@ try586: /* Use the `cpuid' instruction. */ #endif /* Calculate where to start the bootstrap tables. */ - movl %edi,%esi # edi = esym ?: end + movl %edi,%esi # edi = esym ? esym : end addl $PGOFSET,%esi # page align up andl $~PGOFSET,%esi @@ -1481,6 +1588,9 @@ ENTRY(remrq) 3: .asciz "remrq" #endif /* DIAGNOSTIC */ +#if NAPM > 0 + .globl _apm_cpu_idle,_apm_cpu_busy,_apm_dobusy +#endif /* * When no processes are on the runq, cpu_switch() branches to here to wait for * something to come ready. @@ -1491,7 +1601,16 @@ ENTRY(idle) testl %ecx,%ecx jnz sw1 sti +#if NAPM > 0 + call _apm_cpu_idle +#endif hlt +#if NAPM > 0 + cmpl $0,_apm_dobusy + je 1f + call _apm_cpu_busy +1: +#endif jmp _idle #ifdef DIAGNOSTIC @@ -2075,3 +2194,78 @@ ENTRY(bzero) popl %edi ret + +#if NAPM > 0 +/* + * int apmcall(int function, struct apmregs *regs): + * call the APM protected mode bios function FUNCTION for BIOS selection + * WHICHBIOS. + * Fills in *regs with registers as returned by APM. + * returns nonzero if error returned by APM. + */ +apmstatus: .long 0 +ENTRY(apmcall) + pushl %ebp + movl %esp,%ebp + pushl %esi + pushl %edi + pushl %ebx + +#if defined(DEBUG) || defined(DIAGNOSTIC) + pushl %ds + pushl %es + pushl %fs + pushl %gs + xorl %ax,%ax +/* movl %ax,%ds # can't toss %ds, we need it for apmstatus*/ + movl %ax,%es + movl %ax,%fs + movl %ax,%gs +#endif + movb %cs:8(%ebp),%al + movb $0x53,%ah + movl %cs:12(%ebp),%ebx + movw %cs:APMREG_CX(%ebx),%cx + movw %cs:APMREG_DX(%ebx),%dx + movw %cs:APMREG_BX(%ebx),%bx + pushfl + cli + pushl %ds + lcall %cs:(_apminfo+APM_CALL) + popl %ds + setc apmstatus + popfl +#if defined(DEBUG) || defined(DIAGNOSTIC) + popl %gs + popl %fs + popl %es + popl %ds # see above +#endif + movl 12(%ebp),%esi + movw %ax,APMREG_AX(%esi) + movw %bx,APMREG_BX(%esi) + movw %cx,APMREG_CX(%esi) + movw %dx,APMREG_DX(%esi) +/* todo: do something with %edi? */ + cmpl $0,apmstatus + jne 1f + xorl %eax,%eax +1: + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + +_apm_init_image: + .globl _apm_init_image + +8: +#include "lib/apm_init/apm_init.inc" +9: + +_apm_init_image_size: + .globl _apm_init_image_size + .long 9b - 8b + +#endif /* APM */ diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c index e3bdb4ad24b..27438f0608e 100644 --- a/sys/arch/i386/i386/machdep.c +++ b/sys/arch/i386/i386/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.13 1996/04/21 22:16:31 deraadt Exp $ */ +/* $OpenBSD: machdep.c,v 1.14 1996/04/29 14:13:15 hvozda Exp $ */ /* $NetBSD: machdep.c,v 1.197 1996/04/12 08:44:40 mycroft Exp $ */ /*- @@ -93,6 +93,12 @@ #include <i386/isa/isa_machdep.h> #include <i386/isa/nvram.h> +#include "apm.h" + +#if NAPM > 0 +#include <machine/apmvar.h> +#endif + #ifdef VM86 #include <machine/vm86.h> #endif @@ -736,6 +742,16 @@ haltsys: doshutdownhooks(); if (howto & RB_HALT) { +#if NAPM > 0 && !defined(APM_NO_POWEROFF) + /* turn off, if we can. But try to turn disk off and + * wait a bit first--some disk drives are slow to clean up + * and users have reported disk corruption. + */ + delay(500000); + apm_set_powstate(APM_DEV_DISK(0xff), APM_SYS_OFF); + delay(500000); + apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_OFF); +#endif printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); @@ -1169,6 +1185,16 @@ init386(first_avail) /* call pmap initialization to make new kernel address space */ pmap_bootstrap((vm_offset_t)atdevbase + IOM_SIZE); +#ifdef USER_LDT +#define MAXPROC ((MAXGDTSIZ-NGDT)/2) +#else +#define MAXPROC (MAXGDTSIZ-NGDT) +#endif + if (maxproc > MAXPROC) { + printf("reducing maxproc to %d to fit into gdt\n", MAXPROC); + maxproc = MAXPROC; + } + #ifdef DDB ddb_init(); if (boothowto & RB_KDB) diff --git a/sys/arch/i386/i386/mainbus.c b/sys/arch/i386/i386/mainbus.c index 5de4b476180..c20a774c307 100644 --- a/sys/arch/i386/i386/mainbus.c +++ b/sys/arch/i386/i386/mainbus.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mainbus.c,v 1.2 1996/04/21 22:16:32 deraadt Exp $ */ +/* $OpenBSD: mainbus.c,v 1.3 1996/04/29 14:13:25 hvozda Exp $ */ /* $NetBSD: mainbus.c,v 1.8 1996/04/11 22:13:37 cgd Exp $ */ /* @@ -45,6 +45,11 @@ #include <i386/isa/isa_machdep.h> #include "pci.h" +#include "apm.h" + +#if NAPM > 0 +#include <machine/apmvar.h> +#endif int mainbus_match __P((struct device *, void *, void *)); void mainbus_attach __P((struct device *, struct device *, void *)); @@ -64,6 +69,9 @@ union mainbus_attach_args { struct pcibus_attach_args mba_pba; struct eisabus_attach_args mba_eba; struct isabus_attach_args mba_iba; +#if NAPM > 0 + struct apm_attach_args mba_aaa; +#endif }; /* @@ -118,7 +126,12 @@ mainbus_attach(parent, self, aux) config_found(self, &mba.mba_pba, mainbus_print); } #endif - +#if NAPM > 0 + { + mba.mba_aaa.aaa_busname = "apm"; + config_found(self, &mba.mba_aaa, mainbus_print); + } +#endif } int diff --git a/sys/arch/i386/include/apmvar.h b/sys/arch/i386/include/apmvar.h new file mode 100644 index 00000000000..75dfd2c155d --- /dev/null +++ b/sys/arch/i386/include/apmvar.h @@ -0,0 +1,263 @@ +/* $NetBSD$ */ +/* + * 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. + * + */ +#ifndef __I386_APM_H__ +#define __I386_APM_H__ + +/* Advanced Power Management (v1.0 and v1.1 specification) + * functions/defines/etc. + */ +#define APM_BIOS_FNCODE (0x53) +#define APM_SYSTEM_BIOS (0x15) +#define APM_BIOS_FN(x) ((APM_BIOS_FNCODE<<8)|(x)) +/* + * APM info word from boot loader + */ +#define APM_MAJOR_VERS(info) (((info)&0xff00)>>8) +#define APM_MINOR_VERS(info) ((info)&0xff) +#define APM_16BIT_SUPPORTED 0x00010000 +#define APM_32BIT_SUPPORTED 0x00020000 +#define APM_IDLE_SLOWS 0x00040000 +#define APM_BIOS_PM_DISABLED 0x00080000 +#define APM_BIOS_PM_DISENGAGED 0x00100000 + +#define APM_ERR_CODE(regs) (((regs)->ax & 0xff00) >> 8) +#define APM_ERR_PM_DISABLED 0x01 +#define APM_ERR_REALALREADY 0x02 +#define APM_ERR_NOTCONN 0x03 +#define APM_ERR_16ALREADY 0x05 +#define APM_ERR_16NOTSUPP 0x06 +#define APM_ERR_32ALREADY 0x07 +#define APM_ERR_32NOTSUPP 0x08 +#define APM_ERR_UNRECOG_DEV 0x09 +#define APM_ERR_ERANGE 0x0A +#define APM_ERR_NOTENGAGED 0x0B +#define APM_ERR_UNABLE 0x60 +#define APM_ERR_NOEVENTS 0x80 +#define APM_ERR_NOT_PRESENT 0x86 + +#define APM_DEV_APM_BIOS 0x0000 +#define APM_DEV_ALLDEVS 0x0001 +/* device classes are high byte; device IDs go in low byte */ +#define APM_DEV_DISPLAY(x) (0x0100|((x)&0xff)) +#define APM_DEV_DISK(x) (0x0200|((x)&0xff)) +#define APM_DEV_PARALLEL(x) (0x0300|((x)&0xff)) +#define APM_DEV_SERIAL(x) (0x0400|((x)&0xff)) +#define APM_DEV_NETWORK(x) (0x0500|((x)&0xff)) +#define APM_DEV_PCMCIA(x) (0x0600|((x)&0xff)) +#define APM_DEV_ALLUNITS 0xff + +#define APM_INSTALLATION_CHECK 0x00 /* int15 only */ +#define APM_REALMODE_CONNECT 0x01 /* int15 only */ +#define APM_16BIT_CONNECT 0x02 /* int15 only */ +#define APM_32BIT_CONNECT 0x03 /* int15 only */ +#define APM_DISCONNECT 0x04 /* %bx = APM_DEV_APM_BIOS */ +#define APM_CPU_IDLE 0x05 +#define APM_CPU_BUSY 0x06 +#define APM_SET_PWR_STATE 0x07 +#define APM_SYS_READY 0x0000 /* %cx */ +#define APM_SYS_STANDBY 0x0001 +#define APM_SYS_SUSPEND 0x0002 +#define APM_SYS_OFF 0x0003 +#define APM_LASTREQ_INPROG 0x0004 +#define APM_LASTREQ_REJECTED 0x0005 + +/* system standby is device ID (%bx) 0x0001, APM_SYS_STANDBY */ +/* system suspend is device ID (%bx) 0x0001, APM_SYS_SUSPEND */ + +#define APM_PWR_MGT_ENABLE 0x08 +#define APM_MGT_ALL 0xffff /* %bx */ +#define APM_MGT_DISABLE 0x0 /* %cx */ +#define APM_MGT_ENABLE 0x1 + +#define APM_SYSTEM_DEFAULTS 0x09 +#define APM_DEFAULTS_ALL 0xffff /* %bx */ + +#define APM_POWER_STATUS 0x0a +#define APM_AC_OFF 0x00 +#define APM_AC_ON 0x01 +#define APM_AC_BACKUP 0x02 +#define APM_AC_UNKNOWN 0xff +#define APM_BATT_HIGH 0x00 +#define APM_BATT_LOW 0x01 +#define APM_BATT_CRITICAL 0x02 +#define APM_BATT_CHARGING 0x03 +#define APM_BATT_UNKNOWN 0xff +#define APM_BATT_FLAG_HIGH 0x01 +#define APM_BATT_FLAG_LOW 0x02 +#define APM_BATT_FLAG_CRITICAL 0x04 +#define APM_BATT_FLAG_CHARGING 0x08 +#define APM_BATT_FLAG_NOBATTERY 0x80 +#define APM_BATT_LIFE_UNKNOWN 0xff +#define BATT_STATE(regp) ((regp)->bx & 0xff) +#define BATT_FLAGS(regp) (((regp)->cx & 0xff00) >> 8) +#define AC_STATE(regp) (((regp)->bx & 0xff00) >> 8) +#define BATT_LIFE(regp) ((regp)->cx & 0xff) /* in % */ +#define BATT_REMAINING(regp) (((regp)->dx & 0x8000) ? \ + ((regp)->dx & 0x7fff)*60 : \ + ((regp)->dx & 0x7fff)) +#define BATT_REM_VALID(regp) (((regp)->dx & 0xffff) != 0xffff) +#define APM_GET_PM_EVENT 0x0b +#define APM_STANDBY_REQ 0x0001 /* %bx on return */ +#define APM_SUSPEND_REQ 0x0002 +#define APM_NORMAL_RESUME 0x0003 +#define APM_CRIT_RESUME 0x0004 /* suspend/resume happened + without us */ +#define APM_BATTERY_LOW 0x0005 +#define APM_POWER_CHANGE 0x0006 +#define APM_UPDATE_TIME 0x0007 +#define APM_CRIT_SUSPEND_REQ 0x0008 +#define APM_USER_STANDBY_REQ 0x0009 +#define APM_USER_SUSPEND_REQ 0x000A +#define APM_SYS_STANDBY_RESUME 0x000B + +#define APM_GET_POWER_STATE 0x0c +#define APM_DEVICE_MGMT_ENABLE 0x0d + +#define APM_DRIVER_VERSION 0x0e +/* %bx should be DEV value (APM_DEV_APM_BIOS) + %ch = driver major vno + %cl = driver minor vno + return: %ah = conn major; %al = conn minor + */ +#define APM_CONN_MINOR(regp) ((regp)->ax & 0xff) +#define APM_CONN_MAJOR(regp) (((regp)->ax & 0xff00) >> 8) + +#define APM_PWR_MGT_ENGAGE 0x0F +#define APM_MGT_DISENGAGE 0x0 /* %cx */ +#define APM_MGT_ENGAGE 0x1 + +#define APM_OEM 0x80 + +#ifdef _LOCORE +/* + * LP (Laptop Package) + * + * Copyright (C) 1994 by HOSOKAWA Tatsumi <hosokawa@mt.cs.keio.ac.jp> + * + * This software may be used, modified, copied, and distributed, in + * both source and binary form provided that the above copyright and + * these terms are retained. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with its + * use. + * + * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) + */ + +/* Error code of APM initializer */ +#define APMINI_CANTFIND 0xffffffff +#define APMINI_NOT32BIT 0xfffffffe +#define APMINI_CONNECTERR 0xfffffffd + +#define SIZEOF_GDTE 8 +#define BOOTSTRAP_GDT_NUM 9 /* see i386/boot/table.c */ + +#define APM_INIT_CS_INDEX (BOOTSTRAP_GDT_NUM - 3) +#define APM_INIT_DS_INDEX (BOOTSTRAP_GDT_NUM - 2) +#define APM_INIT_CS16_INDEX (BOOTSTRAP_GDT_NUM - 1) +#define APM_INIT_CS_SEL (APM_INIT_CS_INDEX << 3) +#define APM_INIT_DS_SEL (APM_INIT_DS_INDEX << 3) +#define APM_INIT_CS16_SEL (APM_INIT_CS16_INDEX << 3) + +#define CS32_ATTRIB 0xCF9e +#define CS16_ATTRIB 0x0F9e +#define DS32_ATTRIB 0xCF92 + +#define BOOTSTRAP_DS_SEL 0x10 +/* APM initializer physical address */ +#define APM_OURADDR 0x00080000 +#define APM_RELOC(x) ((x) - _apm_init_image) + +#else /* !_LOCORE */ + +/* filled in by apmcall */ +struct apmregs { + u_short ax; + u_short bx; + u_short cx; + u_short dx; +}; + +struct apm_connect_info { + u_int apm_code32_seg_base; /* real-mode style segment selector */ + u_int apm_code16_seg_base; + u_int apm_data_seg_base; + u_int apm_entrypt; + u_short apm_segsel; /* segment selector for APM */ + u_short _pad1; + u_int apm_code32_seg_len; + u_int apm_data_seg_len; + u_int apm_detail; +}; + +struct apm_event_info { + u_int type; + u_int index; + u_int spare[8]; +}; + +#define APM_BATTERY_ABSENT 4 + +struct apm_power_info { + u_char battery_state; + u_char ac_state; + u_char battery_life; + u_char spare1; + u_int minutes_left; /* estimate */ + u_int spare2[6]; +}; + +struct apm_ctl { + u_int dev; + u_int mode; +}; + +#define APM_IOC_REJECT _IOW('A', 0, struct apm_event_info) /* reject request # */ +#define APM_IOC_STANDBY _IO('A', 1) /* put system into standby */ +#define APM_IOC_SUSPEND _IO('A', 2) /* put system into suspend */ +#define APM_IOC_GETPOWER _IOR('A', 3, struct apm_power_info) /* fetch battery state */ +#define APM_IOC_NEXTEVENT _IOR('A', 4, struct apm_event_info) /* fetch event */ +#define APM_IOC_DEV_CTL _IOW('A', 5, struct apm_ctl) /* put device into mode */ + +struct apm_attach_args { + char *aaa_busname; +}; + +#ifdef _KERNEL +extern struct apm_connect_info apminfo; /* in locore */ +extern int apmpresent; +extern int apmcall __P((int function, struct apmregs *regs)); +extern void apm_cpu_busy __P((void)); +extern void apm_cpu_idle __P((void)); +extern void apminit __P((void)); +int apm_set_powstate __P((u_int devid, u_int powstate)); +#endif /* _KERNEL */ +#endif /* _LOCORE */ +#endif /* __i386_apm_h__ */ diff --git a/sys/arch/i386/include/gdt.h b/sys/arch/i386/include/gdt.h index ebb8d5b0d57..7b7a6ea5ac5 100644 --- a/sys/arch/i386/include/gdt.h +++ b/sys/arch/i386/include/gdt.h @@ -1,4 +1,4 @@ -/* $OpenBSD: gdt.h,v 1.2 1996/04/18 19:21:37 niklas Exp $ */ +/* $OpenBSD: gdt.h,v 1.3 1996/04/29 14:13:48 hvozda Exp $ */ /* $NetBSD: gdt.h,v 1.3 1996/02/27 22:32:11 jtc Exp $ */ /*- @@ -37,6 +37,9 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define MAXGDTSIZ 8192 /* max # entries in an i386 GDT */ +extern union descriptor *dynamic_gdt; + void tss_alloc __P((struct pcb *)); void tss_free __P((struct pcb *)); void ldt_alloc __P((struct pcb *, union descriptor *, size_t)); diff --git a/sys/arch/i386/include/segments.h b/sys/arch/i386/include/segments.h index a1a332e23f3..4558ccce6aa 100644 --- a/sys/arch/i386/include/segments.h +++ b/sys/arch/i386/include/segments.h @@ -217,7 +217,10 @@ void setsegment __P((struct segment_descriptor *, void *, size_t, int, int, #define GLDT_SEL 3 /* Default LDT descriptor */ #define GUCODE_SEL 4 /* User code descriptor */ #define GUDATA_SEL 5 /* User data descriptor */ -#define NGDT 6 +#define GAPM32CODE_SEL 6 +#define GAPM16CODE_SEL 7 +#define GAPMDATA_SEL 8 +#define NGDT 9 /* * Entries in the Local Descriptor Table (LDT) diff --git a/sys/dev/ic/com.c b/sys/dev/ic/com.c index 77dd24748f4..5570f7ef424 100644 --- a/sys/dev/ic/com.c +++ b/sys/dev/ic/com.c @@ -1,4 +1,4 @@ -/* $OpenBSD: com.c,v 1.11 1996/04/21 22:23:15 deraadt Exp $ */ +/* $OpenBSD: com.c,v 1.12 1996/04/29 14:16:15 hvozda Exp $ */ /* $NetBSD: com.c,v 1.79 1996/04/15 18:54:31 cgd Exp $ */ /*- @@ -81,6 +81,7 @@ struct com_softc { struct device sc_dev; void *sc_ih; + bus_chipset_tag_t sc_bc; struct tty *sc_tty; int sc_overflows; @@ -94,7 +95,6 @@ struct com_softc { int sc_hayespbase; #endif - bus_chipset_tag_t sc_bc; bus_io_handle_t sc_ioh; bus_io_handle_t sc_hayespioh; @@ -102,6 +102,9 @@ struct com_softc { #define COM_HW_NOIEN 0x01 #define COM_HW_FIFO 0x02 #define COM_HW_HAYESP 0x04 +#define COM_HW_ABSENT_PENDING 0x08 /* reattached, awaiting close/reopen */ +#define COM_HW_ABSENT 0x10 /* configure actually failed, or removed */ +#define COM_HW_REATTACH 0x20 /* reattaching */ #define COM_HW_CONSOLE 0x40 u_char sc_swflags; #define COM_SW_SOFTCAR 0x01 @@ -125,6 +128,8 @@ int comintr __P((void *)); void compoll __P((void *)); int comparam __P((struct tty *, struct termios *)); void comstart __P((struct tty *)); +void com_absent_notify __P((struct com_softc *sc)); +void comstart_pending __P((void *arg)); /* * XXX the following two cfattach structs should be different, and possibly @@ -145,12 +150,17 @@ struct cfattach com_commulti_ca = { }; #endif + struct cfdriver com_cd = { NULL, "com", DV_TTY }; int cominit __P((bus_chipset_tag_t, bus_io_handle_t, int)); +#ifndef CONSPEED +#define CONSPEED B9600 +#endif + #ifdef COMCONSOLE int comdefaultrate = CONSPEED; /* XXX why set default? */ #else @@ -181,49 +191,201 @@ extern int kgdb_debug_init; #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) -/*#include "pcmciabus.h"*/ -#if NPCMCIABUS >0 +#if NCOM_PCMCIA +#include <dev/pcmcia/pcmciavar.h> + +int com_pcmcia_match __P((struct device *, void *, void *)); +void com_pcmcia_attach __P((struct device *, struct device *, void *)); +int com_pcmcia_detach __P((struct device *)); + +struct cfattach com_pcmcia_ca = { + sizeof(struct com_softc), com_pcmcia_match, comattach, + com_pcmcia_detach +}; + +int +com_pcmcia_mod __P((struct pcmcia_link *pc_link, + struct device *self, + struct pcmcia_conf *pc_cf, + struct cfdata *cf)); + /* additional setup needed for pcmcia devices */ -#include <dev/pcmcia/pcmciabus.h> /* modify config entry */ -static int -commod(pc_link,self,pc_cf,cf) +int +com_pcmcia_mod(pc_link, self, pc_cf, cf) struct pcmcia_link *pc_link; struct device *self; struct pcmcia_conf *pc_cf; struct cfdata *cf; { int err; - struct pcmciadevs *dev=pc_link->device; + struct pcmciadevs *dev = pc_link->device; struct ed_softc *sc = (void *)self; - if(!(err=pc_link->adapter->bus_link->bus_config(pc_link,self,pc_cf,cf))) { - pc_cf->memwin=0; - if(pc_cf->cfgtype==0) - pc_cf->cfgtype=CFGENTRYID; /* determine from ioaddr */ + if (!(err = PCMCIA_BUS_CONFIG(pc_link->adapter, pc_link, self, + pc_cf, cf))) { + pc_cf->memwin = 0; + if (pc_cf->cfgtype == 0) + pc_cf->cfgtype = CFGENTRYID; /* determine from ioaddr */ } return err; } + +int com_pcmcia_isa_attach __P((struct device *, void *, void *, + struct pcmcia_link *)); +int com_pcmcia_remove __P((struct pcmcia_link *, struct device *)); + static struct pcmcia_com { struct pcmcia_device pcd; -} pcmcia_com= { - "PCMCIA Modem card",commod,NULL,NULL,NULL +} pcmcia_com = { + {"PCMCIA Modem card", com_pcmcia_mod, com_pcmcia_isa_attach, + NULL, com_pcmcia_remove} }; -struct pcmciadevs pcmcia_com_devs[]={ - { "com", 0, + + +struct pcmciadevs pcmcia_com_devs[] = { + { "com", 0, NULL, "*MODEM*", NULL, NULL, NULL, (void *)&pcmcia_com }, - { "com", 0, + { "com", 0, NULL, NULL, "*MODEM*", NULL, NULL, (void *)&pcmcia_com }, - { "com", 0, + { "com", 0, NULL, NULL, NULL, "*MODEM*", NULL, (void *)&pcmcia_com }, {NULL} }; +#define ncom_pcmcia_devs sizeof(pcmcia_com_devs)/sizeof(pcmcia_com_devs[0]) + +int +com_pcmcia_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + return pcmcia_slave_match(parent, match, aux, pcmcia_com_devs, + ncom_pcmcia_devs); +} + +int +com_pcmcia_isa_attach(parent, match, aux, pc_link) + struct device *parent; + void *match; + void *aux; + struct pcmcia_link *pc_link; +{ + struct isa_attach_args *ia = aux; + struct com_softc *sc = match; + + int rval; + if (rval = comprobe(parent, sc->sc_dev.dv_cfdata, ia)) { + if (ISSET(pc_link->flags, PCMCIA_REATTACH)) { +#ifdef COM_DEBUG + printf("comreattach, hwflags=%x\n", sc->sc_hwflags); +#endif + sc->sc_hwflags = COM_HW_REATTACH | + (sc->sc_hwflags & (COM_HW_ABSENT_PENDING|COM_HW_CONSOLE)); + } else + sc->sc_hwflags = 0; + } + return rval; +} + + +/* + * Called by config_detach attempts, shortly after com_pcmcia_remove + * was called. + */ +int +com_pcmcia_detach(self) + struct device *self; +{ + struct com_softc *sc = (void *)self; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + /* don't let it really be detached, it is still open */ + return EBUSY; + } + return 0; /* OK! */ +} + +/* + * called by pcmcia framework to accept/reject remove attempts. + * If we return 0, then the detach will proceed. + */ +int +com_pcmcia_remove(pc_link, self) + struct pcmcia_link *pc_link; + struct device *self; +{ + struct com_softc *sc = (void *)self; + struct tty *tp; + int s; + + if (!sc->sc_tty) + goto ok; + tp = sc->sc_tty; + + /* not in use ? if so, return "OK" */ + if (!ISSET(tp->t_state, TS_ISOPEN) && + !ISSET(tp->t_state, TS_WOPEN)) { + ttyfree(sc->sc_tty); + sc->sc_tty = NULL; + ok: + isa_intr_disestablish(sc->sc_bc, sc->sc_ih); + sc->sc_ih = NULL; + SET(sc->sc_hwflags, COM_HW_ABSENT); + return 0; /* OK! */ + } + /* + * Not easily removed. Put device into a dead state, clean state + * as best we can. notify all waiters. + */ + SET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING); +#ifdef COM_DEBUG + printf("pending detach flags %x\n", sc->sc_hwflags); +#endif + + s = spltty(); + com_absent_notify(sc); + splx(s); + + return 0; +} + +#if 0 +void +com_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcmcia_attach_args *paa = aux; + + printf("com_pcmcia_attach %p %p %p\n", parent, self, aux); + delay(2000000); + if (!pcmcia_configure(parent, self, paa->paa_link)) { + struct com_softc *sc = (void *)self; + sc->sc_hwflags |= COM_HW_ABSENT; + printf(": not attached\n"); + } +} #endif +#endif + +/* + * must be called at spltty() or higher. + */ +void +com_absent_notify(sc) + struct com_softc *sc; +{ + struct tty *tp; + if (tp = sc->sc_tty) { + CLR(tp->t_state, TS_CARR_ON|TS_BUSY); + ttyflush(tp, FREAD|FWRITE); + } +} int comspeed(speed) @@ -351,13 +513,21 @@ comprobe(parent, match, aux) int iobase, needioh; int rv = 1; +#if NCOM_ISA || NCOM_PCMCIA +#define IS_ISA(parent) \ + (!strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") || \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "pcmcia")) +#elif NCOM_ISA +#define IS_ISA(parent) \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") +#endif /* * XXX should be broken out into functions for isa probe and * XXX for commulti probe, with a helper function that contains * XXX most of the interesting stuff. */ -#if NCOM_ISA - if (!strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (IS_ISA(parent)) { struct isa_attach_args *ia = aux; bc = ia->ia_bc; @@ -393,8 +563,8 @@ comprobe(parent, match, aux) bus_io_unmap(bc, ioh, COM_NPORTS); out: -#if NCOM_ISA - if (rv && !strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (rv && IS_ISA(parent)) { struct isa_attach_args *ia = aux; ia->ia_iosize = COM_NPORTS; @@ -425,10 +595,16 @@ comattach(parent, self, aux) * XXX for commulti attach, with a helper function that contains * XXX most of the interesting stuff. */ - sc->sc_hwflags = 0; + if (ISSET(sc->sc_hwflags, COM_HW_REATTACH)) { + int s; + s = spltty(); + com_absent_notify(sc); + splx(s); + } else + sc->sc_hwflags = 0; sc->sc_swflags = 0; -#if NCOM_ISA - if (!strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (IS_ISA(parent)) { struct isa_attach_args *ia = aux; /* @@ -457,7 +633,7 @@ comattach(parent, self, aux) irq = IRQUNK; if (ca->ca_noien) - sc->sc_hwflags |= COM_HW_NOIEN; + SET(sc->sc_hwflags, COM_HW_NOIEN); } else #endif panic("comattach: impossible"); @@ -522,8 +698,8 @@ comattach(parent, self, aux) bus_io_write_1(bc, ioh, com_mcr, 0); if (irq != IRQUNK) { -#if NCOM_ISA - if (!strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (IS_ISA(parent)) { struct isa_attach_args *ia = aux; sc->sc_ih = isa_intr_establish(ia->ia_ic, irq, @@ -576,7 +752,7 @@ comopen(dev, flag, mode, p) if (unit >= com_cd.cd_ndevs) return ENXIO; sc = com_cd.cd_devs[unit]; - if (!sc) + if (!sc || ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) return ENXIO; if (!sc->sc_tty) @@ -716,19 +892,30 @@ comclose(dev, flag, mode, p) (*linesw[tp->t_line].l_close)(tp, flag); s = spltty(); - CLR(sc->sc_lcr, LCR_SBREAK); - bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); - bus_io_write_1(bc, ioh, com_ier, 0); - if (ISSET(tp->t_cflag, HUPCL) && - !ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) { - /* XXX perhaps only clear DTR */ - bus_io_write_1(bc, ioh, com_mcr, 0); + if (!ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* can't do any of this stuff .... */ + CLR(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + bus_io_write_1(bc, ioh, com_ier, 0); + if (ISSET(tp->t_cflag, HUPCL) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) { + /* XXX perhaps only clear DTR */ + bus_io_write_1(bc, ioh, com_mcr, 0); + } } CLR(tp->t_state, TS_BUSY | TS_FLUSH); if (--comsopen == 0) untimeout(compoll, NULL); splx(s); ttyclose(tp); +#ifdef COM_DEBUG + /* mark it ready for more use if reattached earlier */ + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + printf("comclose pending cleared\n"); + } +#endif + CLR(sc->sc_hwflags, COM_HW_ABSENT_PENDING); + #ifdef notyet /* XXXX */ if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { ttyfree(tp); @@ -747,6 +934,13 @@ comread(dev, uio, flag) struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)]; struct tty *tp = sc->sc_tty; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } @@ -759,6 +953,13 @@ comwrite(dev, uio, flag) struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)]; struct tty *tp = sc->sc_tty; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } @@ -800,6 +1001,13 @@ comioctl(dev, cmd, data, flag, p) bus_io_handle_t ioh = sc->sc_ioh; int error; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return error; @@ -914,6 +1122,13 @@ comparam(tp, t) tcflag_t oldcflag; int s; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + /* check requested parameters */ if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) return EINVAL; @@ -1042,6 +1257,18 @@ comparam(tp, t) } void +comstart_pending(arg) + void *arg; +{ + struct com_softc *sc = arg; + int s; + + s = spltty(); + com_absent_notify(sc); + splx(s); +} + +void comstart(tp) struct tty *tp; { @@ -1051,6 +1278,16 @@ comstart(tp) int s; s = spltty(); + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* + * not quite good enough: if caller is ttywait() it will + * go to sleep immediately, so hang out a bit and then + * prod caller again. + */ + com_absent_notify(sc); + timeout(comstart_pending, sc, 1); + goto out; + } if (ISSET(tp->t_state, TS_BUSY)) goto out; if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || @@ -1236,6 +1473,9 @@ comintr(arg) } iter[32]; #endif + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT) || !sc->sc_tty) + return 0; /* can't do squat. */ + #ifdef COM_DEBUG n = 0; if (ISSET(iter[n].iir = bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) diff --git a/sys/dev/ic/i82365reg.h b/sys/dev/ic/i82365reg.h index fb05f10c0c2..1f0a8f77858 100644 --- a/sys/dev/ic/i82365reg.h +++ b/sys/dev/ic/i82365reg.h @@ -8,7 +8,7 @@ * Support is included for Intel 82365SL PCIC controllers and clones * thereof. * - * $Id: i82365reg.h,v 1.1 1996/01/15 00:08:50 hvozda Exp $ + * $Id: i82365reg.h,v 1.2 1996/04/29 14:15:58 hvozda Exp $ ***********************************************************************/ /* @@ -30,6 +30,7 @@ */ #define PCIC_BASE 0x03e0 /* base adddress of pcic register set */ +#define PCIC_NPORTS 2 /* pcic takes 2 ports */ /* First, all the registers */ #define PCIC_ID_REV 0x00 /* Identification and Revision */ @@ -107,6 +108,8 @@ #define PCIC_INTEL1 0x83 /* Intel 82365SL Rev. 1; Both Memory and I/O */ #define PCIC_IBM1 0x88 /* IBM PCIC clone; Both Memory and I/O */ #define PCIC_IBM2 0x89 /* IBM PCIC clone; Both Memory and I/O */ +#define PCIC_146FC6 0x84 /* VL82C146FC6; Both Memory and I/O */ +#define PCIC_146FC7 0x85 /* VL82C146FC7; Both Memory and I/O */ /* For Interface Status register (PCIC_STATUS) */ #define PCIC_VPPV 0x80 /* Vpp_valid */ @@ -128,7 +131,7 @@ /* For the Interrupt and General Control register (PCIC_INT_GEN) */ #define PCIC_INT_MASK 0x0f #define PCIC_INT_FLAGMASK 0x0f -#define PCIC_INTR_ENA 0x10 +#define PCIC_INTR_ENA 0x10 /* clr bit means CSC interrupt goes via IRQ */ #define PCIC_CARDTYPE 0x20 /* Card Type 0 = memory, 1 = I/O */ #define PCIC_IOCARD 0x20 #define PCIC_MEMCARD 0x00 @@ -136,12 +139,20 @@ #define PCIC_RINGIND 0x80 /* For the Card Status Change register (PCIC_STAT_CHG) */ +#define PCIC_GPICH 0x10 /* General Purpose Input (GPI) Change */ #define PCIC_CDTCH 0x08 /* Card Detect Change */ #define PCIC_RDYCH 0x04 /* Ready Change */ #define PCIC_BATWRN 0x02 /* Battery Warning */ #define PCIC_BATDED 0x01 /* Battery Dead */ #define PCIC_STCH 0x01 /* Status Change */ +/* For the Card Status Change interrupt config register (PCIC_STAT_INT) */ +#define PCIC_CDT_ENA 0x08 /* Card Detect Enable */ +#define PCIC_RDY_ENA 0x04 /* Ready Enable */ +#define PCIC_BATWRN_ENA 0x02 /* Battery Warning */ +#define PCIC_BATDED_ENA 0x01 /* Battery Dead */ +#define PCIC_ST_ENA 0x01 /* Status Change */ + /* For the Address Window Enable Register (PCIC_ADDRWINE) */ #define PCIC_SM0_EN 0x01 /* Memory Window 0 Enable */ #define PCIC_SM1_EN 0x02 /* Memory Window 1 Enable */ @@ -194,7 +205,7 @@ /* For Card Detect and General Control register (PCIC_CDGC) */ #define PCIC_16_DL_INH 0x01 /* 16-bit memory delay inhibit */ #define PCIC_CNFG_RST_EN 0x02 /* configuration reset enable */ -#define PCIC_GPI_EN 0x04 /* GPI Enable */ +#define PCIC_GPI_EN 0x04 /* General Purpose Input (GPI) Enable */ #define PCIC_GPI_TRANS 0x08 /* GPI Transition Control */ #define PCIC_CDRES_EN 0x10 /* card detect resume enable */ #define PCIC_SW_CD_INT 0x20 /* s/w card detect interrupt */ @@ -206,12 +217,16 @@ struct pcic_register { }; struct pcic_regs { u_short chip_vers; -#define PCMICA_CHIP_82365_0 1 -#define PCMICA_CHIP_82365_1 2 -#define PCMICA_CHIP_IBM_1 3 -#define PCMICA_CHIP_IBM_2 4 +#define PCMICA_CHIP_82365_0 1 +#define PCMICA_CHIP_82365_1 2 +#define PCMICA_CHIP_IBM_1 3 +#define PCMICA_CHIP_IBM_2 4 +#define PCMICA_CHIP_146FC6 5 +#define PCMICA_CHIP_146FC7 6 u_short cnt; struct pcic_register reg[128]; }; /* DON'T ADD ANYTHING AFTER THIS #endif */ #endif /* __82365_H__ */ +#ifndef __82365_H__ +#define __82365_H__ diff --git a/sys/dev/isa/com.c b/sys/dev/isa/com.c index 77dd24748f4..5570f7ef424 100644 --- a/sys/dev/isa/com.c +++ b/sys/dev/isa/com.c @@ -1,4 +1,4 @@ -/* $OpenBSD: com.c,v 1.11 1996/04/21 22:23:15 deraadt Exp $ */ +/* $OpenBSD: com.c,v 1.12 1996/04/29 14:16:15 hvozda Exp $ */ /* $NetBSD: com.c,v 1.79 1996/04/15 18:54:31 cgd Exp $ */ /*- @@ -81,6 +81,7 @@ struct com_softc { struct device sc_dev; void *sc_ih; + bus_chipset_tag_t sc_bc; struct tty *sc_tty; int sc_overflows; @@ -94,7 +95,6 @@ struct com_softc { int sc_hayespbase; #endif - bus_chipset_tag_t sc_bc; bus_io_handle_t sc_ioh; bus_io_handle_t sc_hayespioh; @@ -102,6 +102,9 @@ struct com_softc { #define COM_HW_NOIEN 0x01 #define COM_HW_FIFO 0x02 #define COM_HW_HAYESP 0x04 +#define COM_HW_ABSENT_PENDING 0x08 /* reattached, awaiting close/reopen */ +#define COM_HW_ABSENT 0x10 /* configure actually failed, or removed */ +#define COM_HW_REATTACH 0x20 /* reattaching */ #define COM_HW_CONSOLE 0x40 u_char sc_swflags; #define COM_SW_SOFTCAR 0x01 @@ -125,6 +128,8 @@ int comintr __P((void *)); void compoll __P((void *)); int comparam __P((struct tty *, struct termios *)); void comstart __P((struct tty *)); +void com_absent_notify __P((struct com_softc *sc)); +void comstart_pending __P((void *arg)); /* * XXX the following two cfattach structs should be different, and possibly @@ -145,12 +150,17 @@ struct cfattach com_commulti_ca = { }; #endif + struct cfdriver com_cd = { NULL, "com", DV_TTY }; int cominit __P((bus_chipset_tag_t, bus_io_handle_t, int)); +#ifndef CONSPEED +#define CONSPEED B9600 +#endif + #ifdef COMCONSOLE int comdefaultrate = CONSPEED; /* XXX why set default? */ #else @@ -181,49 +191,201 @@ extern int kgdb_debug_init; #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) -/*#include "pcmciabus.h"*/ -#if NPCMCIABUS >0 +#if NCOM_PCMCIA +#include <dev/pcmcia/pcmciavar.h> + +int com_pcmcia_match __P((struct device *, void *, void *)); +void com_pcmcia_attach __P((struct device *, struct device *, void *)); +int com_pcmcia_detach __P((struct device *)); + +struct cfattach com_pcmcia_ca = { + sizeof(struct com_softc), com_pcmcia_match, comattach, + com_pcmcia_detach +}; + +int +com_pcmcia_mod __P((struct pcmcia_link *pc_link, + struct device *self, + struct pcmcia_conf *pc_cf, + struct cfdata *cf)); + /* additional setup needed for pcmcia devices */ -#include <dev/pcmcia/pcmciabus.h> /* modify config entry */ -static int -commod(pc_link,self,pc_cf,cf) +int +com_pcmcia_mod(pc_link, self, pc_cf, cf) struct pcmcia_link *pc_link; struct device *self; struct pcmcia_conf *pc_cf; struct cfdata *cf; { int err; - struct pcmciadevs *dev=pc_link->device; + struct pcmciadevs *dev = pc_link->device; struct ed_softc *sc = (void *)self; - if(!(err=pc_link->adapter->bus_link->bus_config(pc_link,self,pc_cf,cf))) { - pc_cf->memwin=0; - if(pc_cf->cfgtype==0) - pc_cf->cfgtype=CFGENTRYID; /* determine from ioaddr */ + if (!(err = PCMCIA_BUS_CONFIG(pc_link->adapter, pc_link, self, + pc_cf, cf))) { + pc_cf->memwin = 0; + if (pc_cf->cfgtype == 0) + pc_cf->cfgtype = CFGENTRYID; /* determine from ioaddr */ } return err; } + +int com_pcmcia_isa_attach __P((struct device *, void *, void *, + struct pcmcia_link *)); +int com_pcmcia_remove __P((struct pcmcia_link *, struct device *)); + static struct pcmcia_com { struct pcmcia_device pcd; -} pcmcia_com= { - "PCMCIA Modem card",commod,NULL,NULL,NULL +} pcmcia_com = { + {"PCMCIA Modem card", com_pcmcia_mod, com_pcmcia_isa_attach, + NULL, com_pcmcia_remove} }; -struct pcmciadevs pcmcia_com_devs[]={ - { "com", 0, + + +struct pcmciadevs pcmcia_com_devs[] = { + { "com", 0, NULL, "*MODEM*", NULL, NULL, NULL, (void *)&pcmcia_com }, - { "com", 0, + { "com", 0, NULL, NULL, "*MODEM*", NULL, NULL, (void *)&pcmcia_com }, - { "com", 0, + { "com", 0, NULL, NULL, NULL, "*MODEM*", NULL, (void *)&pcmcia_com }, {NULL} }; +#define ncom_pcmcia_devs sizeof(pcmcia_com_devs)/sizeof(pcmcia_com_devs[0]) + +int +com_pcmcia_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + return pcmcia_slave_match(parent, match, aux, pcmcia_com_devs, + ncom_pcmcia_devs); +} + +int +com_pcmcia_isa_attach(parent, match, aux, pc_link) + struct device *parent; + void *match; + void *aux; + struct pcmcia_link *pc_link; +{ + struct isa_attach_args *ia = aux; + struct com_softc *sc = match; + + int rval; + if (rval = comprobe(parent, sc->sc_dev.dv_cfdata, ia)) { + if (ISSET(pc_link->flags, PCMCIA_REATTACH)) { +#ifdef COM_DEBUG + printf("comreattach, hwflags=%x\n", sc->sc_hwflags); +#endif + sc->sc_hwflags = COM_HW_REATTACH | + (sc->sc_hwflags & (COM_HW_ABSENT_PENDING|COM_HW_CONSOLE)); + } else + sc->sc_hwflags = 0; + } + return rval; +} + + +/* + * Called by config_detach attempts, shortly after com_pcmcia_remove + * was called. + */ +int +com_pcmcia_detach(self) + struct device *self; +{ + struct com_softc *sc = (void *)self; + + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + /* don't let it really be detached, it is still open */ + return EBUSY; + } + return 0; /* OK! */ +} + +/* + * called by pcmcia framework to accept/reject remove attempts. + * If we return 0, then the detach will proceed. + */ +int +com_pcmcia_remove(pc_link, self) + struct pcmcia_link *pc_link; + struct device *self; +{ + struct com_softc *sc = (void *)self; + struct tty *tp; + int s; + + if (!sc->sc_tty) + goto ok; + tp = sc->sc_tty; + + /* not in use ? if so, return "OK" */ + if (!ISSET(tp->t_state, TS_ISOPEN) && + !ISSET(tp->t_state, TS_WOPEN)) { + ttyfree(sc->sc_tty); + sc->sc_tty = NULL; + ok: + isa_intr_disestablish(sc->sc_bc, sc->sc_ih); + sc->sc_ih = NULL; + SET(sc->sc_hwflags, COM_HW_ABSENT); + return 0; /* OK! */ + } + /* + * Not easily removed. Put device into a dead state, clean state + * as best we can. notify all waiters. + */ + SET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING); +#ifdef COM_DEBUG + printf("pending detach flags %x\n", sc->sc_hwflags); +#endif + + s = spltty(); + com_absent_notify(sc); + splx(s); + + return 0; +} + +#if 0 +void +com_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcmcia_attach_args *paa = aux; + + printf("com_pcmcia_attach %p %p %p\n", parent, self, aux); + delay(2000000); + if (!pcmcia_configure(parent, self, paa->paa_link)) { + struct com_softc *sc = (void *)self; + sc->sc_hwflags |= COM_HW_ABSENT; + printf(": not attached\n"); + } +} #endif +#endif + +/* + * must be called at spltty() or higher. + */ +void +com_absent_notify(sc) + struct com_softc *sc; +{ + struct tty *tp; + if (tp = sc->sc_tty) { + CLR(tp->t_state, TS_CARR_ON|TS_BUSY); + ttyflush(tp, FREAD|FWRITE); + } +} int comspeed(speed) @@ -351,13 +513,21 @@ comprobe(parent, match, aux) int iobase, needioh; int rv = 1; +#if NCOM_ISA || NCOM_PCMCIA +#define IS_ISA(parent) \ + (!strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") || \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "pcmcia")) +#elif NCOM_ISA +#define IS_ISA(parent) \ + !strcmp((parent)->dv_cfdata->cf_driver->cd_name, "isa") +#endif /* * XXX should be broken out into functions for isa probe and * XXX for commulti probe, with a helper function that contains * XXX most of the interesting stuff. */ -#if NCOM_ISA - if (!strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (IS_ISA(parent)) { struct isa_attach_args *ia = aux; bc = ia->ia_bc; @@ -393,8 +563,8 @@ comprobe(parent, match, aux) bus_io_unmap(bc, ioh, COM_NPORTS); out: -#if NCOM_ISA - if (rv && !strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (rv && IS_ISA(parent)) { struct isa_attach_args *ia = aux; ia->ia_iosize = COM_NPORTS; @@ -425,10 +595,16 @@ comattach(parent, self, aux) * XXX for commulti attach, with a helper function that contains * XXX most of the interesting stuff. */ - sc->sc_hwflags = 0; + if (ISSET(sc->sc_hwflags, COM_HW_REATTACH)) { + int s; + s = spltty(); + com_absent_notify(sc); + splx(s); + } else + sc->sc_hwflags = 0; sc->sc_swflags = 0; -#if NCOM_ISA - if (!strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (IS_ISA(parent)) { struct isa_attach_args *ia = aux; /* @@ -457,7 +633,7 @@ comattach(parent, self, aux) irq = IRQUNK; if (ca->ca_noien) - sc->sc_hwflags |= COM_HW_NOIEN; + SET(sc->sc_hwflags, COM_HW_NOIEN); } else #endif panic("comattach: impossible"); @@ -522,8 +698,8 @@ comattach(parent, self, aux) bus_io_write_1(bc, ioh, com_mcr, 0); if (irq != IRQUNK) { -#if NCOM_ISA - if (!strcmp(parent->dv_cfdata->cf_driver->cd_name, "isa")) { +#if NCOM_ISA || NCOM_PCMCIA + if (IS_ISA(parent)) { struct isa_attach_args *ia = aux; sc->sc_ih = isa_intr_establish(ia->ia_ic, irq, @@ -576,7 +752,7 @@ comopen(dev, flag, mode, p) if (unit >= com_cd.cd_ndevs) return ENXIO; sc = com_cd.cd_devs[unit]; - if (!sc) + if (!sc || ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) return ENXIO; if (!sc->sc_tty) @@ -716,19 +892,30 @@ comclose(dev, flag, mode, p) (*linesw[tp->t_line].l_close)(tp, flag); s = spltty(); - CLR(sc->sc_lcr, LCR_SBREAK); - bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); - bus_io_write_1(bc, ioh, com_ier, 0); - if (ISSET(tp->t_cflag, HUPCL) && - !ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) { - /* XXX perhaps only clear DTR */ - bus_io_write_1(bc, ioh, com_mcr, 0); + if (!ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* can't do any of this stuff .... */ + CLR(sc->sc_lcr, LCR_SBREAK); + bus_io_write_1(bc, ioh, com_lcr, sc->sc_lcr); + bus_io_write_1(bc, ioh, com_ier, 0); + if (ISSET(tp->t_cflag, HUPCL) && + !ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) { + /* XXX perhaps only clear DTR */ + bus_io_write_1(bc, ioh, com_mcr, 0); + } } CLR(tp->t_state, TS_BUSY | TS_FLUSH); if (--comsopen == 0) untimeout(compoll, NULL); splx(s); ttyclose(tp); +#ifdef COM_DEBUG + /* mark it ready for more use if reattached earlier */ + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT_PENDING)) { + printf("comclose pending cleared\n"); + } +#endif + CLR(sc->sc_hwflags, COM_HW_ABSENT_PENDING); + #ifdef notyet /* XXXX */ if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) { ttyfree(tp); @@ -747,6 +934,13 @@ comread(dev, uio, flag) struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)]; struct tty *tp = sc->sc_tty; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } @@ -759,6 +953,13 @@ comwrite(dev, uio, flag) struct com_softc *sc = com_cd.cd_devs[COMUNIT(dev)]; struct tty *tp = sc->sc_tty; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } @@ -800,6 +1001,13 @@ comioctl(dev, cmd, data, flag, p) bus_io_handle_t ioh = sc->sc_ioh; int error; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return error; @@ -914,6 +1122,13 @@ comparam(tp, t) tcflag_t oldcflag; int s; + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + int s = spltty(); + com_absent_notify(sc); + splx(s); + return EIO; + } + /* check requested parameters */ if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) return EINVAL; @@ -1042,6 +1257,18 @@ comparam(tp, t) } void +comstart_pending(arg) + void *arg; +{ + struct com_softc *sc = arg; + int s; + + s = spltty(); + com_absent_notify(sc); + splx(s); +} + +void comstart(tp) struct tty *tp; { @@ -1051,6 +1278,16 @@ comstart(tp) int s; s = spltty(); + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT|COM_HW_ABSENT_PENDING)) { + /* + * not quite good enough: if caller is ttywait() it will + * go to sleep immediately, so hang out a bit and then + * prod caller again. + */ + com_absent_notify(sc); + timeout(comstart_pending, sc, 1); + goto out; + } if (ISSET(tp->t_state, TS_BUSY)) goto out; if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || @@ -1236,6 +1473,9 @@ comintr(arg) } iter[32]; #endif + if (ISSET(sc->sc_hwflags, COM_HW_ABSENT) || !sc->sc_tty) + return 0; /* can't do squat. */ + #ifdef COM_DEBUG n = 0; if (ISSET(iter[n].iir = bus_io_read_1(bc, ioh, com_iir), IIR_NOPEND)) diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa index f145e7b4d30..6303824ce85 100644 --- a/sys/dev/isa/files.isa +++ b/sys/dev/isa/files.isa @@ -1,4 +1,4 @@ -# $OpenBSD: files.isa,v 1.10 1996/04/27 18:39:07 niklas Exp $ +# $OpenBSD: files.isa,v 1.11 1996/04/29 14:16:22 hvozda Exp $ # $NetBSD: files.isa,v 1.17 1996/03/29 20:53:30 mycroft Exp $ # # Config.new file and device description for machine-independent ISA code. @@ -19,27 +19,6 @@ file dev/isa/isa.c isa needs-flag define isadma file dev/isa/isadma.c isadma needs-flag -# PCMCIA -# XXX What is this? -#config problems -#device pcic at isa -#file dev/isa/pcmcia_pcic.c pcic pcmciabus - -define pcicbus {[iomem = -1], [iosiz = 0]} - -device pcic: pcicbus -attach pcic at isa -file dev/isa/pcmcia_pcic.c pcic - -file dev/isa/pcmcia_isa.c pcmcia - -# -# PCMCIA-only drivers -# - -include "../../../dev/pcmcia/files.pcmcia" - - # # 8250/16[45]50-based multi-port serial boards # @@ -69,7 +48,8 @@ file dev/isa/rtfps.c rtfps device com: tty attach com at isa with com_isa attach com at commulti with com_commulti -file dev/isa/com.c com & (com_isa | com_commulti) needs-flag +attach com at pcmcia with com_pcmcia +file dev/isa/com.c com & (com_isa | com_commulti | com_pcmcia) needs-flag # Cyclades Cyclom multiport serial cards # XXX currently broken @@ -167,9 +147,10 @@ file dev/isa/elink.c elink # National Semiconductor DS8390/WD83C690-based boards # (WD/SMC 80x3 family, SMC Ultra [8216], 3Com 3C503, NE[12]000, and clones) # XXX conflicts with amiga if_ed.c -#device ed: ether, ifnet -#attach ed at isa -#file dev/isa/if_ed.c ed needs-flag +device ed: ether, ifnet +attach ed at isa with ed_isa +attach ed at pcmcia with ed_pcmcia +file dev/isa/if_ed.c ed & (ed_isa | ed_pcmcia) needs-flag # 3Com 3C505 device eg: ether, ifnet @@ -185,7 +166,8 @@ file dev/isa/if_el.c el device ep: ether, ifnet, elink attach ep at isa with ep_isa attach ep at pci with ep_pci -file dev/isa/if_ep.c ep needs-flag +attach ep at pcmcia with ep_pcmcia +file dev/isa/if_ep.c ep & (ep_isa | ep_pci | ep_pcmcia) needs-flag # Fujitsu MB8696[05]-based boards # (Allied Telesis AT1700) @@ -265,3 +247,14 @@ file dev/isa/wss.c wss needs-flag device gus: audio, isadma, ics2101, ad1848, mulaw attach gus at isa file dev/isa/gus.c gus needs-flag + +# +# PCMCIA PCIC (i82365SL and compatibles): +# +device pcicmaster { [irq = -1], [iomem = -1], [iosiz = 0] } +attach pcicmaster at isa +device pcic: pcmciabus +attach pcic at pcicmaster +file dev/isa/pcmcia_pcic.c pcic | pcicmaster + +file dev/isa/pcmcia_isa.c pcmcia diff --git a/sys/dev/isa/if_ed.c b/sys/dev/isa/if_ed.c index d770c80a73e..8f2499c9d18 100644 --- a/sys/dev/isa/if_ed.c +++ b/sys/dev/isa/if_ed.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ed.c,v 1.11 1996/04/27 22:19:59 niklas Exp $ */ +/* $OpenBSD: if_ed.c,v 1.12 1996/04/29 14:16:31 hvozda Exp $ */ /* $NetBSD: if_ed.c,v 1.93 1996/04/11 22:28:55 cgd Exp $ */ /* @@ -19,6 +19,7 @@ */ #include "bpfilter.h" +#include "ed.h" #include <sys/param.h> #include <sys/systm.h> @@ -154,9 +155,11 @@ void ed_pio_readmem __P((struct ed_softc *, u_short, caddr_t, u_short)); void ed_pio_writemem __P((struct ed_softc *, caddr_t, u_short, u_short)); u_short ed_pio_write_mbufs __P((struct ed_softc *, struct mbuf *, u_short)); -struct cfattach ed_ca = { +#if NED_ISA > 0 +struct cfattach ed_isa_ca = { sizeof(struct ed_softc), edprobe, edattach }; +#endif struct cfdriver ed_cd = { NULL, "ed", DV_IFNET @@ -166,17 +169,19 @@ struct cfdriver ed_cd = { #define ETHER_MAX_LEN 1518 #define ETHER_ADDR_LEN 6 -#define NIC_PUT(bc, ioh, nic, reg, val) \ - bus_io_write_1((bc), (ioh), ((nic) + (reg)), (val)) -#define NIC_GET(bc, ioh, nic, reg) \ - bus_io_read_1((bc), (ioh), ((nic) + (reg))) +#if NED_PCMCIA > 0 +#include <dev/pcmcia/pcmciavar.h> -/*#include "pcmciabus.h"*/ -#if NPCMCIABUS > 0 +int ed_pcmcia_match __P((struct device *, void *, void *)); +void ed_pcmcia_attach __P((struct device *, struct device *, void *)); +int ed_pcmcia_detach __P((struct device *)); -#include <dev/pcmcia/pcmciabus.h> -static int ed_probe_pcmcia_ne __P((struct device *, void *, - void *, struct pcmcia_link *)); +struct cfattach ed_pcmcia_ca = { + sizeof(struct ed_softc), ed_pcmcia_match, edattach, ed_pcmcia_detach +}; + +static int ed_pcmcia_isa_attach __P((struct device *, void *, + void *, struct pcmcia_link *)); static int edmod __P((struct pcmcia_link *, struct device *, struct pcmcia_conf *, struct cfdata *cf)); @@ -185,7 +190,7 @@ static int ed_remove __P((struct pcmcia_link *, struct device *)); /* additional setup needed for pcmcia devices */ static int -ed_probe_pcmcia_ne(parent, match, aux, pc_link) +ed_pcmcia_isa_attach(parent, match, aux, pc_link) struct device *parent; void *match; void *aux; @@ -199,17 +204,18 @@ ed_probe_pcmcia_ne(parent, match, aux, pc_link) extern int ifqmaxlen; u_char enaddr[ETHER_ADDR_LEN]; - if ((int)dev->param >= 0) + if ((int)dev->param != -1) err = pcmcia_read_cis(pc_link, enaddr, (int) dev->param, ETHER_ADDR_LEN); else err = 0; if (err) - printf("Cannot read cis info %d\n", err); + printf("%s: attaching ed: cannot read cis info %d\n", + parent->dv_xname, err); - if (ed_probe_Novell(sc, cf, ia)) { + if (ed_find_Novell(sc, cf, ia)) { delay(100); - if ((int)dev->param >= 0) { + if ((int)dev->param != -1) { err = pcmcia_read_cis(pc_link, sc->sc_arpcom.ac_enaddr, (int) dev->param, ETHER_ADDR_LEN); if (err) { @@ -229,8 +235,8 @@ ed_probe_pcmcia_ne(parent, match, aux, pc_link) sc->type_str = dev->model; sc->sc_arpcom.ac_if.if_snd.ifq_maxlen=ifqmaxlen; return 1; - } - return 0; + } else + return 0; } /* modify config entry */ @@ -242,16 +248,20 @@ edmod(pc_link, self, pc_cf, cf) struct cfdata *cf; { int err; - struct pcmciadevs *dev=pc_link->device; - struct ed_softc *sc = (void *)self; - int svec_card = strcmp(dev->manufacturer, "SVEC") == 0; +/* struct pcmciadevs *dev=pc_link->device;*/ +/* struct ed_softc *sc = (void *)self;*/ + int svec_card = pc_cf->memwin == 5; int de650_0 = (pc_cf->memwin != 0) && !svec_card; - err = pc_link->adapter->bus_link->bus_config(pc_link, self, pc_cf, cf); + err = PCMCIA_BUS_CONFIG(pc_link->adapter, pc_link, self, pc_cf, cf); if (err) return err; if (svec_card) { pc_cf->memwin = 0; +#if 0 + pc_cf->cfgid = 32; /* Try this if it still doesn't work */ + pc_cf->cfgid |= 32; /* or Try this if it still doesn't work */ +#endif } if (de650_0) { pc_cf->io[0].flags = @@ -281,13 +291,15 @@ ed_remove(pc_link,self) shutdownhook_disestablish(sc->sc_sh); ifp->if_flags &= ~(IFF_RUNNING|IFF_UP); sc->spec_flags |= ED_NOTPRESENT; - return pc_link->adapter->bus_link->bus_unconfig(pc_link); + isa_intr_disestablish(sc->sc_bc, sc->sc_ih); + return PCMCIA_BUS_UNCONFIG(pc_link->adapter, pc_link); } static struct pcmcia_dlink { struct pcmcia_device pcd; -} pcmcia_dlink= { - "PCMCIA Novell compatible", edmod, ed_probe_pcmcia_ne, NULL, ed_remove +} pcmcia_dlink = { + {"PCMCIA Novell compatible", edmod, ed_pcmcia_isa_attach, + NULL, ed_remove} }; struct pcmciadevs pcmcia_ed_devs[]={ @@ -303,16 +315,61 @@ struct pcmciadevs pcmcia_ed_devs[]={ "Socket EA PCMCIA LAN Adapter Revision D", "Ethernet ID 000000000000", NULL, (void *) -1, (void *)&pcmcia_dlink }, - /* probably not right for ethernet address--card does not seem to - have it anywhere. */ + /* something screwed up in ports requested */ { "ed", 0, "SVEC", "FD605 PCMCIA EtherNet Card", "V1-1", NULL, - (void *)0xb4, (void *)&pcmcia_dlink }, - { "ed", 0, "PMX ", "PE-200", "ETHERNET", "R01", (void *) 0x110, - (void *)&pcmcia_dlink }, /* 0x110 is a guess */ + (void *)-1, (void *)&pcmcia_dlink }, +#if 0 + /* not quite right for ethernet adress */ + { "ed", 0, "PMX ", "PE-200", "ETHERNET", "R01", (void *)-1, + (void *)&pcmcia_dlink }, +#endif { NULL } }; + +#define ned_pcmcia_devs sizeof(pcmcia_ed_devs)/sizeof(pcmcia_ed_devs[0]) + +int +ed_pcmcia_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + return pcmcia_slave_match(parent, match, aux, pcmcia_ed_devs, + ned_pcmcia_devs); +} + +void +ed_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcmcia_attach_args *paa = aux; + + printf("ed_pcmcia_attach %p %p %p\n", parent, self, aux); + delay(2000000); + if (!pcmcia_configure(parent, self, paa->paa_link)) { + struct ed_softc *sc = (void *)self; + sc->spec_flags |= ED_NOTPRESENT; + printf(": not attached\n"); + } +} + +/* + * No detach; network devices are too well linked into the rest of the + * kernel. + */ +int +ed_pcmcia_detach(self) + struct device *self; +{ + return EBUSY; +} + #endif +#define NIC_PUT(bc, ioh, nic, reg, val) \ + bus_io_write_1((bc), (ioh), ((nic) + (reg)), (val)) +#define NIC_GET(bc, ioh, nic, reg) \ + bus_io_read_1((bc), (ioh), ((nic) + (reg))) /* * Determine if the device is present. @@ -1499,7 +1556,6 @@ edinit(sc) struct ifnet *ifp = &sc->sc_arpcom.ac_if; int nicbase = sc->nic_base, asicbase = sc->asic_base; int i; - u_char command; u_long mcaf[2]; /* @@ -2412,7 +2468,6 @@ ed_pio_write_mbufs(sc, m, dst) bus_io_handle_t ioh = sc->sc_ioh; int nicbase = sc->nic_base, asicbase = sc->asic_base; u_short len; - struct mbuf *mp; int maxwait = 100; /* about 120us */ len = m->m_pkthdr.len; diff --git a/sys/dev/isa/if_ep.c b/sys/dev/isa/if_ep.c index 4102ad294e4..f9eab4b8368 100644 --- a/sys/dev/isa/if_ep.c +++ b/sys/dev/isa/if_ep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ep.c,v 1.11 1996/04/21 22:23:52 deraadt Exp $ */ +/* $OpenBSD: if_ep.c,v 1.12 1996/04/29 14:16:41 hvozda Exp $ */ /* $NetBSD: if_ep.c,v 1.90 1996/04/11 22:29:15 cgd Exp $ */ /* @@ -31,8 +31,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/*#include "pcmciabus.h"*/ +#include "pcmcia.h" #include "bpfilter.h" +#include "ep.h" #include <sys/param.h> #include <sys/mbuf.h> @@ -42,6 +43,7 @@ #include <sys/syslog.h> #include <sys/select.h> #include <sys/device.h> +#include <sys/systm.h> #include <net/if.h> #include <net/netisr.h> @@ -114,26 +116,34 @@ struct ep_softc { #define EP_BUS_PCI 0x3 #define EP_IS_BUS_32(a) ((a) & 0x2) + + u_char pcmcia_flags; +#define EP_REATTACH 0x01 +#define EP_ABSENT 0x02 }; static int epprobe __P((struct device *, void *, void *)); static void epattach __P((struct device *, struct device *, void *)); /* XXX the following two structs should be different. */ +#if NEP_ISA > 0 struct cfattach ep_isa_ca = { sizeof(struct ep_softc), epprobe, epattach }; +#endif +#if NEP_PCI > 0 struct cfattach ep_pci_ca = { sizeof(struct ep_softc), epprobe, epattach }; +#endif + struct cfdriver ep_cd = { NULL, "ep", DV_IFNET }; int epintr __P((void *)); -static void epxstat __P((struct ep_softc *)); static int epstatus __P((struct ep_softc *)); void epinit __P((struct ep_softc *)); int epioctl __P((struct ifnet *, u_long, caddr_t)); @@ -142,7 +152,7 @@ void epwatchdog __P((int)); void epreset __P((struct ep_softc *)); void epread __P((struct ep_softc *)); struct mbuf *epget __P((struct ep_softc *, int)); -void epmbuffill __P((struct ep_softc *)); +void epmbuffill __P((void *)); void epmbufempty __P((struct ep_softc *)); void epstop __P((struct ep_softc *)); void epsetfilter __P((struct ep_softc *)); @@ -177,31 +187,39 @@ epaddcard(iobase, irq, bustype) epcards[nepcards].bustype = bustype; nepcards++; } + +#if NEP_PCMCIA > 0 +#include <dev/pcmcia/pcmciavar.h> + +int ep_pcmcia_match __P((struct device *, void *, void *)); +void ep_pcmcia_attach __P((struct device *, struct device *, void *)); +int ep_pcmcia_detach __P((struct device *)); -#if NPCMCIABUS > 0 -#include <dev/pcmcia/pcmciabus.h> -static int ep_probe_pcmcia __P((struct device *, void *, +static int ep_pcmcia_isa_attach __P((struct device *, void *, void *, struct pcmcia_link *)); static int epmod __P((struct pcmcia_link *, struct device *, struct pcmcia_conf *, struct cfdata * cf)); static int ep_remove __P((struct pcmcia_link *, struct device *)); +struct cfattach ep_pcmcia_ca = { + sizeof(struct ep_softc), ep_pcmcia_match, epattach, ep_pcmcia_detach +}; + /* additional setup needed for pcmcia devices */ static int -ep_probe_pcmcia(parent, match, aux, pc_link) +ep_pcmcia_isa_attach(parent, match, aux, pc_link) struct device *parent; void *match; void *aux; struct pcmcia_link *pc_link; { struct ep_softc *sc = (void *) match; - struct cfdata *cf = sc->sc_dev.dv_cfdata; +/* struct cfdata *cf = sc->sc_dev.dv_cfdata;*/ struct isa_attach_args *ia = aux; - struct pcmciadevs *dev = pc_link->device; +/* struct pcmciadevs *dev = pc_link->device;*/ struct ifnet *ifp = &sc->sc_arpcom.ac_if; int i; extern int ifqmaxlen; - short addr[3]; outw(ia->ia_iobase + EP_COMMAND, WINDOW_SELECT | 0); outw(ia->ia_iobase + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); @@ -211,16 +229,15 @@ ep_probe_pcmcia(parent, match, aux, pc_link) * ok til here. Now try to figure out which link we have. * try coax first... */ - sc->bustype = EP_BUS_PCMCIA; #ifdef EP_COAX_DEFAULT outw(ia->ia_iobase + EP_W0_ADDRESS_CFG, 0xC000); #else - /* COAX as default is reportet to be a problem */ + /* COAX as default is reported to be a problem */ outw(ia->ia_iobase + EP_W0_ADDRESS_CFG, 0x0000); #endif ifp->if_snd.ifq_maxlen = ifqmaxlen; - epaddcard(ia->ia_iobase, ia->ia_irq, 0); + epaddcard(ia->ia_iobase, ia->ia_irq, EP_BUS_PCMCIA); for (i = 0; i < nepcards; i++) { if (epcards[i].available == 0) @@ -243,10 +260,11 @@ good: ia->ia_iosize = 0x10; ia->ia_msize = 0; + sc->bustype = epcards[i].bustype; + sc->pcmcia_flags = (pc_link->flags & PCMCIA_REATTACH) ? EP_REATTACH:0; return 1; } - /* modify config entry */ static int epmod(pc_link, self, pc_cf, cf) @@ -256,11 +274,11 @@ epmod(pc_link, self, pc_cf, cf) struct cfdata *cf; { int err; - struct pcmciadevs *dev = pc_link->device; - struct ep_softc *sc = (void *) self; +/* struct pcmciadevs *dev = pc_link->device;*/ +/* struct ep_softc *sc = (void *) self;*/ - if ((err = pc_link->adapter->bus_link->bus_config(pc_link, self, - pc_cf, cf)) != 0) { + if ((err = PCMCIA_BUS_CONFIG(pc_link->adapter, pc_link, self, + pc_cf, cf)) != 0) { printf("bus_config failed %d\n", err); return err; } @@ -285,13 +303,14 @@ ep_remove(pc_link, self) if_down(ifp); epstop(sc); ifp->if_flags &= ~(IFF_RUNNING | IFF_UP); - return pc_link->adapter->bus_link->bus_unconfig(pc_link); + sc->pcmcia_flags = EP_ABSENT; + return PCMCIA_BUS_UNCONFIG(pc_link->adapter, pc_link); } static struct pcmcia_3com { struct pcmcia_device pcd; } pcmcia_3com = { - "PCMCIA 3COM 3C589", epmod, ep_probe_pcmcia, NULL, ep_remove + {"PCMCIA 3COM 3C589", epmod, ep_pcmcia_isa_attach, NULL, ep_remove} }; struct pcmciadevs pcmcia_ep_devs[] = { @@ -312,8 +331,47 @@ struct pcmciadevs pcmcia_ep_devs[] = { #endif { NULL } }; +#define nep_pcmcia_devs sizeof(pcmcia_ep_devs)/sizeof(pcmcia_ep_devs[0]) + +int +ep_pcmcia_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + return pcmcia_slave_match(parent, match, aux, pcmcia_ep_devs, + nep_pcmcia_devs); +} + +void +ep_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcmcia_attach_args *paa = aux; + + printf("ep_pcmcia_attach %p %p %p\n", parent, self, aux); + delay(2000000); + if (!pcmcia_configure(parent, self, paa->paa_link)) { + struct ep_softc *sc = (void *)self; + sc->pcmcia_flags |= EP_ABSENT; + printf(": not attached\n"); + } +} + +/* + * No detach; network devices are too well linked into the rest of the + * kernel. + */ +int +ep_pcmcia_detach(self) + struct device *self; +{ + return EBUSY; +} + #endif + /* * 3c579 cards on the EISA bus are probed by their slot number. 3c509 * cards on the ISA bus are probed in ethernet address order. The probe @@ -513,14 +571,15 @@ epconfig(sc, conn) ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; - if_attach(ifp); - ether_ifattach(ifp); + if ((sc->pcmcia_flags & EP_REATTACH) == 0) { + if_attach(ifp); + ether_ifattach(ifp); #if NBPFILTER > 0 - bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, - sizeof(struct ether_header)); + bpfattach(&sc->sc_arpcom.ac_if.if_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); #endif - + } sc->tx_start_thresh = 20; /* probably a good starting point. */ } @@ -1219,6 +1278,13 @@ epioctl(ifp, cmd, data) int s, error = 0; s = splnet(); + if (sc->bustype == EP_BUS_PCMCIA && + (sc->pcmcia_flags & EP_ABSENT)) { + if_down(ifp); + printf("%s: device offline\n", sc->sc_dev.dv_xname); + splx(s); + return ENXIO; + } switch (cmd) { @@ -1412,9 +1478,10 @@ epbusyeeprom(sc) } void -epmbuffill(sc) - struct ep_softc *sc; +epmbuffill(arg) + void *arg; { + struct ep_softc *sc = arg; int s, i; s = splnet(); diff --git a/sys/dev/isa/pcmcia_isa.c b/sys/dev/isa/pcmcia_isa.c index d2fd46ee66f..5a485ca8b7a 100644 --- a/sys/dev/isa/pcmcia_isa.c +++ b/sys/dev/isa/pcmcia_isa.c @@ -1,4 +1,6 @@ +/* $Id: pcmcia_isa.c,v 1.2 1996/04/29 14:16:47 hvozda Exp $ */ /* + * Copyright (c) 1995,1996 John T. Kohl. All rights reserved. * Copyright (c) 1994 Stefan Grefen. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,7 +28,6 @@ * (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: pcmcia_isa.c,v 1.1 1996/01/16 20:13:01 hvozda Exp $ */ /* TODO add modload support and loadable lists of devices */ @@ -36,9 +37,10 @@ #include <sys/systm.h> #include <sys/malloc.h> #include <sys/device.h> +#include <vm/vm.h> -#include <dev/pcmcia/pcmcia.h> -#include <dev/pcmcia/pcmciabus.h> +#include <dev/pcmcia/pcmciavar.h> +#include <dev/pcmcia/pcmciareg.h> #include <dev/isa/isareg.h> #include <dev/isa/isavar.h> @@ -49,14 +51,20 @@ #define PCMCIA_ISA_DEBUG #endif -static int pcmcia_isa_init __P((struct device *, struct cfdata *, +#ifdef PCMCIA_ISA_DEBUG +#define STATIC +#else +#define STATIC static +#endif + +STATIC int pcmcia_isa_init __P((struct device *, struct cfdata *, void *, struct pcmcia_adapter *, int)); -static int pcmcia_isa_search __P((struct device *, void *, cfprint_t)); -static int pcmcia_isa_probe __P((struct device *, void *, +STATIC int pcmcia_isa_search __P((struct device *, void *, cfprint_t)); +STATIC int pcmcia_isa_probe __P((struct device *, void *, void *, struct pcmcia_link *)); -static int pcmcia_isa_config __P((struct pcmcia_link *, struct device *, +STATIC int pcmcia_isa_config __P((struct pcmcia_link *, struct device *, struct pcmcia_conf *, struct cfdata *)); -static int pcmcia_isa_unconfig __P((struct pcmcia_link *)); +STATIC int pcmcia_isa_unconfig __P((struct pcmcia_link *)); struct pcmciabus_link pcmcia_isa_link = { pcmcia_isa_config, @@ -67,7 +75,7 @@ struct pcmciabus_link pcmcia_isa_link = { }; /* copy out the addr and length from machine specific attach struct */ -static int +STATIC int pcmcia_isa_init(parent, cf, aux, pca, flag) struct device *parent; struct cfdata *cf; @@ -75,22 +83,34 @@ pcmcia_isa_init(parent, cf, aux, pca, flag) struct pcmcia_adapter *pca; int flag; { - struct isa_attach_args *ia = aux; + struct pcmciabus_attach_args *pa = aux; + bus_mem_handle_t memh; + vm_offset_t physaddr; #ifdef PCMCIA_ISA_DEBUG if (parent != NULL) printf("PARENT %s\n", parent->dv_xname); #endif - if (flag) { /* attach */ - pca->scratch_mem = (caddr_t) ISA_HOLE_VADDR(ia->ia_maddr); - pca->scratch_memsiz = ia->ia_msize; + if (flag == 0) { /* match */ + if (bus_mem_map(pa->pba_bc, pa->pba_maddr, pa->pba_msize, 0, + &memh)) + return 0; + pca->scratch_memsiz = pa->pba_msize; + pca->scratch_memh = memh; + pca->pa_bc = pa->pba_bc; +#ifdef PCMCIA_ISA_DEBUG + printf("pbaaddr %p maddr %x msize %x\n", + pa, pa->pba_maddr, pa->pba_msize); + printf("PCA %p mem %p size %d chip %x memh %x\n", + pca, pca->scratch_mem, pca->scratch_memsiz, + pca->scratch_chipset, pca->scratch_memh); +#endif } - ia->ia_iosize = 0; return 1; } /* probe and attach a device, the has to be configured already */ -static int +STATIC int pcmcia_isa_probe(parent, match, aux, pc_link) struct device *parent; void *match; @@ -103,23 +123,39 @@ pcmcia_isa_probe(parent, match, aux, pc_link) struct pcmciadevs *pcs = pc_link->device; int (*probe) () = (pcs != NULL) ? pcs->dev->pcmcia_probe : NULL; + if (cf->cf_loc[6] != -1 && cf->cf_loc[6] != pc_link->slot) { +#ifdef PCMCIA_ISA_DEBUG + printf("- isa probe slot mismatch: cf %d <> link %d\n", + cf->cf_loc[6], pc_link->slot); +#endif + return 0; + } +#if 0 + if (pcs == NULL || pcs->dev->pcmcia_probe == NULL) { +#ifdef PCMCIA_ISA_DEBUG + printf("- isa probe null proberoutine %p\n", pcs); +#endif + return 0; + } +#endif ia.ia_iobase = cf->cf_loc[0]; - ia.ia_iosize = 0x666; + ia.ia_iosize = cf->cf_loc[1] == -1 ? 0x666 : cf->cf_loc[1]; ia.ia_maddr = cf->cf_loc[2]; ia.ia_msize = cf->cf_loc[3]; - ia.ia_irq = cf->cf_loc[4]; + ia.ia_irq = cf->cf_loc[4] == 2 ? 9 : cf->cf_loc[4] ; ia.ia_drq = cf->cf_loc[5]; + ia.ia_bc = pc_link->bus->sc_bc; if (probe == NULL) - probe = cf->cf_driver->cd_match; + probe = cf->cf_attach->ca_match; #ifdef PCMCIA_ISA_DEBUG - printf("pcmcia probe %x %x %x\n", ia.ia_iobase, ia.ia_irq, probe); + printf("pcmcia probe %x %x %p\n", ia.ia_iobase, ia.ia_irq, probe); printf("parentname = %s\n", parent->dv_xname); printf("devname = %s\n", dev->dv_xname); printf("driver name = %s\n", cf->cf_driver->cd_name); #endif if ((*probe) (parent, dev, &ia, pc_link) > 0) { - extern isaprint(); + extern isaprint(); config_attach(parent, dev, &ia, isaprint); #ifdef PCMCIA_ISA_DEBUG printf("biomask %x netmask %x ttymask %x\n", @@ -128,7 +164,7 @@ pcmcia_isa_probe(parent, match, aux, pc_link) #endif return 1; } - else + else if (parent->dv_cfdata->cf_driver->cd_indirect == 0) free(dev, M_DEVBUF); return 0; } @@ -140,7 +176,7 @@ pcmcia_isa_probe(parent, match, aux, pc_link) * contiguous windows and shift according to the offset for the first not * fixed window */ -static int +STATIC int pcmcia_isa_config(pc_link, self, pc_cf, cf) struct pcmcia_link *pc_link; struct device *self; @@ -148,6 +184,7 @@ pcmcia_isa_config(pc_link, self, pc_cf, cf) struct cfdata *cf; { struct isa_attach_args ia; + struct pcmciadevs *pcs = pc_link->device; ia.ia_iobase = cf->cf_loc[0]; ia.ia_iosize = 0x666; @@ -156,11 +193,19 @@ pcmcia_isa_config(pc_link, self, pc_cf, cf) ia.ia_irq = cf->cf_loc[4]; ia.ia_drq = cf->cf_loc[5]; #ifdef PCMCIA_ISA_DEBUG - printf("pcmcia_isa_config iobase=%x maddr=%x msize=%x irq=%x drq=%x\n", + printf("pcmcia_isa_config iobase=%x maddr=%x msize=%x irq=%d drq=%d slot=%d\n", ia.ia_iobase, ISA_HOLE_VADDR(ia.ia_maddr), ia.ia_msize, - ia.ia_irq, ia.ia_drq); + ia.ia_irq, ia.ia_drq, cf->cf_loc[6]); #endif + if (pcs && strcmp(pcs->devname, self->dv_cfdata->cf_driver->cd_name)) { +#ifdef PCMCIA_ISA_DEBUG + printf("- wrong driver %s vs %s\n", pcs->devname, + self->dv_cfdata->cf_driver->cd_name); +#endif + return ENODEV; + } + if (ia.ia_irq != IRQUNK) { int irq = 1 << ia.ia_irq; /* @@ -177,7 +222,10 @@ pcmcia_isa_config(pc_link, self, pc_cf, cf) if (irq == (1 << 9) || irq == (1 << 2)) irq = (1 << 9) | (1 << 2); if ((irq & pc_cf->irq_mask) == 0) { - printf("irq %d mask %x\n", ia.ia_irq, + printf("%s: slot %d requested irq %d, avail_mask %x\n", + self->dv_parent->dv_xname, + pc_link->slot, + ia.ia_irq, pc_cf->irq_mask); return ENODEV; } @@ -247,24 +295,27 @@ pcmcia_isa_config(pc_link, self, pc_cf, cf) } -static int +STATIC int pcmcia_isa_unconfig(pc_link) struct pcmcia_link *pc_link; { +#if 0 if (pc_link && pc_link->intr > 0) { /* THIS IS A GUESS ... TODO check all possible drivers */ struct softc { struct device sc_dev; void *sc_ih; + bus_chipset_tag_t sc_bc; } *sc = pc_link->devp; if (sc) - isa_intr_disestablish(sc->sc_ih); + isa_intr_disestablish(sc->sc_bc, sc->sc_ih); } +#endif return 0; } /* Searches for for configured devices on the pcmciabus */ -static int +STATIC int pcmcia_isa_search(parent, aux, print) struct device *parent; void *aux; diff --git a/sys/dev/isa/pcmcia_pcic.c b/sys/dev/isa/pcmcia_pcic.c index 8e8f16f1594..dfcf2ea4a81 100644 --- a/sys/dev/isa/pcmcia_pcic.c +++ b/sys/dev/isa/pcmcia_pcic.c @@ -1,3 +1,32 @@ +/* $Id: pcmcia_pcic.c,v 1.5 1996/04/29 14:16:53 hvozda Exp $ */ +/* + * 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. + * + */ /* * Device Driver for Intel 82365 based pcmcia slots * @@ -20,13 +49,16 @@ #include <sys/device.h> #include <sys/proc.h> #include <sys/user.h> +#include <sys/cpu.h> #include <machine/pio.h> #include <dev/isa/isavar.h> #include <dev/ic/i82365reg.h> -#include <dev/pcmcia/pcmciabus.h> +#include <dev/pcmcia/pcmciavar.h> +#include <dev/pcmcia/pcmciareg.h> + #ifdef IBM_WD #define PCIC_DEBUG 0xf #endif @@ -36,13 +68,13 @@ #define PCDINTR 0x04 #define PCDSERV 0x08 #define PCDRW 0x10 +#define PCDCONF 0x20 int pcic_debug = PCIC_DEBUG; #define DEBUG(a) (pcic_debug & (a)) #else #define DEBUG(a) (0) #endif - /* * pcic_softc: per line info and status */ @@ -69,20 +101,26 @@ struct slot { struct pcmcia_link *link; struct pcic_softc *chip; }; + struct pcic_softc { - struct device sc_dev; + struct device sc_dev; + bus_chipset_tag_t sc_bc; + struct pcmcia_adapter sc_adapter; void *sc_ih; int sc_polltimo; int sc_pcic_irq; - u_short pcic_base; /* base port for each board */ - u_char slot_id; - u_char chip_inf; - struct slot slot[2]; -} pcic_softc[4]; + bus_io_handle_t sc_ioh; + bus_mem_handle_t sc_memh; + u_short pcic_base; /* base port for each board */ + u_char chip_inf; + struct slot slot[4]; /* treat up to 4 as on the same pcic */ +}; +#define pcic_parent(sc) ((struct pcicmaster_softc *)(sc)->sc_dev.dv_parent) static int pcic_map_io __P((struct pcmcia_link *, u_int, u_int, int)); -static int pcic_map_mem __P((struct pcmcia_link *, caddr_t, +static int pcic_map_mem __P((struct pcmcia_link *, bus_chipset_tag_t, + bus_mem_handle_t, u_int, u_int, int)); static int pcic_map_intr __P((struct pcmcia_link *, int, int)); static int pcic_service __P((struct pcmcia_link *, int, void *, int)); @@ -94,15 +132,53 @@ static struct pcmcia_funcs pcic_funcs = { pcic_service }; -int pcicprobe __P((struct device *, void *, void *)); -void pcicattach __P((struct device *, struct device *, void *)); +int pcic_probe __P((struct device *, void *, void *)); +void pcic_attach __P((struct device *, struct device *, void *)); +int pcic_print __P((void *, char *)); + +int pcicmaster_probe __P((struct device *, void *, void *)); +void pcicmaster_attach __P((struct device *, struct device *, void *)); +int pcicmaster_print __P((void *, char *)); extern struct pcmciabus_link pcmcia_isa_link; -struct cfdriver pciccd = { - NULL, "pcic", pcicprobe, pcicattach, DV_DULL, sizeof(struct pcic_softc) +struct cfattach pcic_ca = { + sizeof(struct pcic_softc), pcic_probe, pcic_attach, }; +struct cfdriver pcic_cd = { + NULL, "pcic", DV_DULL +}; + +struct pcicmaster_softc { + struct device sc_dev; + bus_chipset_tag_t sc_bc; + bus_io_handle_t sc_ioh; + struct pcic_softc *sc_ctlrs[2]; + char sc_slavestate[2]; +#define SLAVE_NOTPRESENT 0 +#define SLAVE_FOUND 1 +#define SLAVE_CONFIGURED 2 +}; + +struct cfattach pcicmaster_ca = { + sizeof(struct pcicmaster_softc), pcicmaster_probe, pcicmaster_attach, +}; + +struct cfdriver pcicmaster_cd = { + NULL, "pcicmaster", DV_DULL, 1 +}; + +struct pcic_attach_args { + int pia_ctlr; /* pcic ctlr number */ + bus_chipset_tag_t pia_bc; /* bus chipset tag */ + bus_io_handle_t pia_ioh; /* base i/o address */ + int pia_iosize; /* span of ports used */ + int pia_irq; /* interrupt request */ + int pia_drq; /* DMA request */ + int pia_maddr; /* physical i/o mem addr */ + u_int pia_msize; /* size of i/o memory */ +}; static u_char pcic_rd __P((struct slot *, int)); static void pcic_wr __P((struct slot *, int, int)); @@ -114,12 +190,13 @@ pcic_rd(slot, reg) int reg; { u_char res; + bus_chipset_tag_t bc = slot->chip->sc_bc; + bus_io_handle_t ioh = slot->chip->sc_ioh; if (DEBUG(PCDRW)) - printf("pcic_rd(%x [%x %x]) = ", reg, slot->reg_off, - slot->chip->pcic_base); - outb(slot->chip->pcic_base, slot->reg_off + reg); + printf("pcic_rd(%x [%x %x]) = ", reg, slot->reg_off, ioh); + bus_io_write_1(bc, ioh, 0, slot->reg_off + reg); delay(1); - res = inb(slot->chip->pcic_base + 1); + res = bus_io_read_1(bc, ioh, 1); if (DEBUG(PCDRW)) printf("%x\n", res); return res; @@ -130,15 +207,17 @@ pcic_wr(slot, reg, val) struct slot *slot; int reg, val; { - outb(slot->chip->pcic_base, slot->reg_off + reg); + bus_chipset_tag_t bc = slot->chip->sc_bc; + bus_io_handle_t ioh = slot->chip->sc_ioh; + bus_io_write_1(bc, ioh, 0, slot->reg_off + reg); delay(1); - outb(slot->chip->pcic_base + 1, val); + bus_io_write_1(bc, ioh, 1, val); if (DEBUG(PCDRW)) { int res; delay(1); - outb(slot->chip->pcic_base, slot->reg_off + reg); + bus_io_write_1(bc, ioh, 0, slot->reg_off + reg); delay(1); - res = inb(slot->chip->pcic_base + 1); + res = bus_io_read_1(bc, ioh, 1); printf("pcic_wr(%x %x) = %x\n", reg, val, res); } } @@ -154,121 +233,214 @@ pcic_wait(slot, i) } int -pcicprobe(parent, self, aux) +pcic_probe(parent, self, aux) struct device *parent; void *self; void *aux; { - struct pcic_softc *pcic = (void *) self; - struct isa_attach_args *ia = aux; - struct cfdata *cf = pcic->sc_dev.dv_cfdata; - u_int chip_inf = 0; - int i; + struct pcic_softc *pcic = self; + struct pcicmaster_softc *pcicm = (struct pcicmaster_softc *) parent; + struct pcic_attach_args *pia = aux; + bus_mem_handle_t memh; + u_int chip_inf = 0, ochip_inf = 0; + int first = 1; + int i, j, maxslot; - pcic->pcic_base = ia->ia_iobase; - pcic->slot_id = 0; /* XXX */ bzero(pcic->slot, sizeof(pcic->slot)); - pcic->slot[0].chip = pcic; - pcic->slot[0].reg_off = (pcic->slot_id & 1) * 0x80; - pcic->slot[1].chip = pcic; - pcic->slot[1].reg_off = ((pcic->slot_id & 1) * 0x80) + 0x40; - chip_inf = pcic_rd(&pcic->slot[0], PCIC_ID_REV); - switch (chip_inf) { - case PCIC_INTEL0: + + if (DEBUG(PCDCONF)) { + printf("pcic_probe controller %d unit %d\n", pia->pia_ctlr, + pcic->sc_dev.dv_unit); + delay(2000000); + } + if (pcicm->sc_slavestate[pia->pia_ctlr] != SLAVE_FOUND) + return 0; + if (pcic->sc_dev.dv_cfdata->cf_loc[1] == -1 || + pcic->sc_dev.dv_cfdata->cf_loc[2] == 0) + return 0; + + /* + * select register offsets based on which controller we are. + * 2 pcic controllers (w/ 2 slots each) possible at each + * IO port location, for a total of 8 possible PCMCIA slots. + * + * for VLSI controllers, we probe up to 4 slots for the same chip type, + * and handle them on one controller. This is slightly + * cheating (two separate pcic's are required for 4 slots, according + * to the i82365 spec). + * + * For other controllers, we only take up to 2 slots. + */ + pcic->sc_ioh = pia->pia_ioh; + pcic->sc_bc = pia->pia_bc; + pcic->sc_adapter.nslots = 0; + maxslot = 2; + for (i = j = 0; i < maxslot; i++) { + pcic->slot[j].reg_off = 0x80 * pia->pia_ctlr + 0x40 * i; + pcic->slot[j].chip = pcic; + + chip_inf = pcic_rd(&pcic->slot[j], PCIC_ID_REV); + if (DEBUG(PCDCONF)) { + printf("pcic_probe read info %x\n", chip_inf); + delay(2000000); + } + if (!first && ochip_inf != chip_inf) + continue; /* don't attach, it's different */ + ochip_inf = chip_inf; + switch (chip_inf) { + case PCIC_INTEL0: pcic->chip_inf = PCMICA_CHIP_82365_0; goto ok; - case PCIC_INTEL1: + case PCIC_INTEL1: pcic->chip_inf = PCMICA_CHIP_82365_1; goto ok; - case PCIC_IBM1: + case PCIC_IBM1: pcic->chip_inf = PCMICA_CHIP_IBM_1; goto ok; - case PCIC_IBM2: + case PCIC_146FC6: + pcic->chip_inf = PCMICA_CHIP_146FC6; + maxslot = 4; + goto ok; + case PCIC_146FC7: + pcic->chip_inf = PCMICA_CHIP_146FC7; + maxslot = 4; + goto ok; + case PCIC_IBM2: pcic->chip_inf = PCMICA_CHIP_IBM_2; -ok: - ia->ia_msize = 0; - ia->ia_iosize = 2; - pcmcia_register(pcic, &pcmcia_isa_link, &pcic_funcs, - pcic->slot_id); + ok: + if (first) { + pcic->sc_adapter.adapter_softc = (void *)pcic; + pcic->sc_adapter.chip_link = &pcic_funcs; + pcic->sc_adapter.bus_link = &pcmcia_isa_link; + pcicm->sc_ctlrs[pia->pia_ctlr] = pcic; + pcicm->sc_slavestate[pia->pia_ctlr] = SLAVE_CONFIGURED; + first = 0; + } + pcic->sc_adapter.nslots++; + j++; + default: + if (DEBUG(PCDCONF)) { + printf("found ID %x at pcic%d position\n", + chip_inf & 0xff, pcic->sc_dev.dv_unit); + } + continue; + } + } + if (pcic->sc_adapter.nslots != 0) { + pcic->sc_memh = memh; return 1; - default: - printf("found ID %x at pcic position\n", chip_inf & 0xff); - break; } - /* reset mappings .... */ - pcic_wr(&pcic->slot[0], PCIC_POWER, pcic->slot[0].pow=PCIC_DISRST); - pcic_wr(&pcic->slot[1], PCIC_POWER, pcic->slot[1].pow=PCIC_DISRST); - - delay(1000); - - for (i = PCIC_INT_GEN; i < 0x40; i++) { - pcic_wr(&pcic->slot[0], i, 0); - pcic_wr(&pcic->slot[1], i, 0); + if (DEBUG(PCDCONF)) { + printf("pcic_probe failed\n"); + delay(2000000); } - delay(10000); + bus_mem_unmap(pia->pia_bc, memh, pia->pia_msize); return 0; } int pcic_intr __P((void *)); +int +pcic_print(aux, name) + void *aux; + char *name; +{ + if (name != NULL) + printf("%s: pcmciabus ", name); + return UNCONF; +} void -pcicattach(parent, self, aux) +pcic_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct pcic_softc *pcic = (void *) self; - struct isa_attach_args *ia = aux; + struct pcic_attach_args *pia = aux; + struct pcmciabus_attach_args pba; struct slot *slot; int i; static char *pcic_names[] = { "Intel 82365sl Rev. 0", "Intel 82365sl Rev. 1", "IBM 82365sl clone Rev. 1", - "IBM 82365sl clone Rev. 2"}; - printf(": %s slots %d-%d (%x %x)\n", pcic_names[pcic->chip_inf - - PCMICA_CHIP_82365_0], pcic->slot_id * 2, pcic->slot_id * 2 + 1, - &pcic->slot[0], &pcic->slot[1]); + "IBM 82365sl clone Rev. 2", + "VL82146 (82365sl clone) Rev. 6", + "VL82146 (82365sl clone) Rev. 7" }; + if (DEBUG(PCDCONF)) { + printf("pcic_attach found\n"); + delay(2000000); + } + pia->pia_irq = self->dv_cfdata->cf_loc[0]; + pia->pia_maddr = self->dv_cfdata->cf_loc[1]; + pia->pia_msize = self->dv_cfdata->cf_loc[2]; + + printf(": %s slots %d-%d iomem %x-%x", + pcic_names[pcic->chip_inf - PCMICA_CHIP_82365_0], + pcic->sc_dev.dv_unit * 2, + pcic->sc_dev.dv_unit * 2 + pcic->sc_adapter.nslots - 1, + pia->pia_maddr, pia->pia_maddr + pia->pia_msize - 1); + if (pia->pia_irq != IRQUNK) + printf(" irq %d\n", pia->pia_irq); + else + printf("\n"); + if (DEBUG(PCDCONF)) + delay(2000000); + +#ifdef PCMCIA_ISA_DEBUG + printf("pcic %p slots %p,%p\nisaaddr %p ports %x size %d irq %d drq %d maddr %x msize %x\n", + pcic, &pcic->slot[0], &pcic->slot[1], + pia, pia->pia_ioh, pia->pia_iosize, + pia->pia_irq, pia->pia_drq, pia->pia_maddr, pia->pia_msize); + if (DEBUG(PCDCONF)) + delay(2000000); +#endif + /* enable interrupts on events */ - if (ia->ia_irq != IRQUNK) - pcic->sc_pcic_irq = ia->ia_irq; + if (pia->pia_irq != IRQUNK) + pcic->sc_pcic_irq = pia->pia_irq; else pcic->sc_pcic_irq = 0; - for (i = 0; i < 2; i++) { - slot = &pcic->slot[i]; - slot->irq = pcic->sc_pcic_irq | PCIC_INTR_ENA; - pcic_wr(slot, PCIC_STAT_INT, - (pcic->sc_pcic_irq << 4) |PCIC_CDTCH | PCIC_STCH); - pcic_wr(&pcic->slot[i], PCIC_INT_GEN, slot->irq); - (void) pcic_rd(&pcic->slot[i], PCIC_STAT_CHG); + for (i = 0; i < pcic->sc_adapter.nslots; i++) { + slot = &pcic->slot[i]; + /* + * Arrange for card status change interrupts + * to be steered to specified IRQ. + * Treat all cards as I/O cards for the moment so we get + * sensible card change interrupt codes (besides, we don't + * support memory cards :) + */ + pcic_wr(slot, PCIC_STAT_INT, + (pcic->sc_pcic_irq << 4) | + PCIC_CDTCH | PCIC_IOCARD); + slot->irq = pcic_rd(slot, PCIC_INT_GEN) & ~PCIC_INTR_ENA; + pcic_wr(slot, PCIC_INT_GEN, slot->irq); + (void) pcic_rd(slot, PCIC_STAT_CHG); } - if (ia->ia_irq == IRQUNK) { + if (pia->pia_irq == IRQUNK) { pcic->sc_polltimo = hz/2; timeout((void (*)(void *))pcic_intr, pcic, pcic->sc_polltimo); } else { - pcic->sc_ih = isa_intr_establish(ia->ia_irq, IST_EDGE, - IPL_NET, pcic_intr, pcic, pcic->sc_dev.dv_xname); + pcic->sc_ih = isa_intr_establish(pia->pia_bc, + pia->pia_irq, IST_EDGE, + IPL_PCMCIA, pcic_intr, pcic, pcic->sc_dev.dv_xname); pcic->sc_polltimo = 0; } -} - -#ifdef DDB -int pcic_intr_test(slot) -struct slot *slot; -{ - printf("CSC interrupt state: %x\n", pcic_rd(slot, PCIC_STAT_INT)); - printf("General interrupt state: %x\n", pcic_rd(slot, PCIC_INT_GEN)); -} - -int pcic_intr_set(slot) -struct slot *slot; -{ - pcic_wr(slot, PCIC_INT_GEN, pcic_rd(slot, PCIC_INT_GEN)|PCIC_INTR_ENA); - pcic_intr_test(slot); -} + /* + * Probe the pcmciabus at this controller. + */ + pba.pba_bc = pia->pia_bc; + pba.pba_maddr = pia->pia_maddr; + pba.pba_msize = pia->pia_msize; + pba.pba_aux = &pcic->sc_adapter; +#ifdef PCMCIA_DEBUG + printf("config_found(%p, %p, %p)\n", + self, &pba, pcic_print); #endif + config_found(self, (void *)&pba, pcic_print); +} int pcic_intr(arg) @@ -278,14 +450,17 @@ void *arg; u_char statchg, intgen; register int i; +#ifdef PCMCIA_PCIC_DEBUG if (pcic->sc_polltimo == 0) printf("%s: interrupt:", pcic->sc_dev.dv_xname); - for (i = 0; i < 2; i++) { +#endif + for (i = 0; i < pcic->sc_adapter.nslots; i++) { struct pcmcia_link *link = pcic->slot[i].link; statchg = pcic_rd(&pcic->slot[i], PCIC_STAT_CHG); if (statchg == 0) continue; intgen = pcic_rd(&pcic->slot[i], PCIC_INT_GEN); +#ifdef PCMCIA_PCIC_DEBUG if (intgen & PCIC_IOCARD) { printf("%s: slot %d iocard status %s%s\n", pcic->sc_dev.dv_xname, i, @@ -295,6 +470,7 @@ void *arg; printf("%s: slot %d memcard status %x\n", pcic->sc_dev.dv_xname, i, statchg); } +#endif if ((statchg & PCIC_CDTCH) && (link->flags & PCMCIA_SLOT_OPEN) == 0) { #if 0 @@ -330,7 +506,10 @@ pcic_map_io(link, start, len, flags) int flags; { struct pcic_softc *sc = link->adapter->adapter_softc; - struct slot *slot = &sc->slot[link->slot & 1]; + struct slot *slot; + if (link->slot >= sc->sc_adapter.nslots) + return ENXIO; + slot = &sc->slot[link->slot]; len--; if (DEBUG(PCDIO)) { @@ -403,10 +582,8 @@ pcic_map_io(link, start, len, flags) delay(1000); return 0; } else { - u_int stop; int window; int winid; - int ioflags; if (flags & PCMCIA_LAST_WIN) { window = MAX_IOSECTION - 1; } else if (flags & PCMCIA_FIRST_WIN) { @@ -440,26 +617,33 @@ pcic_map_io(link, start, len, flags) slot->io_addr[window] = start; slot->io_len[window] = len; + return 0; } - } + static int -pcic_map_mem(link, haddr, start, len, flags) +pcic_map_mem(link, bc, ioh, start, len, flags) struct pcmcia_link *link; - caddr_t haddr; + bus_chipset_tag_t bc; + bus_mem_handle_t ioh; u_int start, len; int flags; { - struct pcic_softc *sc = link->adapter->adapter_softc; - struct slot *slot = &sc->slot[link->slot & 1]; vm_offset_t physaddr; + struct pcic_softc *sc = link->adapter->adapter_softc; + struct slot *slot; + caddr_t haddr = ioh; /* XXX */ + if (link->slot >= sc->sc_adapter.nslots) + return ENXIO; + + slot = &sc->slot[link->slot]; if (flags & PCMCIA_PHYSICAL_ADDR) physaddr = (vm_offset_t) haddr; else physaddr = pmap_extract(pmap_kernel(), (vm_offset_t) haddr); if (DEBUG(PCDMEM)) - printf("pcic_map_mem %x %x %x %x %x\n", haddr, physaddr, + printf("pcic_map_mem %p %lx %x %x %x\n", haddr, physaddr, start, len, flags); (u_long) physaddr >>= 12; @@ -491,7 +675,7 @@ pcic_map_mem(link, haddr, start, len, flags) offs = (start - (u_long) physaddr) & 0x3fff; if (DEBUG(PCDMEM)) - printf("mapmem 2:%x %x %x\n", offs, physaddr + offs, + printf("mapmem 2:%x %lx %x\n", offs, physaddr + offs, start); stop = (u_long) physaddr + len; @@ -503,7 +687,7 @@ pcic_map_mem(link, haddr, start, len, flags) (u_long) physaddr & 0xff); pcic_wr(slot, winid | PCIC_START | PCIC_ADDR_HIGH, (((u_long) physaddr >> 8) & 0x3f) | - /* PCIC_ZEROWS|/* */ + /* PCIC_ZEROWS| */ ((flags & PCMCIA_MAP_16) ? PCIC_DATA16 : 0)); pcic_wr(slot, winid | PCIC_END | PCIC_ADDR_LOW, @@ -527,8 +711,6 @@ pcic_map_mem(link, haddr, start, len, flags) delay(1000); return 0; } else { - u_int offs; - u_int stop; int window; int winid; @@ -571,20 +753,24 @@ pcic_map_mem(link, haddr, start, len, flags) return 0; } } + static int pcic_map_intr(link, irq, flags) struct pcmcia_link *link; int irq, flags; { struct pcic_softc *sc = link->adapter->adapter_softc; - struct slot *slot = &sc->slot[link->slot & 1]; + struct slot *slot; + if (link->slot >= sc->sc_adapter.nslots) + return ENXIO; + + slot = &sc->slot[link->slot]; if (DEBUG(PCDINTR)) printf("pcic_map_intr %x %x\n", irq, flags); if (flags & PCMCIA_UNMAP) { - slot->irq &= ~PCIC_INT_MASK; - slot->irq |= sc->sc_pcic_irq | PCIC_INTR_ENA; + slot->irq &= ~(PCIC_INT_MASK|PCIC_INTR_ENA); pcic_wr(slot, PCIC_INT_GEN, slot->irq); } else { @@ -592,8 +778,8 @@ pcic_map_intr(link, irq, flags) return EINVAL; if(irq==2) irq=9; - slot->irq = (slot->irq & PCIC_INT_FLAGMASK) | - irq | PCIC_INTR_ENA; + slot->irq &= ~(PCIC_INTR_ENA|PCIC_INT_MASK); + slot->irq |= irq | PCIC_CARDRESET; /* reset is inverted */ pcic_wr(slot, PCIC_INT_GEN, slot->irq); } return 0; @@ -608,7 +794,11 @@ pcic_service(link, opcode, arg, flags) int flags; { struct pcic_softc *sc = link->adapter->adapter_softc; - struct slot *slot = &sc->slot[link->slot & 1]; + struct slot *slot; + if (link->slot >= sc->sc_adapter.nslots) + return ENXIO; + + slot = &sc->slot[link->slot]; slot->link = link; /* save it for later :) */ switch (opcode) { @@ -656,7 +846,10 @@ pcic_service(link, opcode, arg, flags) if (DEBUG(PCDSERV)) printf("pcic_service(reset)\n"); - slot->irq |= flags ? PCIC_IOCARD : 0; + if (flags) + slot->irq |= PCIC_IOCARD; + else + slot->irq &= ~PCIC_IOCARD; /* XXX? */ pcic_wr(slot, PCIC_POWER, slot->pow &= ~PCIC_DISRST); slot->irq &= ~PCIC_CARDRESET; pcic_wr(slot, PCIC_INT_GEN, slot->irq); @@ -683,7 +876,8 @@ pcic_service(link, opcode, arg, flags) printf("pcic_service(power): "); if (flags & PCMCIA_POWER_ON) { int nv = (PCIC_DISRST|PCIC_OUTENA); - pcic_wr(slot, PCIC_INT_GEN, slot->irq = 0); + pcic_wr(slot, PCIC_INT_GEN, + slot->irq = PCIC_IOCARD); if(flags & PCMCIA_POWER_3V) nv |= PCIC_VCC3V; if(flags & PCMCIA_POWER_5V) @@ -731,3 +925,136 @@ pcic_service(link, opcode, arg, flags) return EINVAL; } } + +/* + * Handle I/O space mapping for children. Thin layer. + */ +int +pcicmaster_probe(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + struct pcicmaster_softc *pcicm = self; + struct isa_attach_args *ia = aux; + struct cfdata *cf = pcicm->sc_dev.dv_cfdata; + bus_chipset_tag_t bc; + bus_io_handle_t ioh; + + u_int chip_inf = 0; + int i, j; + int rval = 0; + struct pcic_softc pcic; /* faked up for probing only */ + + if (DEBUG(PCDCONF)) { + printf("pcicmaster_probe\n"); + delay(2000000); + } + bc = ia->ia_bc; + if (bus_io_map(bc, ia->ia_iobase, PCIC_NPORTS, &ioh)) + return (0); + /* + * Probe the slots for each of the possible child controllers, + * and if any are there we return a positive indication. + */ + pcic.sc_ioh = ioh; + for (i = 0; i < 2; i++) { + bzero(pcic.slot, sizeof(pcic.slot)); + pcic.slot[0].chip = &pcic; + pcic.slot[0].reg_off = i * 0x80; + chip_inf = pcic_rd(&pcic.slot[0], PCIC_ID_REV); + switch (chip_inf) { + case PCIC_INTEL0: + case PCIC_INTEL1: + case PCIC_IBM1: + case PCIC_146FC6: + case PCIC_146FC7: + case PCIC_IBM2: + if (DEBUG(PCDCONF)) { + printf("pcicmaster_probe found, cf=%p\n", cf); + delay(2000000); + } + pcicm->sc_slavestate[i] = SLAVE_FOUND; + rval++; + break; + default: + pcicm->sc_slavestate[i] = SLAVE_NOTPRESENT; + if (DEBUG(PCDCONF)) { + printf("found ID %x at slave %d\n", + chip_inf & 0xff, i); + } + break; + } + if (pcicm->sc_slavestate[i] != SLAVE_FOUND) { + /* reset mappings .... */ + pcic_wr(&pcic.slot[0], PCIC_POWER, + pcic.slot[0].pow=PCIC_DISRST); + delay(1000); + for (j = PCIC_INT_GEN; j < 0x40; j++) { + pcic_wr(&pcic.slot[0], j, 0); + } + delay(10000); + } + } + if (rval) { + ia->ia_iosize = 2; + pcicm->sc_bc = bc; + pcicm->sc_ioh = ioh; + } else + bus_io_unmap(bc, ioh, PCIC_NPORTS); + return rval; +} + +void +pcicmaster_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcicmaster_softc *pcicm = (void *) self; + struct isa_attach_args *ia = aux; + struct pcic_attach_args pia; + int i; + printf("\n"); + if (DEBUG(PCDCONF)) { + printf("pcicmaster_attach\n"); + delay(2000000); + } +#ifdef PCMCIA_ISA_DEBUG + printf("pcicm %p isaaddr %p ports %x size %d irq %d drq %d maddr %x msize %x\n", + pcicm, ia, ia->ia_iobase, ia->ia_iosize, + ia->ia_irq, ia->ia_drq, ia->ia_maddr, ia->ia_msize); + if (DEBUG(PCDCONF)) + delay(2000000); +#endif + /* attach up to two PCICs at this I/O address */ + for (i = 0; i < 2; i++) { + if (pcicm->sc_slavestate[i] == SLAVE_FOUND) { + pia.pia_ctlr = i; + /* + * share the I/O space and memory mapping space. + */ + pia.pia_bc = pcicm->sc_bc; + pia.pia_ioh = pcicm->sc_ioh; + pia.pia_iosize = ia->ia_iosize; + pia.pia_drq = ia->ia_drq; +#if 0 + pia.pia_irq = ia->ia_irq; + pia.pia_irq = cf->cf_loc[0]; /* irq from master attach */ + pia.pia_maddr = ia->ia_maddr + (ia->ia_msize / 2) * i; + pia.pia_msize = ia->ia_msize / 2; +#endif + + config_found(self, &pia, pcicmaster_print); + } + } +} + +int +pcicmaster_print(aux, name) + void *aux; + char *name; +{ + if (name != NULL) + printf("%s: master controller ", name); + return UNCONF; +} diff --git a/sys/dev/isa/wd.c b/sys/dev/isa/wd.c index 76267053725..293048e8981 100644 --- a/sys/dev/isa/wd.c +++ b/sys/dev/isa/wd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wd.c,v 1.10 1996/04/21 22:24:40 deraadt Exp $ */ +/* $OpenBSD: wd.c,v 1.11 1996/04/29 14:17:00 hvozda Exp $ */ /* $NetBSD: wd.c,v 1.148 1996/04/11 22:30:31 cgd Exp $ */ /* @@ -57,7 +57,8 @@ #include <dev/isa/isadmavar.h> #include <dev/isa/wdreg.h> -#define WAITTIME (4 * hz) /* time to wait for a completion */ +#define WAITTIME (8 * hz) /* time to wait for a completion + (long enough for disk spin-ups) */ #define RECOVERYTIME (hz / 2) /* time to recover from an error */ #define WDCDELAY 100 @@ -67,7 +68,7 @@ #define WDCNDELAY_DEBUG 10 #endif -#define WDIORETRIES 5 /* number of retries before giving up */ +#define WDIORETRIES 3 /* number of retries before giving up */ #define WDUNIT(dev) DISKUNIT(dev) #define WDPART(dev) DISKPART(dev) @@ -587,7 +588,7 @@ loop: wd->sc_blkno = blkno / (lp->d_secsize / DEV_BSIZE); } else { #ifdef WDDEBUG - printf(" %d)%x", wd->sc_skip, inb(wd->sc_iobase+wd_altsts)); + printf(" %d)%x", wd->sc_skip, inb(wdc->sc_iobase+wd_altsts)); #endif } @@ -696,7 +697,7 @@ loop: #ifdef WDDEBUG printf("sector %d cylin %d head %d addr %x sts %x\n", sector, - cylin, head, bp->b_data, inb(wd->sc_iobase+wd_altsts)); + cylin, head, bp->b_data, inb(wdc->sc_iobase+wd_altsts)); #endif } else if (wd->sc_nblks > 1) { /* The number of blocks in the last stretch may be smaller. */ @@ -743,11 +744,9 @@ wdcintr(arg) struct wd_softc *wd; struct buf *bp; - if ((wdc->sc_flags & WDCF_ACTIVE) == 0) { - /* Clear the pending interrupt and abort. */ - (void) inb(wdc->sc_iobase+wd_status); + if ((wdc->sc_flags & WDCF_ACTIVE) == 0) + /* leave it alone if we didn't ask for this interrupt */ return 0; - } wdc->sc_flags &= ~WDCF_ACTIVE; untimeout(wdctimeout, wdc); @@ -756,7 +755,7 @@ wdcintr(arg) bp = wd->sc_q.b_actf; #ifdef WDDEBUG - printf("I%d ", ctrlr); + printf("I%s ", wdc->sc_dev.dv_xname); #endif if (wait_for_unbusy(wdc) < 0) { @@ -781,7 +780,6 @@ wdcintr(arg) /* Have we an error? */ if (wdc->sc_status & WDCS_ERR) { - lose: #ifdef WDDEBUG wderror(wd, NULL, "wdcintr"); #endif @@ -795,8 +793,9 @@ wdcintr(arg) goto bad; #endif - if (++wdc->sc_errors < WDIORETRIES) - goto restart; + wdcunwedge(wdc); + if (wdc->sc_errors < WDIORETRIES) + return 1; wderror(wd, bp, "hard error"); bad: diff --git a/sys/dev/pcmcia/files.pcmcia b/sys/dev/pcmcia/files.pcmcia index 3429486e007..a32bef3b0e6 100644 --- a/sys/dev/pcmcia/files.pcmcia +++ b/sys/dev/pcmcia/files.pcmcia @@ -1,14 +1,21 @@ -# $OpenBSD: files.pcmcia,v 1.3 1996/04/21 22:25:58 deraadt Exp $ +# $OpenBSD: files.pcmcia,v 1.4 1996/04/29 14:17:10 hvozda Exp $ +# $Id: files.pcmcia,v 1.4 1996/04/29 14:17:10 hvozda Exp $ # -# Config file and device description for machine-independent PCMCIA code. +# Config.new file and device description for machine-independent PCMCIA code. # Included by ports that need it. # XXX Does this comment hold? # ports should define their own "device pcmcia" line (like the one below, # but with the correct bus attachment). -device pcmcia: isa -attach pcmcia at pcicbus +# +# needs all the parameters available on isa, so devices can attach here. +# + +device pcmcia {[port = -1], [size = 0], + [iomem = -1], [iosiz = 0], + [irq = -1], [drq = -1], [slot = -1]} +attach pcmcia at pcmciabus -file dev/pcmcia/pcmcia.c pcmcia needs-count +file dev/pcmcia/pcmcia.c pcmcia needs-flag file dev/pcmcia/pcmcia_conf.c pcmcia diff --git a/sys/dev/pcmcia/pcmcia.c b/sys/dev/pcmcia/pcmcia.c index 7729774be59..f37de3ef3a4 100644 --- a/sys/dev/pcmcia/pcmcia.c +++ b/sys/dev/pcmcia/pcmcia.c @@ -1,4 +1,6 @@ +/* $Id: pcmcia.c,v 1.3 1996/04/29 14:17:15 hvozda Exp $ */ /* + * Copyright (c) 1996 John T. Kohl. All rights reserved. * Copyright (c) 1994 Stefan Grefen. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,9 +29,14 @@ * (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: pcmcia.c,v 1.2 1996/01/26 21:27:31 hvozda Exp $ */ +/* XXX - these next two lines are just "glue" until the confusion over + pcmcia vs pcmciabus between the framework and sys/conf/files + gets resolved */ +#define pcmciabus_cd pcmcia_cd +#define pcmciabus_ca pcmcia_ca + /* derived from scsiconf.c writte by Julian Elischer et al */ /* TODO add modload support and loadable lists of devices */ #include <sys/types.h> @@ -39,15 +46,13 @@ #include <sys/device.h> #include <sys/ioctl.h> #include <sys/fcntl.h> +#include <sys/proc.h> +#include <sys/cpu.h> -#include <dev/pcmcia/pcmcia.h> -#include <dev/pcmcia/pcmciabus.h> +#include <dev/pcmcia/pcmciavar.h> +#include <dev/pcmcia/pcmciareg.h> #include <dev/pcmcia/pcmcia_ioctl.h> -#include "ed.h" -#include "com.h" -#include "ep.h" - #ifdef IBM_WD #define PCMCIA_DEBUG #endif @@ -57,107 +62,57 @@ # define PPRINTF(a) #endif -static void pcmciadumpcf __P((struct pcmcia_conf *)); -static int pcmcia_strcmp __P((char *, char *, int, char *)); - -void pcmcia_shuthook __P((void *)); - -static struct pcmcia_adapter pcmcia_drivers[4]; -static int pcmcia_cntrl = 0; -static int probed = 0; -static struct device **deldevs = NULL; -static int ndeldevs = 0; - -/* I've decided to re-ifdef these. It makes making a kernel easier - until I either get config(8) modified to deal somehow - or figure out a better to way declare the prototypes and - build up the knowndevs struct. Stefan may have ideas... -*/ - -#if NED > 0 -extern struct pcmciadevs pcmcia_ed_devs[]; -#endif -#if NCOM > 0 -extern struct pcmciadevs pcmcia_com_devs[]; -#endif -#if NEP > 0 -extern struct pcmciadevs pcmcia_ep_devs[]; -#endif - -static struct pcmciadevs *knowndevs[] = { -#if NED > 0 - pcmcia_ed_devs, -#endif -#if NCOM > 0 - pcmcia_com_devs, -#endif -#if NEP > 0 - pcmcia_ep_devs, +#ifdef PCMCIA_DEBUG +void pcmciadumpcf __P((struct pcmcia_conf *)); #endif - NULL -}; -#ifdef notyet -static struct pcmciadevs *knowndevs[10] = { NULL }; -#define KNOWNSIZE (sizeof(knowndevs) / sizeof(knowndevs[0])) -#endif +static struct old_devs { + struct device *dev; + struct pcmciadevs *pcdev; +} *deldevs; +static int ndeldevs = 0; #define PCMCIA_SERVICE(a,b,c,d,e) ((a)->chip_link->pcmcia_service(b,c,\ (void *) d,e)) #define PCMCIA_MAP_IO(a,b,c,d,e) ((a)->chip_link->pcmcia_map_io(b,c,d,e)) #define PCMCIA_MAP_INTR(a,b,c,d) ((a)->chip_link->pcmcia_map_intr(b,c,d)) -#define PCMCIA_MAP_MEM(a,b,c,d,e,f) ((a)->chip_link->pcmcia_map_mem(b,c,d,e,f)) - -#define PCMCIA_BUS_INIT(a,b,c,d,e,f)((a)->bus_link->bus_init((b),(c),(d),(e)\ - ,(f))) -#define PCMCIA_BUS_SEARCH(a,b,c,d) ((a)->bus_link->bus_search((b),(c),(d))) -#define PCMCIA_BUS_PROBE(a,b,c,d,e) ((a)->bus_link->bus_probe((b),(c),(d),(e))) -#define PCMCIA_BUS_CONFIG(a,b,c,d,e)((a)->bus_link->bus_config((b),(c),(d),(e))) -#define PCMCIA_BUS_UNCONFIG(a,b) ((a)->bus_link->bus_unconfig((b))) +/* XXX + * this is quite broken in the face of various bus mapping stuff... + * drivers need to cooperate with the pcmcia framework to deal with + * bus mapped memory. Whee. + */ +#define PCMCIA_MAP_MEM(a,b,c,d,e,f,g) ((a)->chip_link->pcmcia_map_mem(b,c,d,e,f,g)) -#define SCRATCH_MEM(a) ((a)->scratch_mem) +#define SCRATCH_MEM(a) ((a)->scratch_memh) +#define SCRATCH_BC(a) ((a)->pa_bc) #define SCRATCH_SIZE(a) ((a)->scratch_memsiz) #define SCRATCH_INUSE(a)((a)->scratch_inuse) /* * Declarations */ -struct pcmciadevs *pcmcia_probedev __P((struct pcmcia_link *)); -struct pcmciadevs *pcmcia_selectdev __P((char *, char *, char *, char *)); -int pcmcia_probe_bus __P((struct pcmcia_link *, int, int, - struct pcmcia_conf *)); +int pcmcia_probedev __P((struct pcmcia_link *, struct pcmcia_cardinfo *)); +int pcmcia_probe_bus __P((int, int)); int pcmciabusmatch __P((struct device *, void *, void *)); void pcmciabusattach __P((struct device *, struct device *, void *)); +int pcmcia_mapcard __P((struct pcmcia_link *, int, struct pcmcia_conf *)); -struct cfdriver pcmciabuscd = { - NULL, "pcmcia", pcmciabusmatch, pcmciabusattach, DV_DULL, - sizeof(struct pcmciabus_softc), 1 -}; - -#ifdef notyet -int -pcmcia_add_device(devs) - struct pcmciadevs *devs; -{ - int i; - - if (devs == NULL) - return 0; +int pcmcia_unconfigure __P((struct pcmcia_link *)); +int pcmcia_unmapcard __P((struct pcmcia_link *)); - for (i = 0; i < KNOWNSIZE; i++) - if (knowndevs[i] == NULL) - break; +int pcmcia_print __P((void *, char *)); +int pcmcia_submatch __P((struct device *, void *, void *)); +void pcmcia_probe_link __P((struct pcmcia_link *)); - if (i == KNOWNSIZE) - panic("Too many pcmcia devices"); +struct cfattach pcmcia_ca = { + sizeof(struct pcmciabus_softc), pcmciabusmatch, pcmciabusattach, +}; - knowndevs[i] = devs; - for (; devs->devname != NULL; devs++) - printf("added %s\n", devs->devname); - return i; -} -#endif +struct cfdriver pcmcia_cd = { + NULL, "pcmcia", DV_DULL, 1 +}; +#if 0 int pcmcia_register(adapter_softc, bus_link, chip_link, slot) void *adapter_softc; @@ -178,6 +133,7 @@ pcmcia_register(adapter_softc, bus_link, chip_link, slot) } return 0; } +#endif int pcmciabusmatch(parent, self, aux) @@ -187,18 +143,16 @@ pcmciabusmatch(parent, self, aux) { struct pcmciabus_softc *sc = (void *)self; struct cfdata *cf = sc->sc_dev.dv_cfdata; - int i, found = 0; + struct pcmciabus_attach_args *pba = aux; + struct pcmcia_adapter *pca = pba->pba_aux; + int found = 0; - PPRINTF(("- pcmciabusmatch\n")); - if (pcmcia_cntrl <= 0) - return 0; + PPRINTF(("- pcmciabusmatch %p %p\n", pba, pca)); - for (i = 0; i < 4; i++) - if (pcmcia_drivers[i].bus_link) { - if (PCMCIA_BUS_INIT(&pcmcia_drivers[i], parent, cf, - aux, &pcmcia_drivers[i], 0)) - found++; - } + if (pca->bus_link) { + if (PCMCIA_BUS_INIT(pca, parent, cf, aux, pca, 0)) + found++; + } return found != 0; } @@ -214,19 +168,18 @@ pcmciabusattach(parent, self, aux) { struct pcmciabus_softc *sc = (struct pcmciabus_softc *) self; struct cfdata *cf = self->dv_cfdata; - int i, found = 0; + struct pcmciabus_attach_args *pba = aux; + struct pcmcia_adapter *pca = pba->pba_aux; PPRINTF(("- pcmciabusattach\n")); - for (i = 0; i < 4; i++) - if (pcmcia_drivers[i].bus_link) { - if (PCMCIA_BUS_INIT(&pcmcia_drivers[i], parent, cf, - aux, &pcmcia_drivers[i], 1)) - found++; - } - + if (pca->bus_link) { + PCMCIA_BUS_INIT(pca, parent, cf, aux, pca, 1); + } printf("\n"); - pcmcia_probe_bus(NULL, sc->sc_dev.dv_unit, -1, NULL); + sc->sc_driver = pca; + sc->sc_bc = pba->pba_bc; + pcmcia_probe_bus(sc->sc_dev.dv_unit, -1); } /* @@ -239,52 +192,52 @@ pcmcia_probe_busses(bus, slot) { PPRINTF(("- pcmcia_probe_busses\n")); if (bus == -1) { - for (bus = 0; bus < pcmciabuscd.cd_ndevs; bus++) - if (pcmciabuscd.cd_devs[bus]) - pcmcia_probe_bus(NULL, bus, slot, NULL); + for (bus = 0; bus < pcmciabus_cd.cd_ndevs; bus++) + if (pcmciabus_cd.cd_devs[bus]) + pcmcia_probe_bus(bus, slot); return 0; } else { - return pcmcia_probe_bus(NULL, bus, slot, NULL); + return pcmcia_probe_bus(bus, slot); } } +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + /* * Probe the requested pcmcia bus. It must be already set up. */ int -pcmcia_probe_bus(link, bus, slot, cf) - struct pcmcia_link *link; +pcmcia_probe_bus(bus, slot) int bus, slot; - struct pcmcia_conf *cf; { struct pcmciabus_softc *pcmcia; - int maxslot, minslot, maxlun, minlun; - struct pcmciadevs *bestmatch = NULL; - int spec_probe = (link != NULL); + int maxslot, minslot; + struct pcmcia_link *link; PPRINTF(("- pcmcia_probe_bus\n")); - if (bus < 0 || bus >= pcmciabuscd.cd_ndevs) + if (bus < 0 || bus >= pcmciabus_cd.cd_ndevs) return ENXIO; - pcmcia = pcmciabuscd.cd_devs[bus]; - if (!pcmcia) + pcmcia = pcmciabus_cd.cd_devs[bus]; + if (!pcmcia || pcmcia->sc_driver == NULL) /* bus is not configured */ return ENXIO; if (slot == -1) { - maxslot = 7; + maxslot = pcmcia->sc_driver->nslots - 1; minslot = 0; } else { - if (slot < 0 || slot > 7) + if (slot < 0 || slot >= pcmcia->sc_driver->nslots) return EINVAL; maxslot = minslot = slot; } for (slot = minslot; slot <= maxslot; slot++) { - if (link = pcmcia->sc_link[slot]) { + if ((link = pcmcia->sc_link[slot])) { if (link->devp) continue; } - if (pcmcia_drivers[slot >> 1].adapter_softc == NULL) - continue; /* * If we presently don't have a link block @@ -297,88 +250,91 @@ pcmcia_probe_bus(link, bus, slot, cf) return ENOMEM; bzero(link, sizeof(*link)); link->opennings = 1; - link->adapter = &pcmcia_drivers[slot >> 1]; + link->adapter = pcmcia->sc_driver; + link->bus = pcmcia; link->slot = slot; } - bestmatch = pcmcia_probedev(link); - /* - * We already know what the device is. We use a - * special matching routine which insists that the - * cfdata is of the right type rather than putting - * more intelligence in individual match routines for - * each high-level driver. - * We must have the final probe do all of the comparisons, - * or we could get stuck in an infinite loop trying the same - * device repeatedly. We use the `fordriver' field of - * the pcmcia_link for now, rather than inventing a new - * structure just for the config_search(). - */ - if (link->fordriver == NULL) { - if (bestmatch) - link->fordriver = bestmatch->devname; - else { - if (!spec_probe) { - link->device = NULL; - link->devp = NULL; - PCMCIA_SERVICE(link->adapter, - link, PCMCIA_OP_POWER, - 0, 0); - } - } - } + (void) pcmcia_probe_link(link); + } + return 0; +} - if (spec_probe) { - if (cf && pcmcia_mapcard(link, -1, cf) != 0) - link->fordriver = NULL; - } +void +pcmcia_probe_link(link) + struct pcmcia_link *link; +{ + struct pcmcia_cardinfo cardinfo; + struct pcmcia_attach_args paa; + struct pcmciabus_softc *pcmcia = link->bus; + int i; - if (link->fordriver != NULL) { - int i; - struct device **delp = deldevs; - int found = 0; - link->device = bestmatch; - link->flags = (link->flags & - ~(PCMCIA_ATTACH_TYPE)) | - PCMCIA_REATTACH; - for (i = 0; i < ndeldevs; i++, delp++) { - if (*delp && - pcmcia_configure((*delp)->dv_parent, *delp, - link)) { - link->flags = (link->flags & - ~PCMCIA_ATTACH_TYPE) - | PCMCIA_SLOT_INUSE; - found = 1; - *delp = NULL; - break; - } - } - if (!found) { - link->flags = (link->flags & - ~PCMCIA_ATTACH_TYPE) | PCMCIA_ATTACH; - if (PCMCIA_BUS_SEARCH(link->adapter, - &pcmcia->sc_dev, - link, NULL)) { - link->flags = (link->flags & - ~PCMCIA_ATTACH_TYPE) - | PCMCIA_SLOT_INUSE; - } else { - link->flags &= ~(PCMCIA_ATTACH_TYPE | - PCMCIA_SLOT_INUSE); - link->device = NULL; - printf( - "No matching config entry %s.\n", - link->fordriver ? - link->fordriver : "(NULL)"); - if (!spec_probe) - PCMCIA_SERVICE(link->adapter, - link, - PCMCIA_OP_POWER, - 0, 0); - } + PPRINTF(("- pcmcia_probe_link %p\n", link)); + /* + * Set up card and fetch card info. + */ + if (pcmcia_probedev(link, &cardinfo) == 0) { + /* could not fetch its strings, so give up on it. */ + PCMCIA_SERVICE(link->adapter, + link, PCMCIA_OP_POWER, + 0, 0); + return; + } + + /* + * See if we can reattach a device. + */ + CLR(link->flags, PCMCIA_ATTACH_TYPE); + SET(link->flags, PCMCIA_REATTACH); + for (i = 0; i < ndeldevs; i++) { + if (deldevs[i].dev) { + PPRINTF(("trying device\n")); + link->device = deldevs[i].pcdev; + if (pcmcia_configure(deldevs[i].dev->dv_parent, + deldevs[i].dev, link)) { + CLR(link->flags, PCMCIA_ATTACH_TYPE); + SET(link->flags, PCMCIA_SLOT_INUSE); + deldevs[i].dev = NULL; + deldevs[i].pcdev = NULL; + return; } } } - return 0; + + + paa.paa_cardinfo = &cardinfo; + paa.paa_link = link; + paa.paa_aux = NULL; + paa.paa_bestmatch = 0; + paa.paa_matchonly = 1; + CLR(link->flags, PCMCIA_ATTACH_TYPE); + SET(link->flags, PCMCIA_ATTACH); + + /* Run the config matching routines to find us a good match. + * match routines will flag on "matchonly" and fill in stuff + * into the link structure, but not return any match. + */ + (void) config_found_sm(&pcmcia->sc_dev, + &paa, + pcmcia_print, + pcmcia_submatch); + + if (PCMCIA_BUS_SEARCH(link->adapter, + &pcmcia->sc_dev, + link, NULL)) { + CLR(link->flags, PCMCIA_ATTACH_TYPE); + SET(link->flags, PCMCIA_SLOT_INUSE); + } else { + CLR(link->flags, PCMCIA_ATTACH_TYPE|PCMCIA_SLOT_INUSE); + link->device = NULL; + printf("%s slot %d: No matching config entry.\n", + pcmcia->sc_dev.dv_xname, + link->slot); + PCMCIA_SERVICE(link->adapter, + link, PCMCIA_OP_POWER, + 0, 0); + link->fordriver = NULL; + } + return; } /* @@ -386,16 +342,13 @@ pcmcia_probe_bus(link, bus, slot, cf) * it is, and find the correct driver table * entry. */ -struct pcmciadevs * -pcmcia_probedev(link) +int +pcmcia_probedev(link, cardinfo) struct pcmcia_link *link; + struct pcmcia_cardinfo *cardinfo; { struct pcmcia_adapter *pca = link->adapter; u_char scratch[CIS_MAXSIZE]; - char manu[MAX_CIS_NAMELEN]; - char model[MAX_CIS_NAMELEN]; - char add_inf1[MAX_CIS_NAMELEN]; - char add_inf2[MAX_CIS_NAMELEN]; int card_stat; int err; int pow = 0; @@ -403,22 +356,22 @@ pcmcia_probedev(link) PPRINTF(("- pcmcia_probe_dev\n")); - printf("%s slot %d:", - ((struct device *) link->adapter->adapter_softc)->dv_xname, - slot & 1); + printf("%s: slot %d ", ((struct device *) link->bus)->dv_xname, slot); + /* turn off power in case it's on, to get a fresh start on things: */ + PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 0, 0); if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS, &card_stat, 0)) != 0) { printf("failed to get status %d\n", err); return NULL; } - if ((card_stat & PCMCIA_CARD_PRESENT) == 0) { - printf(" <slot empty>\n"); + if (ISSET(card_stat, PCMCIA_CARD_PRESENT) == 0) { + printf("is empty\n"); return NULL; } - if (!(card_stat & PCMCIA_POWER)) { + if (!ISSET(card_stat, PCMCIA_POWER)) { pow = 1; if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 10000, PCMCIA_POWER_ON| @@ -428,7 +381,7 @@ pcmcia_probedev(link) } } - if (!(link->flags & (PCMCIA_SLOT_INUSE | CARD_IS_MAPPED))) { + if (!ISSET(link->flags, (PCMCIA_SLOT_INUSE | CARD_IS_MAPPED))) { if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, 500000, 0)) != 0) { printf("failed to reset %d\n", err); @@ -446,127 +399,25 @@ pcmcia_probedev(link) } if ((err = pcmcia_get_cisver1(link, scratch, sizeof(scratch), - manu, model, add_inf1, - add_inf2)) != 0) { + cardinfo->manufacturer, + cardinfo->model, cardinfo->add_info1, + cardinfo->add_info2)) != 0) { printf("failed to get cis info %d\n", err); goto bad; } - printf(" <%s, %s", manu, model); - if (add_inf1[0]) - printf(", %s", add_inf1); - if (add_inf2[0]) - printf(", %s", add_inf2); + printf("contains <%s, %s", cardinfo->manufacturer, cardinfo->model); + if (cardinfo->add_info1[0]) + printf(", %s", cardinfo->add_info1); + if (cardinfo->add_info2[0]) + printf(", %s", cardinfo->add_info2); printf(">\n"); - - /* - * Try make as good a match as possible with - * available sub drivers - */ - return pcmcia_selectdev(manu, model, add_inf1, add_inf2); + return 1; bad: if (!pow) PCMCIA_SERVICE(pca, link, PCMCIA_OP_POWER, 0, 0); - return NULL; -} - -/* - * Try make as good a match as possible with - * available sub drivers - */ -struct pcmciadevs * -pcmcia_selectdev(manu, model, add_inf1, add_inf2) - char *manu, *model, *add_inf1, *add_inf2; -{ - u_int bestmatches = 0; - struct pcmciadevs *bestmatch = (struct pcmciadevs *) 0; - struct pcmciadevs **dlist, *dentry; - - PPRINTF(("- pcmcia_selectdev\n")); - for (dlist = knowndevs; *dlist; dlist++) - for (dentry = *dlist; dentry && - dentry->devname != NULL; dentry++) { - int match = 0; - -#ifdef PCMCIA_DEBUG - dentry->flags |= PC_SHOWME; -#endif - match|=pcmcia_strcmp(dentry->manufacturer, - manu,dentry->flags,"manufacturer")<<6; - match|=pcmcia_strcmp(dentry->model, - model,dentry->flags,"model")<<4; - match|=pcmcia_strcmp(dentry->add_inf1, - add_inf1,dentry->flags,"info1")<<2; - match|=pcmcia_strcmp(dentry->add_inf2, - add_inf2,dentry->flags,"info2"); -/* the following was replaced by the wildcard function called above */ -#if 0 - if (dentry->flags & PC_SHOWME) - printf("manufacturer = `%s'-`%s'\n", - dentry->manufacturer ? - dentry->manufacturer : - "X", - manu); - if (dentry->manufacturer) { - if (strcmp(dentry->manufacturer, manu) == 0) { - match |= 8; - } else { - continue; - } - } - - if (dentry->flags & PC_SHOWME) - printf("model = `%s'-`%s'\n", - dentry->model ? dentry->model : - "X", - model); - if (dentry->model) { - if (strcmp(dentry->model, model) == 0) { - match |= 4; - } else { - continue; - } - } - - - if (dentry->flags & PC_SHOWME) - printf("info1 = `%s'-`%s'\n", - dentry->add_inf1 ? dentry->add_inf1 : - "X", - add_inf1); - if (dentry->add_inf1) { - if (strcmp(dentry->add_inf1, add_inf1) == 0) { - match |= 2; - } else { - continue; - } - } - - if (dentry->flags & PC_SHOWME) - printf("info2 = `%s'-`%s'\n", - dentry->add_inf2 ? dentry->add_inf2 : - "X", - add_inf2); - if (dentry->add_inf2) { - if (strcmp(dentry->add_inf2, add_inf2) == 0) { - match |= 1; - } else { - continue; - } - } -#endif -#ifdef PCMCIA_DEBUG - printf("match == %d [%d]\n",match,bestmatches); -#endif - - if(match > bestmatches) { - bestmatches = match; - bestmatch = dentry; - } - } - - return bestmatch; + return 0; } int @@ -579,26 +430,29 @@ pcmcia_configure(parent, self, aux) struct pcmcia_link *link = aux; struct cfdata *cf = dev->dv_cfdata; struct cfdriver *cd = cf->cf_driver; - char *devname = (char *) link->fordriver; struct pcmciadevs *pcs = link->device; struct pcmcia_device *pcd; struct pcmcia_adapter *pca = link->adapter; struct pcmcia_conf pc_cf; + char *devname = (char *) link->fordriver; u_char scratch[CIS_MAXSIZE]; int mymap = 0; PPRINTF(("- pcmcia_configure\n")); - if (strcmp(devname, cd->cd_name) || !pca) + if ((devname && strcmp(devname, cd->cd_name)) || !pca) return 0; + if (link->devp) + return 0; /* something else already attached */ + if (pcs == NULL) pcd = NULL; else pcd = pcs->dev; - PPRINTF(("pcmcia_configure: %x\n", pcd)); - if (!(link->flags & CARD_IS_MAPPED)) { + PPRINTF(("pcmcia_configure: %p\n", pcd)); + if (!ISSET(link->flags, CARD_IS_MAPPED)) { /* read 'suggested' configuration */ PPRINTF(("pcmcia_configure: calling read cis\n")); if (pcmcia_read_cis(link, scratch, 0, sizeof(scratch)) != 0) @@ -615,8 +469,9 @@ pcmcia_configure(parent, self, aux) #endif /* and modify it (device specific) */ if (pcd && pcd->pcmcia_config) { - PPRINTF(("pcmcia_configure: calling config\n")); - if (pcd->pcmcia_config(link, dev, &pc_cf, cf)) + PPRINTF(("pcmcia_configure: calling config %p %p\n", + pcd, pcd->pcmcia_config)); + if ((*pcd->pcmcia_config)(link, dev, &pc_cf, cf)) return 0; if ((pc_cf.cfgtype & CFGENTRYMASK) == CFGENTRYID) { @@ -649,7 +504,7 @@ pcmcia_configure(parent, self, aux) } link->devp = dev; - PPRINTF(("pcmcia_configure: calling bus probe\n")); + PPRINTF(("pcmcia_configure: calling bus attach\n")); if (!(PCMCIA_BUS_PROBE(pca, parent, dev, cf, link))) { PPRINTF(("pcmcia_configure: bus probe failed\n")); goto bad; @@ -660,39 +515,34 @@ pcmcia_configure(parent, self, aux) goto bad; } - link->shuthook = shutdownhook_establish(pcmcia_shuthook, - (void *)link); return 1; bad: link->devp = NULL; if (mymap) pcmcia_unmapcard(link); - printf("pcmcia_configure: configuration error\n"); + PPRINTF(("pcmcia_configure: configuration error\n")); return 0; } void -pcmcia_shuthook(arg) -void *arg; +pcmcia_detach(dev, arg) + struct device *dev; + void *arg; { - struct pcmcia_link *link = (struct pcmcia_link *)arg; - if (pcmcia_unconfigure(link) == 0) { - /* - * turn off power too. - */ - PCMCIA_SERVICE(link->adapter, link, PCMCIA_OP_RESET, 500000, 0); - PCMCIA_SERVICE(link->adapter, link, PCMCIA_OP_POWER, 0, 0); - } + struct pcmcia_link *link = arg; + + link->devp = NULL; + printf("%s: device %s at slot %d detached/really\n", + dev->dv_parent->dv_xname, + dev->dv_xname, link->slot); } int pcmcia_unconfigure(link) struct pcmcia_link *link; { - int status; - int i, err; - struct device **delp; + int i; struct device *dev; struct pcmcia_adapter *pca = link->adapter; struct pcmcia_device *pcd; @@ -706,9 +556,9 @@ pcmcia_unconfigure(link) else pcd = NULL; - if (link->flags & CARD_IS_MAPPED) { + if (ISSET(link->flags, CARD_IS_MAPPED)) { if (pcd && pcd->pcmcia_remove) { - if (pcd->pcmcia_remove(link, link->devp)) + if ((*pcd->pcmcia_remove)(link, link->devp)) return EBUSY; } else { @@ -718,39 +568,56 @@ pcmcia_unconfigure(link) if (pcmcia_unmapcard(link) != 0) return EBUSY; } - delp = deldevs; - for (i = 0; delp && *delp && i < ndeldevs; i++, delp++) - continue; - if (i >= ndeldevs) { - int sz = ndeldevs ? (ndeldevs * 2) : - (MINALLOCSIZE / sizeof(void *)); - struct device **ndel = malloc(sz * sizeof(void *), - M_DEVBUF, M_NOWAIT); - if (!ndel) { - PPRINTF(("pcmcia_delete: creating dev array")); - return ENOMEM; - } - bzero(ndel, sz * sizeof(void *)); - if (ndeldevs) { - bcopy(deldevs, ndel, ndeldevs * sizeof(void *)); - free(deldevs, M_DEVBUF); + if (config_detach(link->devp->dv_cfdata, pcmcia_detach, link)) { + /* must be retained */ + for (i = 0; deldevs && deldevs[i].dev && i < ndeldevs; i++) + continue; + + if (i >= ndeldevs) { + int sz = ndeldevs ? (ndeldevs * 2) : + (MINALLOCSIZE / sizeof(deldevs[0])); + struct old_devs *ndel = malloc(sz * sizeof(deldevs[0]), + M_DEVBUF, M_NOWAIT); + if (!ndel) { + PPRINTF(("pcmcia_delete: creating dev array")); + return ENOMEM; + } + bzero(ndel, sz * sizeof(ndel[0])); + if (ndeldevs) { + bcopy(deldevs, ndel, + ndeldevs * sizeof(deldevs[0])); + free(deldevs, M_DEVBUF); + } + ndeldevs = sz - 1; + deldevs = ndel; } - ndeldevs = sz - 1; - deldevs = ndel; - delp = deldevs + i; + dev = deldevs[i].dev = link->devp; + deldevs[i].pcdev = link->device; + link->devp = NULL; + TAILQ_REMOVE(&alldevs, dev, dv_list); + printf("%s: device %s at slot %d detached/retained\n", + dev->dv_parent->dv_xname, + dev->dv_xname, link->slot); + /* + * Make this node eligible to probe again. + * Since we're indirectly allocating state, + * this device data will not get trashed later and we + * can hold onto it. + */ +/* dev->dv_cfdata->cf_fstate = FSTATE_NOTFOUND;*/ } - dev = *delp = link->devp; - link->devp = NULL; - printf("device %s in pcmcia slot %d detached\n", dev->dv_xname, - link->slot); - shutdownhook_disestablish(link->shuthook); - link->shuthook = 0; return 0; } +/* + * Map the card into I/O and memory space, using the details provided + * with pc_cf. + */ + int pcmcia_mapcard(link, unit, pc_cf) struct pcmcia_link *link; + int unit; struct pcmcia_conf *pc_cf; { struct pcmcia_adapter *pca = link->adapter; @@ -768,6 +635,7 @@ pcmcia_mapcard(link, unit, pc_cf) splx(s); for (i = 0; i < pc_cf->memwin; i++) { if ((err = PCMCIA_MAP_MEM(pca, link, + pca->pa_bc, (caddr_t) pc_cf->mem[i].start, pc_cf->mem[i].caddr, pc_cf->mem[i].len, @@ -797,7 +665,7 @@ pcmcia_mapcard(link, unit, pc_cf) } } /* Now we've mapped everything enable it */ - if ((err = PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca), + if ((err = PCMCIA_MAP_MEM(pca, link, SCRATCH_BC(pca), SCRATCH_MEM(pca), pc_cf->cfg_off & (~(SCRATCH_SIZE(pca) - 1)), SCRATCH_SIZE(pca), PCMCIA_MAP_ATTR | PCMCIA_LAST_WIN)) != 0) { PPRINTF(("pcmcia_mapcard: enable err %d\n", err)); @@ -810,45 +678,59 @@ pcmcia_mapcard(link, unit, pc_cf) goto error; } -#define GETMEM(x) SCRATCH_MEM(pca)[(pc_cf->cfg_off & \ - (SCRATCH_SIZE(pca) - 1)) + x] - if ((pc_cf->cfgtype & DOSRESET)) { - GETMEM(0) = PCMCIA_SRESET; +#define GETMEM(x) bus_mem_read_1(pca->scratch_bc, SCRATCH_MEM(pca), \ + (pc_cf->cfg_off & (SCRATCH_SIZE(pca)-1)) + x) +#define PUTMEM(x,v) \ + bus_mem_write_1(pca->scratch_bc, SCRATCH_MEM(pca), \ + (pc_cf->cfg_off & (SCRATCH_SIZE(pca)-1)) + x, v) + + if (ISSET(pc_cf->cfgtype, DOSRESET)) { + PUTMEM(0, PCMCIA_SRESET); delay(50000); } - PPRINTF(("CMDR %x\n",((pc_cf->cfgtype & CFGENTRYID) ? + PPRINTF(("CMDR %x\n",(ISSET(pc_cf->cfgtype, CFGENTRYID) ? pc_cf->cfgid |CFGENTRYID: (pc_cf->cfgtype & CFGENTRYMASK)|1)| (pc_cf->irq_level ? PCMCIA_LVLREQ : 0) )); - GETMEM(0) = ((pc_cf->cfgtype & CFGENTRYID) ? - pc_cf->cfgid |CFGENTRYID: - (pc_cf->cfgtype & CFGENTRYMASK)|1)| - (pc_cf->irq_level ? PCMCIA_LVLREQ : 0); + PUTMEM(0, (ISSET(pc_cf->cfgtype, CFGENTRYID) ? + pc_cf->cfgid |CFGENTRYID: + (pc_cf->cfgtype & CFGENTRYMASK)|1)| + (pc_cf->irq_level ? PCMCIA_LVLREQ : 0)); delay(50000); - if (pc_cf->cfg_regmask & (1 << (PCMCIA_SCR / 2))) - GETMEM(PCMCIA_SCR) = (link->slot & 1) | 0x10; + if (ISSET(pc_cf->cfg_regmask, (1 << (PCMCIA_SCR / 2)))) + PUTMEM(PCMCIA_SCR, (link->slot & 1) | 0x10); #if 0 DPRINTF(("CCSR %x\n", GETMEM(PCMCIA_CCSR])); - if (GETMEM(PCMCIA_CCSR] & PCMCIA_POWER_DOWN) { - GETMEM(PCMCIA_CCSR] &= ~PCMCIA_POWER_DOWN; - DPRINTF(("CCSR now %x\n", GETMEM(PCMCIA_CCSR])); + if (ISSET(GETMEM(PCMCIA_CCSR), PCMCIA_POWER_DOWN)) { + u_char val = GETMEM(PCMCIA_CCSR); + CLR(val, PCMCIA_POWER_DOWN); + PUTMEM(PCMCIA_CCSR, var); + DPRINTF(("CCSR now %x\n", GETMEM(PCMCIA_CCSR))); } #endif + + PPRINTF(("pcmcia_mapcard: about to initialize...\n")); + if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_WAIT, - 500000, 0)) != 0) + 1000, 0)) != 0) { PPRINTF(("failed to initialize %d\n", err)); + err = 0; /* XXX */ + } error: - PCMCIA_MAP_MEM(pca, link, 0, 0, 0, PCMCIA_LAST_WIN | PCMCIA_UNMAP); + PCMCIA_MAP_MEM(pca, link, SCRATCH_BC(pca), SCRATCH_MEM(pca), 0, + SCRATCH_SIZE(pca), PCMCIA_LAST_WIN | PCMCIA_UNMAP); if (err != 0) { + PPRINTF(("pcmcia_mapcard: unmaping\n")); for (i = 0; i < pc_cf->memwin; i++) { PCMCIA_MAP_MEM(pca, link, + pca->pa_bc, (caddr_t) pc_cf->mem[i].start, pc_cf->mem[i].caddr, pc_cf->mem[i].len, @@ -863,12 +745,12 @@ error: PCMCIA_MAP_8)) | i | PCMCIA_UNMAP); } PCMCIA_MAP_INTR(pca, link, pc_cf->irq_num, PCMCIA_UNMAP); - link->flags &= ~CARD_IS_MAPPED; + CLR(link->flags, CARD_IS_MAPPED); link->iowin = 0; link->memwin = 0; link->intr = 0; } else { - link->flags |= CARD_IS_MAPPED; + SET(link->flags, CARD_IS_MAPPED); link->iowin = pc_cf->iowin; link->memwin = pc_cf->memwin; link->intr = pc_cf->irq_num; @@ -891,14 +773,15 @@ pcmcia_unmapcard(link) return ENODEV; for (i = 0; i < link->memwin; i++) - PCMCIA_MAP_MEM(pca, link, 0, 0, 0, (i | PCMCIA_UNMAP)); + PCMCIA_MAP_MEM(pca, link, pca->pa_bc, 0, 0, 0, + (i | PCMCIA_UNMAP)); for (i = 0; i < link->iowin; i++) PCMCIA_MAP_IO(pca, link, 0, 0, (i | PCMCIA_UNMAP)); PCMCIA_MAP_INTR(pca, link, link->intr, PCMCIA_UNMAP); - PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, 0, 0); - link->flags &= ~(CARD_IS_MAPPED | PCMCIA_SLOT_INUSE); + PCMCIA_SERVICE(pca, link, PCMCIA_OP_RESET, 50000, 0); + CLR(link->flags, (CARD_IS_MAPPED | PCMCIA_SLOT_INUSE)); link->iowin = 0; link->memwin = 0; link->intr = 0; @@ -906,30 +789,28 @@ pcmcia_unmapcard(link) } -static int +int pcmcia_mapcard_and_configure(link, unit, pc_cf) struct pcmcia_link *link; struct pcmcia_conf *pc_cf; int unit; { - int err; int mymap = 0; + int err; PPRINTF(("- pcmcia_mapcard_and_configure\n")); if (pc_cf->driver_name[0][0]) { -#if 0 if ((err = pcmcia_mapcard(link, unit, pc_cf)) != 0) { return err; } mymap=1; -#endif link->fordriver = pc_cf->driver_name[0]; - } else { + } else link->fordriver = NULL; - pc_cf = NULL; - } - pcmcia_probe_bus(link, 0, link->slot, pc_cf); - if ((link->flags & PCMCIA_SLOT_INUSE) == 0) { + + pcmcia_probe_link(link); + + if (!ISSET(link->flags, PCMCIA_SLOT_INUSE)) { if (mymap) pcmcia_unmapcard(link); return ENODEV; @@ -952,7 +833,7 @@ pcmcia_read_cis(link, scratch, offs, len) int size = SCRATCH_SIZE(pca); volatile int *inuse = &SCRATCH_INUSE(pca); - PPRINTF(("- pcmcia_read_cis\n")); + PPRINTF(("- pcmcia_read_cis: mem %p size %d\n", p, size)); if (pca == NULL) return ENXIO; @@ -968,15 +849,18 @@ pcmcia_read_cis(link, scratch, offs, len) int tlen = min(len + toff, size / 2) - toff; int i; - if ((err = PCMCIA_MAP_MEM(pca, link, p, pgoff, size, + if ((err = PCMCIA_MAP_MEM(pca, link, pca->pa_bc, p, pgoff, + size, PCMCIA_MAP_ATTR | PCMCIA_LAST_WIN)) != 0) goto error; + PPRINTF(("- pcmcia_read_cis: mem mapped\n")); + for (i = 0; i < tlen; j++, i++) scratch[j] = p[toff + i * 2]; - PCMCIA_MAP_MEM(pca, link, p, 0, size, + PCMCIA_MAP_MEM(pca, link, pca->pa_bc, p, 0, size, PCMCIA_LAST_WIN | PCMCIA_UNMAP); len -= tlen; } @@ -986,21 +870,25 @@ error: wakeup((caddr_t) inuse); splx(s); + PPRINTF(("- pcmcia_read_cis return %d\n", err)); return err; } /* here we start our pseudodev for controlling the slots */ #define PCMCIABUS_UNIT(a) (minor(a)) -#define PCMCIABUS_SLOT(a) (a&0x7) -#define PCMCIABUS_CHIPIID(a) (a&0x3) -#define PCMCIABUS_CHIP 0x10 -#define PCMCIABUS_BUS 0x20 +#define PCMCIABUS_SLOT(a) (a&0x3) /* per-controller */ +#define PCMCIABUS_SLOTID(a) (a&0xf) /* system-wide assignment */ +#define PCMCIABUS_CHIPNO(a) ((a&0xf)>>2) +#define PCMCIABUS_CHIPID(a) (a&0x3) +#define PCMCIABUS_CHIP 0x40 +#define PCMCIABUS_BUS 0x80 +#define PCMCIABUS_BUSID(a) (a&0x3) #define PCMCIABUS_DEVTYPE(a) ((a)&(PCMCIABUS_CHIP|PCMCIABUS_BUS)) static int busopen = 0; static int chipopen[4] = {0, 0, 0, 0}; int -pcmciabusopen(dev, flag, mode, p) +pcmciaopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; @@ -1011,8 +899,6 @@ pcmciabusopen(dev, flag, mode, p) struct pcmciabus_softc *pcmcia; PPRINTF(("- pcmciabusopen\n")); - if (pcmcia_cntrl == 0) - return ENXIO; switch (PCMCIABUS_DEVTYPE(unit)) { case PCMCIABUS_BUS: if (unit != PCMCIABUS_BUS) @@ -1023,10 +909,11 @@ pcmciabusopen(dev, flag, mode, p) break; case PCMCIABUS_CHIP: - chipid = PCMCIABUS_CHIPIID(unit); - if (chipid > 3) + chipid = PCMCIABUS_CHIPID(unit); + if (chipid < 0 || chipid >= pcmciabus_cd.cd_ndevs) return ENXIO; - if (pcmcia_drivers[chipid].adapter_softc == NULL) + pcmcia = pcmciabus_cd.cd_devs[chipid]; + if (pcmcia == NULL || pcmcia->sc_driver == NULL) return ENXIO; if (chipopen[chipid]) @@ -1037,20 +924,22 @@ pcmciabusopen(dev, flag, mode, p) case 0: slot = PCMCIABUS_SLOT(unit); - chipid = slot >> 1; + chipid = PCMCIABUS_CHIPNO(unit); - if (chipid > 7) + if (chipid < 0 || chipid >= pcmciabus_cd.cd_ndevs) return ENXIO; - if (pcmcia_drivers[chipid].adapter_softc == NULL) + pcmcia = pcmciabus_cd.cd_devs[chipid]; + if (pcmcia == NULL || pcmcia->sc_driver == NULL) return ENXIO; - pcmcia = pcmciabuscd.cd_devs[0]; link = pcmcia->sc_link[slot]; + if (!link) + return ENXIO; - if (link->flags & PCMCIA_SLOT_OPEN) + if (ISSET(link->flags, PCMCIA_SLOT_OPEN)) return EBUSY; - link->flags |= PCMCIA_SLOT_OPEN; + SET(link->flags, PCMCIA_SLOT_OPEN); break; default: @@ -1062,7 +951,7 @@ pcmciabusopen(dev, flag, mode, p) int -pcmciabusclose(dev) +pcmciaclose(dev) { int unit = PCMCIABUS_UNIT(dev); int chipid, slot; @@ -1071,25 +960,24 @@ pcmciabusclose(dev) int s; PPRINTF(("- pcmciabusclose\n")); - if (pcmcia_cntrl == 0) - return ENXIO; switch (PCMCIABUS_DEVTYPE(unit)) { case PCMCIABUS_BUS: busopen = 0; break; case PCMCIABUS_CHIP: - chipid = PCMCIABUS_CHIPIID(unit); + chipid = PCMCIABUS_CHIPID(unit); chipopen[chipid] = 0; break; case 0: slot = PCMCIABUS_SLOT(unit); - pcmcia = pcmciabuscd.cd_devs[0]; + chipid = PCMCIABUS_CHIPNO(unit); + pcmcia = pcmciabus_cd.cd_devs[chipid]; link = pcmcia->sc_link[slot]; s = splclock(); - link->flags &= ~(PCMCIA_SLOT_OPEN|PCMCIA_SLOT_EVENT); + CLR(link->flags, (PCMCIA_SLOT_OPEN|PCMCIA_SLOT_EVENT)); splx(s); break; @@ -1104,8 +992,8 @@ pcmciachip_ioctl(chipid, cmd, data) int chipid, cmd; caddr_t data; { - int err = 0; - struct pcmcia_adapter *pca = &pcmcia_drivers[chipid]; + struct pcmciabus_softc *pcmcia = pcmciabus_cd.cd_devs[chipid]; + struct pcmcia_adapter *pca = pcmcia->sc_driver; struct pcmcia_link link; struct pcmcia_regs *pi = (void *) data; @@ -1117,7 +1005,7 @@ pcmciachip_ioctl(chipid, cmd, data) case PCMCIAIO_READ_REGS: pi->chip = chipid; link.adapter = pca; - link.slot = chipid << 1; + link.slot = 0; return PCMCIA_SERVICE(pca, &link, PCMCIA_OP_GETREGS, pi->chip_data, 0); } @@ -1131,7 +1019,9 @@ pcmciaslot_ioctl(link, slotid, cmd, data) caddr_t data; { int err = 0; - struct pcmcia_adapter *pca = &pcmcia_drivers[slotid >> 1]; + struct pcmciabus_softc *pcmcia = + pcmciabus_cd.cd_devs[PCMCIABUS_CHIPNO(slotid)]; + struct pcmcia_adapter *pca = pcmcia->sc_driver; PPRINTF(("- pcmciaslot_ioctl\n")); if (link == NULL || pca->chip_link == NULL || @@ -1147,10 +1037,10 @@ pcmciaslot_ioctl(link, slotid, cmd, data) err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS, &pi->status, 0); if (!err) { - pi->status |= ((link->flags & CARD_IS_MAPPED) ? - PCMCIA_CARD_IS_MAPPED : 0) | - ((link->flags & PCMCIA_SLOT_INUSE) ? - PCMCIA_CARD_INUSE : 0); + if (ISSET(link->flags, CARD_IS_MAPPED)) + SET(pi->status, PCMCIA_CARD_IS_MAPPED); + if (ISSET(link->flags, PCMCIA_SLOT_INUSE)) + SET(pi->status, PCMCIA_CARD_INUSE); } return err; } @@ -1163,7 +1053,7 @@ pcmciaslot_ioctl(link, slotid, cmd, data) if ((err = PCMCIA_SERVICE(pca, link, PCMCIA_OP_STATUS, &status, 0)) != 0) return err; - if ((status & PCMCIA_CARD_PRESENT) == 0) + if (!ISSET(status, PCMCIA_CARD_PRESENT)) return ENODEV; pi->slot = slotid; return pcmcia_read_cis(link, pi->cis_data, 0, @@ -1180,6 +1070,8 @@ pcmciaslot_ioctl(link, slotid, cmd, data) return pcmcia_unconfigure(link); case PCMCIAIO_UNMAP: + if (ISSET(link->flags, PCMCIA_SLOT_INUSE)) + return EBUSY; return pcmcia_unmapcard(link); case PCMCIAIO_SET_POWER: @@ -1226,11 +1118,11 @@ pcmciaslot_ioctl(link, slotid, cmd, data) &status, 0); if (err) return err; - if ((status & PCMCIA_CARD_PRESENT) == 0) + if (!ISSET(status, PCMCIA_CARD_PRESENT)) return ENODEV; - if (status = pcmcia_read_cis(link, pi->cis_data, 0, - CIS_MAXSIZE)) + if ((status = pcmcia_read_cis(link, pi->cis_data, 0, + CIS_MAXSIZE))) return status; bzero(&pc_cf, sizeof(pc_cf)); @@ -1246,7 +1138,9 @@ pcmciaslot_ioctl(link, slotid, cmd, data) SCRATCH_INUSE(pca) = 1; splx(s); - if ((err = PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca), + if ((err = PCMCIA_MAP_MEM(pca, link, + SCRATCH_BC(pca), + SCRATCH_MEM(pca), pc_cf.cfg_off & ~(SCRATCH_SIZE(pca)-1), SCRATCH_SIZE(pca), @@ -1264,7 +1158,9 @@ pcmciaslot_ioctl(link, slotid, cmd, data) } *d++ = 0xff; *d++ = 0xff; - PCMCIA_MAP_MEM(pca, link, SCRATCH_MEM(pca), + PCMCIA_MAP_MEM(pca, link, + SCRATCH_BC(pca), + SCRATCH_MEM(pca), 0,SCRATCH_SIZE(pca), PCMCIA_LAST_WIN|PCMCIA_UNMAP); } @@ -1281,49 +1177,56 @@ pcmciaslot_ioctl(link, slotid, cmd, data) } int -pcmciabusioctl(dev, cmd, data, flag, p) +pcmciaioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { - int unit = PCMCIABUS_UNIT(dev); + int unit = PCMCIABUS_UNIT(dev); + int chipid = PCMCIABUS_CHIPNO(unit); struct pcmciabus_softc *pcmcia; struct pcmcia_link *link; - PPRINTF(("- pcmciabus_ioctl\n")); - pcmcia = pcmciabuscd.cd_devs[0]; - if (pcmcia_cntrl == 0 || pcmcia == NULL) + PPRINTF(("- pcmciabusioctl\n")); + if (chipid < 0 || chipid >= pcmciabus_cd.cd_ndevs) return ENXIO; + + pcmcia = pcmciabus_cd.cd_devs[chipid]; + if (pcmcia == NULL) + return ENXIO; + switch (PCMCIABUS_DEVTYPE(unit)) { #if 0 case PCMCIABUS_BUS: - return pcmciabus_ioctl(0, cmd, data); + return pcmciabus_ioctl(PCMCIABUS_BUSID(unit), cmd, data); #endif case PCMCIABUS_CHIP: - return pcmciachip_ioctl(PCMCIABUS_CHIPIID(unit), cmd, data); + return pcmciachip_ioctl(PCMCIABUS_CHIPID(unit), cmd, data); case 0: link = pcmcia->sc_link[PCMCIABUS_SLOT(unit)]; - return pcmciaslot_ioctl(link, PCMCIABUS_SLOT(unit), cmd, data); + return pcmciaslot_ioctl(link, PCMCIABUS_SLOTID(unit), + cmd, data); default: return ENXIO; } } int -pcmciabusselect(device, rw, p) +pcmciaselect(device, rw, p) dev_t device; int rw; struct proc *p; { int s; int unit = PCMCIABUS_UNIT(device); + int chipid = PCMCIABUS_CHIPNO(unit); struct pcmciabus_softc *pcmcia; struct pcmcia_link *link; - PPRINTF(("- pcmciabus_ioctl\n")); - pcmcia = pcmciabuscd.cd_devs[0]; + PPRINTF(("- pcmciabus_select\n")); + pcmcia = pcmciabus_cd.cd_devs[chipid]; switch (PCMCIABUS_DEVTYPE(unit)) { case 0: @@ -1335,14 +1238,14 @@ pcmciabusselect(device, rw, p) return 0; } - s = splclock(); /* XXX something higher than all devices that can plug in.... */ + s = splpcmcia(); switch (rw) { case FREAD: case FWRITE: break; case 0: - if (link->flags & PCMCIA_SLOT_EVENT) { - link->flags &= ~PCMCIA_SLOT_EVENT; + if (ISSET(link->flags, PCMCIA_SLOT_EVENT)) { + CLR(link->flags, PCMCIA_SLOT_EVENT); splx(s); return 1; } @@ -1354,54 +1257,14 @@ pcmciabusselect(device, rw, p) } int -pcmciabusmmap() +pcmciammap() { return ENXIO; } -/* pcmcia template string match. A '*' matches any number of characters. - A NULL template matches all strings. - return-value - 0 nomatch - 1 wildcard match - 2 excact match - */ -static int -pcmcia_strcmp(templ,val,flags,msg) - char *templ; - char *val; - int flags; - char *msg; -{ - char *ltempl=NULL,*lval=NULL; - - if (flags & PC_SHOWME) - printf("%s = `%s'-`%s'\n", msg, templ ? templ : "X", val); - - if(templ==NULL) - return 1; - while(*val) { - while(*templ=='*') { - ltempl=++templ; - lval=val; - } - if(*templ==*val) { - templ++; - val++; - } else { - if(ltempl==NULL) - return 0; - val=++lval; - templ=ltempl; - } - } - if(*templ!=0 && *templ!='*') - return 0; - return ltempl?1:2; -} #ifdef PCMCIA_DEBUG -static void +void pcmciadumpcf(cf) struct pcmcia_conf * cf; { @@ -1414,6 +1277,7 @@ pcmciadumpcf(cf) printf("IRQ type %s%s\n", cf->irq_level ? "Level " : "", cf->irq_pulse ? "Pulse" : ""); printf("IRQ num %x\n", cf->irq_num); + printf("IRQ mask %x\n", cf->irq_mask); printf("CFG type %x %x\n", cf->cfgtype,cf->cfgid); printf("Cardtype %s\n", cf->iocard ? "IO" : "MEM"); for (i = 0; i < cf->iowin; i++) { @@ -1431,3 +1295,43 @@ pcmciadumpcf(cf) } } #endif + +int +pcmcia_print(aux, pnp) + void *aux; + char *pnp; +{ +#if 0 + struct pcmcia_attach_args *paa = aux; + printf(" slot %d", paa->paa_link->slot); +#endif + return (0); /* be silent */ +} + +/* + * Filter out inappropriate configurations before heading off to + * the device match routines. + */ +int +pcmcia_submatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct device *self = match; + struct cfdata *cf = self->dv_cfdata; + struct pcmcia_attach_args *paa = aux; + struct pcmcia_link *link = paa->paa_link; + +#if 0 + printf("pcmcia_submatch: paa=%p link=%p, cf=%p\n", paa, link, cf); + delay(2000000); + +#endif + + if (cf->cf_loc[6] != -1 && link->slot != cf->cf_loc[6]) { + printf("slot mismatch: %d cf_loc %d\n", link->slot, cf->cf_loc[6]); + return 0; + } + + return ((*cf->cf_attach->ca_match)(parent, match, aux)); +} diff --git a/sys/dev/pcmcia/pcmcia_conf.c b/sys/dev/pcmcia/pcmcia_conf.c index ab0e5b0d741..3e09e0368bc 100644 --- a/sys/dev/pcmcia/pcmcia_conf.c +++ b/sys/dev/pcmcia/pcmcia_conf.c @@ -1,11 +1,50 @@ +/* $Id: pcmcia_conf.c,v 1.2 1996/04/29 14:17:21 hvozda Exp $ */ +/* + * Copyright (c) 1996 John T. Kohl. All rights reserved. + * Copyright (c) 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles Hannum. + * 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. + * + */ + +/* + * This file is shared between user and kernel space, so be careful with the + * coding conventions. + */ #include <errno.h> #include <sys/types.h> #include <sys/file.h> #include <sys/ioctl.h> #include <sys/device.h> +#include <sys/time.h> +#include <sys/systm.h> -#include <dev/pcmcia/pcmcia.h> -#include <dev/pcmcia/pcmciabus.h> +#include <dev/pcmcia/pcmciavar.h> +#include <dev/pcmcia/pcmciareg.h> #include <dev/pcmcia/pcmcia_ioctl.h> #ifdef CFG_DEBUG @@ -68,7 +107,7 @@ pcmcia_get_cf(pc_link, data, dlen, idx, pc_cf) } -int +void read_cfg_info(tbuf, len, pc_cf) u_char *tbuf; int len; @@ -110,14 +149,15 @@ read_cfg_info(tbuf, len, pc_cf) } } -int +void parse_cfent(tbuf, len, slotid, pc_cf) u_char *tbuf; int len; int slotid; struct pcmcia_conf *pc_cf; { - int i, idx, defp, iop, io_16, ios, ftrs, intface, k; + volatile int i, idx, defp, intface, k; + int ios, ftrs; int host_addr_p, addr_size, len_size; #ifdef CFG_DEBUG @@ -164,7 +204,6 @@ parse_cfent(tbuf, len, slotid, pc_cf) int io_16, io_block_len, io_block_size, io_lines; int io_range; - iop = 1; io_lines = tbuf[i] & TPCE_FS_IO_LINES; io_16 = tbuf[i] & TPCE_FS_IO_BUS16; io_range = tbuf[i] &TPCE_FS_IO_RANGE; @@ -178,6 +217,8 @@ parse_cfent(tbuf, len, slotid, pc_cf) TPCE_FS_IO_SIZE_SHIFT; ios = (tbuf[i] & TPCE_FS_IO_NUM) + 1; i++; + elen=io_block_len+(io_block_len==3?1:0)+ + io_block_size+(io_block_size==3?1:0); if ((ftrs & TPCE_FS_IRQ) != 0) { iptr=(ios * elen) + i; #define IRQTYPE (TPCE_FS_IRQ_PULSE|TPCE_FS_IRQ_LEVEL) @@ -249,7 +290,6 @@ parse_cfent(tbuf, len, slotid, pc_cf) } } if (ftrs & TPCE_FS_IRQ) { - int irq_mask, irqp, irq; pc_cf->irq_level = (tbuf[i] & TPCE_FS_IRQ_LEVEL) != 0; pc_cf->irq_pulse = (tbuf[i] & TPCE_FS_IRQ_PULSE) != 0; pc_cf->irq_share = (tbuf[i] & TPCE_FS_IRQ_SHARE) != 0; @@ -339,6 +379,10 @@ parse_cfent(tbuf, len, slotid, pc_cf) mem_haddrs[j] <<= 8; } + break; + default: + mems = 0; + break; } for (j = 0; j < mems; j++) { pc_cf->mem[j].len = mem_lens[j]; @@ -384,7 +428,7 @@ parse_cfent(tbuf, len, slotid, pc_cf) pc_cf->mem[i].start!=tmp_cf.mem[i].start) return; - /* *pc_cf = tmp_cf;/**/ + /* *pc_cf = tmp_cf; */ pc_cf->cfgid = idx; } return; @@ -472,6 +516,134 @@ pcmcia_get_cisver1(pc_link, data, len, manu, model, add_inf1, add_inf2) } p += clen + 2; } - printf("%x %x\n", p, end); +#ifdef CFG_DEBUG + printf("get_cisver1 failed, buffer [%p,%p)\n", p, end); +#endif return ENODEV; } + +#define NULLCP (void *)0 + +/* pcmcia template string match. A '*' matches any number of characters. + A NULL template matches all strings. + return-value + 0 nomatch + 1 wildcard match + 2 excact match + */ +int +pcmcia_strcmp(templ, val, flags, msg) + const char *templ; + const char *val; + int flags; + const char *msg; +{ + const char *ltempl = NULLCP; + const char *lval = NULLCP; + + if (flags & PC_SHOWME) + printf("%s = `%s'-`%s'\n", msg, templ ? templ : "X", val); + + if (templ == NULLCP) { + return 1; + } + while (*val) { + while (*templ == '*') { + ltempl = ++templ; + lval = val; + } + if (*templ == *val) { + templ++; + val++; + } else { + if (ltempl == NULLCP) + return 0; + val = ++lval; + templ = ltempl; + } + } + if (*templ != 0 && *templ != '*') + return 0; + return (ltempl ? 1 : 2); +} + +/* + * Return a match value to estimate how good a match the specified driver + * is for this particular card. + */ + +int +pcmcia_matchvalue(card, dentry) + const struct pcmcia_cardinfo *card; + struct pcmciadevs *dentry; +{ + int match; + +#ifdef PCMCIA_DEBUG + dentry->flags |= PC_SHOWME; +#endif + match = pcmcia_strcmp(dentry->manufacturer, + card->manufacturer, + dentry->flags, "manufacturer")<<6; + match |= pcmcia_strcmp(dentry->model, + card->model, dentry->flags, "model")<<4; + match |= pcmcia_strcmp(dentry->add_inf1, + card->add_info1, dentry->flags, "info1")<<2; + match |= pcmcia_strcmp(dentry->add_inf2, + card->add_info2, dentry->flags, "info2"); +#ifdef PCMCIA_DEBUG + printf("match == %d\n", match); +#endif + return match; +} + +int +pcmcia_bestvalue(card, dentries, nentries, rmatch) + struct pcmcia_cardinfo *card; + struct pcmciadevs *dentries; + int nentries; + struct pcmciadevs **rmatch; +{ + int bestmatch, thismatch; + register int i; + for (i = 0, bestmatch = 0; i < nentries; i++) { + if ((thismatch = pcmcia_matchvalue(card, &dentries[i])) > + bestmatch) { + bestmatch = thismatch; + *rmatch = &dentries[i]; + } + } + return bestmatch; +} + +int +pcmcia_slave_match(parent, match, aux, devs, ndevs) + struct device *parent; + void *match, *aux; + struct pcmciadevs *devs; + int ndevs; +{ + struct pcmcia_attach_args *paa = aux; + struct device *self = match; + struct pcmciadevs *devmatch; + int value; + + if (paa->paa_link->fordriver && + strcmp(paa->paa_link->fordriver, + self->dv_cfdata->cf_driver->cd_name)) + return 0; /* wrong driver */ + value = pcmcia_bestvalue(paa->paa_cardinfo, devs, ndevs, &devmatch); + if (value > paa->paa_bestmatch) { + paa->paa_bestmatch = value; + paa->paa_link->device = devmatch; +#ifdef PCMCIA_DEBUG + printf("pcmcia_slave_match: best so far, %p->%p\n", + paa->paa_link, devmatch); + printf("pcmcia_slave_match returns %d\n", value); + delay(2000000); +#endif + if (!paa->paa_matchonly) + return value; + } + return 0; +} diff --git a/sys/dev/pcmcia/pcmcia_ioctl.h b/sys/dev/pcmcia/pcmcia_ioctl.h index 535e12241b7..dd8b9e6174e 100644 --- a/sys/dev/pcmcia/pcmcia_ioctl.h +++ b/sys/dev/pcmcia/pcmcia_ioctl.h @@ -1,3 +1,4 @@ +/* $Id: pcmcia_ioctl.h,v 1.2 1996/04/29 14:17:25 hvozda Exp $ */ /* * Copyright (c) 1993, 1994 Stefan Grefen. All rights reserved. * @@ -52,8 +53,7 @@ struct pcmcia_regs { #define PCMCIASIO_POWER_AUTO 0x7 #define PCMCIASIO_POWER_OFF 0x0 #define PCMCIAIO_CONFIGURE _IOW('s', 140, struct pcmcia_conf) -#define PCMCIAIO_UNMAP _IOW('s', 141, int) -#define PCMCIAIO_UNCONFIGURE _IOW('s', 142, int) +#define PCMCIAIO_UNMAP _IO('s', 141) +#define PCMCIAIO_UNCONFIGURE _IO('s', 142) #define PCMCIAIO_READ_COR _IOR('s', 143, struct pcmcia_info) #define PCMCIAIO_READ_REGS _IOWR('s', 160, struct pcmcia_regs) - diff --git a/sys/dev/pcmcia/pcmciareg.h b/sys/dev/pcmcia/pcmciareg.h new file mode 100644 index 00000000000..f3f4446c37d --- /dev/null +++ b/sys/dev/pcmcia/pcmciareg.h @@ -0,0 +1,136 @@ +/* $Id: pcmciareg.h,v 1.1 1996/04/29 14:17:35 hvozda Exp $ */ +/* + * This file was apparently first written by Stefan Grefen, although it + * contained no copyright notice at the time. + */ +#ifndef __PCMCIAREG_H__ +#define __PCMCIAREG_H__ + +/* + * Configuration Registers + * + * These are the registers required by Release 2.0 of the standard + * (Section 4.15) + */ + +/* Offsets for register ordering */ +#define PCMCIA_COR 0x00 /* Configuration and Option Register */ +#define PCMCIA_CCSR 0x02 /* Card Configuration and Status Register */ +#define PCMCIA_PIR 0x04 /* Pin Replacement Register */ +#define PCMCIA_SCR 0x06 /* Socket and Copy Register */ + +/* Now register bits, ordered by reg # */ + +/* For Configuration and Option Register (PCMCIA_COR) */ +#define PCMCIA_MEMIO 0x01 /* Use I/O Space */ +#define PCMCIA_CNFG 0x0e /* I/O decoding configuration */ +#define PCMCIA_CNFGMASK 0x3f /* Use template */ +#define PCMCIA_LVLREQ 0x40 /* Generate level mode interrupts */ +#define PCMCIA_SRESET 0x80 /* Reset Card */ + +/* For Card Configuration and Status Register (PCMCIA_CCSR) */ +#define PCMCIA_INTR 0x02 /* Interrupt Pending */ +#define PCMCIA_POWER_DOWN 0x04 +#define PCMCIA_AUDIO_ENA 0x08 +#define PCMCIA_IOIS8 0x20 +#define PCMCIA_SIGCHG_ENA 0x40 +#define PCMCIA_CHANGED 0x80 + +/* Pin Replacement Register (PCMCIA_PIR) */ +#define PCMCIA_WP_STATUS 0x01 +#define PCMCIA_READY_STATUS 0x02 +#define PCMCIA_BVD2_STATUS 0x04 +#define PCMCIA_BVD1_STATUS 0x08 +#define PCMCIA_WP_EVENT 0x10 +#define PCMCIA_READY_EVENT 0x20 +#define PCMCIA_BVD2_EVENT 0x40 +#define PCMCIA_BVD1_EVENT 0x80 + + +/* For Socket and Copy Register (PCMCIA_SCR) */ +#define PCMCIA_SOCKNUM 0x0f /* Which socket I'm sitting in */ +#define PCMCIA_COPNUM 0x70 /* Which instance I am. */ + +/* + * CIS Tuple defines + */ +#define CIS_MAXSIZE 512 + +/* Define tuple types */ +#define CIS_NULL 0x00 /* null tuple */ +#define CIS_DEVICE 0x01 /* Device descriptor, common mem */ +#define CIS_DEVICE_A 0x17 /* Device descriptor, attribute mem */ +#define CIS_DEVICE_TYPE 0xf0 /* type mask */ +#define CIS_DEVICE_TYPE_SHIFT 4 /* type offset */ +#define CIS_DEVICE_WPS 0x08 /* WPS mask */ +#define CIS_DEVICE_SPEED 0x07 /* speed mask */ +#define CIS_DEVICE_ADDRS 0xf8 /* # addr units */ +#define CIS_DEVICE_ADDRS_SHIFT 3 /* # addr units offset */ +#define CIS_DEVICE_SIZE 0x07 +#define CIS_CSUM 0x10 /* Checksum field */ +#define CIS_NOLINK 0x14 /* No Link */ +#define CIS_VER1 0x15 /* Level 1 Version/Product info */ +#define CIS_CFG_INFO 0x1a /* Configuration info map */ +#define TPCC_RASZ 0x03 /* size of regaddr */ +#define TPCC_RASZ_SHIFT 0 +#define TPCC_RMSZ 0x3c /* size of regmask */ +#define TPCC_RMSZ_SHIFT 2 +#define TPCC_LAST 0x3f /* last con entry idx */ +#define TPCC_LAST_SHIFT 0 +#define CIS_CFG_ENT 0x1b /* Configuration info entry */ +#define TPCE_INDX_ENTRY 0x3f /* config entry # */ +#define TPCE_INDX_DEF 0x40 /* default bit */ +#define TPCE_INDX_INT 0x80 /* interface bit */ +#define TPCE_IF_TYPE 0x0f /* interface type */ +#define TPCE_IF_BVD 0x10 /* BVD active bit */ +#define TPCE_IF_WP 0x20 /* WP active bit */ +#define TPCE_IF_RDYBSY 0x40 /* RdyBsy active bit */ +#define TPCE_IF_MWAIT 0x80 /* Wait Sig req. bit */ +#define TPCE_FS_PWR 0x03 /* Power */ +#define TPCE_FS_PWR_VCC 0x01 /* Vcc struct */ +#define TPCE_FS_PWR_VPP 0x02 /* Vpp struct */ +#define TPCE_FS_TD 0x04 /* Timing */ +#define TPCE_FS_TD_WAIT 0x03 /* wait scale */ +#define TPCE_FS_TD_RDY 0x1c /* rdy/bsy scale */ +#define TPCE_FS_TD_RDY_SHIFT 2 +#define TPCE_FS_TD_RSV 0xe0 /* reserved scale */ +#define TPCE_FS_TD_RSV_SHIFT 5 +#define TPCE_FS_IO 0x08 /* I/O Space */ +#define TPCE_FS_IO_LINES 0x1f /* IO addr lines */ +#define TPCE_FS_IO_BUS8 0x20 /* bus 8 bit */ +#define TPCE_FS_IO_BUS16 0x40 /* bus 16 bit */ +#define TPCE_FS_IO_RANGE 0x80 /* range bit */ +#define TPCE_FS_IO_LEN 0xc0 /* block len size */ +#define TPCE_FS_IO_LEN_SHIFT 6 +#define TPCE_FS_IO_SIZE 0x30 /* block size size */ +#define TPCE_FS_IO_SIZE_SHIFT 4 +#define TPCE_FS_IO_NUM 0x0f /* # of blocks */ +#define TPCE_FS_IRQ 0x10 /* IRQ */ +#define TPCE_FS_IRQ_SHARE 0x80 /* int sharing */ +#define TPCE_FS_IRQ_PULSE 0x40 /* pulse request */ +#define TPCE_FS_IRQ_LEVEL 0x20 /* level-trig int */ +#define TPCE_FS_IRQ_MASK 0x10 /* irq mask bit */ +#define TPCE_FS_IRQ_IRQN 0x0f /* irqn mask */ +#define TPCE_FS_IRQ_VEND 0x08 /* vendor sig */ +#define TPCE_FS_IRQ_BERR 0x04 /* bus error */ +#define TPCE_FS_IRQ_IOCK 0x02 /* io check */ +#define TPCE_FS_IRQ_NMI 0x01 /* nmi */ +#define TPCE_FS_MEM 0x60 /* Mem Space */ +#define TPCE_FS_MEM_SHIFT 5 +#define TPCE_FS_MEM_HOST 0x80 +#define TPCE_FS_MEM_ADDR 0x60 +#define TPCE_FS_MEM_ADDR_SHIFT 5 +#define TPCE_FS_MEM_LEN 0x18 +#define TPCE_FS_MEM_LEN_SHIFT 3 +#define TPCE_FS_MEM_WINS 0x07 +#define TPCE_FS_MISC 0x80 /* Misc */ +#define CIS_MFG 0x20 /* Manufacturer's ID */ +#define CIS_FUNC 0x21 /* Function ID */ +#define CIS_FUNE 0x22 /* Function Extension */ +#define CIS_DRIVER 0x77 /* Driver ID */ +#define CIS_END 0xff /* Last Entry */ + +#define splpcmcia spltty +#define IPL_PCMCIA IPL_TTY + +#endif /* __PCMCIAREG_H__ */ diff --git a/sys/dev/pcmcia/pcmciabus.h b/sys/dev/pcmcia/pcmciavar.h index 4dceb987690..2e4f597b703 100644 --- a/sys/dev/pcmcia/pcmciabus.h +++ b/sys/dev/pcmcia/pcmciavar.h @@ -1,4 +1,6 @@ +/* $Id: pcmciavar.h,v 1.1 1996/04/29 14:17:39 hvozda Exp $ */ /* + * Copyright (c) 1995,1996 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 @@ -26,16 +28,16 @@ * (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: pcmciabus.h,v 1.1 1996/01/15 00:05:13 hvozda Exp $ */ /* derived from scsicconf.[ch] writenn by Julian Elischer et al */ -#ifndef _PCMCIA_PCMCIABUS_H_ -#define _PCMCIA_PCMCIABUS_H_ 1 +#ifndef _PCMCIA_PCMCIAVAR_H_ +#define _PCMCIA_PCMCIAVAR_H_ 1 #include <sys/queue.h> #include <sys/select.h> #include <machine/cpu.h> +#include <machine/bus.h> /* * The following documentation tries to describe the relationship between the @@ -80,11 +82,11 @@ struct pcmcia_funcs { /* 4 map io range */ int (*pcmcia_map_io) __P((struct pcmcia_link *, u_int, u_int, int)); /* 8 map memory window */ - int (*pcmcia_map_mem) __P((struct pcmcia_link *, caddr_t, - u_int, u_int, int)); + int (*pcmcia_map_mem) __P((struct pcmcia_link *, bus_chipset_tag_t, + caddr_t, u_int, u_int, int)); /*12 map interrupt */ int (*pcmcia_map_intr) __P((struct pcmcia_link *, int, int)); -/*26 power on/off etc */ +/*16 power on/off etc */ int (*pcmcia_service) __P((struct pcmcia_link *, int, void *, int)); }; @@ -94,22 +96,41 @@ struct pcmciabus_link { /* Link back to the bus we are on */ struct pcmcia_conf *, struct cfdata *)); /* Bus specific unconfigure */ int (*bus_unconfig) __P((struct pcmcia_link *)); - /* Bus specific probe */ + /* Bus specific probe */ int (*bus_probe) __P((struct device *, void *, - void *, struct pcmcia_link *)); + void *, struct pcmcia_link *)); /* Bus specific search */ int (*bus_search) __P((struct device *, void *, cfprint_t)); /* initialize scratch */ int (*bus_init) __P((struct device *, struct cfdata *, void *, struct pcmcia_adapter *, int)); }; +#define PCMCIA_BUS_INIT(a,b,c,d,e,f) \ + ((*(a)->bus_link->bus_init)((b),(c),(d),(e),(f))) +#define PCMCIA_BUS_SEARCH(a,b,c,d) \ + ((*(a)->bus_link->bus_search)((b),(c),(d))) +#define PCMCIA_BUS_PROBE(a,b,c,d,e) \ + ((*(a)->bus_link->bus_probe)((b),(c),(d),(e))) +#define PCMCIA_BUS_CONFIG(a,b,c,d,e) \ + ((*(a)->bus_link->bus_config)((b),(c),(d),(e))) +#define PCMCIA_BUS_UNCONFIG(a,b) \ + ((*(a)->bus_link->bus_unconfig)((b))) + + +/* + * One of these goes at the front of each chip controller's softc, right + * after the struct device. + */ struct pcmcia_adapter { struct pcmcia_funcs *chip_link; struct pcmciabus_link *bus_link; + bus_chipset_tag_t pa_bc; /* bus chipset */ void * adapter_softc; caddr_t scratch_mem; /* pointer to scratch window */ int scratch_memsiz; /* size of scratch window */ + bus_mem_handle_t scratch_memh; /* bus memory handle */ int scratch_inuse; /* window in use */ + int nslots; /* # of slots controlled */ }; #define PCMCIA_MAP_ATTR 0x0100 /* for memory only */ @@ -151,9 +172,9 @@ struct pcmcia_adapter { * as well. */ struct pcmcia_link { - char pcmciabus; /* the Nth pcmciabus */ - char slot; /* slot of this dev */ - char flags; + u_char pcmciabus; /* the Nth pcmciabus */ + u_char slot; /* slot of this dev */ + u_char flags; #define CARD_IS_MAPPED 0x01 #define PCMCIA_ATTACH 0x02 #define PCMCIA_REATTACH 0x04 @@ -161,17 +182,17 @@ struct pcmcia_link { #define PCMCIA_ATTACH_TYPE (PCMCIA_ATTACH|PCMCIA_REATTACH) #define PCMCIA_SLOT_EVENT 0x80 #define PCMCIA_SLOT_OPEN 0x40 - char opennings; + u_char opennings; - char iowin; - char memwin; - char intr; - char dummy; + u_char iowin; + u_char memwin; + u_char intr; + u_char dummy; struct pcmcia_adapter *adapter; /* adapter entry points etc. */ struct pcmciadevs *device; /* device entry points etc. */ - void *devp; /* pointer to configured device */ + struct pcmciabus_softc *bus; /* parent pcmcia bus */ + struct device *devp; /* pointer to configured device */ void *fordriver; /* for private use by the driver */ - void *shuthook; /* shutdown hook handle */ struct selinfo pcmcialink_sel; /* for select users */ }; @@ -184,7 +205,9 @@ struct pcmcia_link { */ struct pcmciabus_softc { struct device sc_dev; - struct pcmcia_link *sc_link[8]; + bus_chipset_tag_t sc_bc; + struct pcmcia_link *sc_link[4]; /* up to 4 slots per bus */ + struct pcmcia_adapter *sc_driver; }; struct pcmcia_conf { @@ -233,6 +256,15 @@ struct pcmcia_device { int (*pcmcia_remove) __P((struct pcmcia_link *, struct device *)); }; +#define MAX_CIS_NAMELEN 64 /* version info string len */ + +struct pcmcia_cardinfo { + char manufacturer[MAX_CIS_NAMELEN]; + char model[MAX_CIS_NAMELEN]; + char add_info1[MAX_CIS_NAMELEN]; + char add_info2[MAX_CIS_NAMELEN]; +}; + struct pcmciadevs { char *devname; int flags; /* 1 show my comparisons during boot(debug) */ @@ -245,6 +277,24 @@ struct pcmciadevs { struct pcmcia_device *dev; }; +/* + * PCMCIA driver attach arguments + */ +struct pcmcia_attach_args { + struct pcmcia_cardinfo *paa_cardinfo; /* card that we're looking at */ + struct pcmcia_link *paa_link; /* this nexus */ + int paa_bestmatch; /* best match so far */ + int paa_matchonly; /* only do matches, don't attach */ + void *paa_aux; /* driver specific */ +}; + +struct pcmciabus_attach_args { + bus_chipset_tag_t pba_bc; + int pba_maddr; + int pba_msize; + void *pba_aux; /* driver specific */ +}; + #ifdef _KERNEL extern int pcmcia_add_device __P((struct pcmciadevs *)); extern int pcmcia_get_cf __P((struct pcmcia_link *, u_char *, int, int, @@ -255,8 +305,23 @@ extern int pcmcia_targmatch __P((struct device *, struct cfdata *, void *)); /* in pcmcia_conf.c, available for user space too: */ extern int pcmcia_get_cisver1 __P((struct pcmcia_link *, u_char *, int, char *, char *, char *, char *)); -int parse_cfent __P((u_char *, int, int, struct pcmcia_conf *)); -int read_cfg_info __P((u_char *, int, struct pcmcia_conf *)); -void pcmcia_getstr __P((char *buf, u_char **, u_char *)); - -#endif /* _PCMCIA_PCMCIABUS_H_ */ +void parse_cfent __P((u_char *, int, int, struct pcmcia_conf *)); +void read_cfg_info __P((u_char *, int, struct pcmcia_conf *)); +void pcmcia_getstr __P((char *buf, u_char **, u_char *)); +extern int pcmcia_configure __P((struct device *, void *, void *)); +extern int pcmcia_register __P((void *, struct pcmciabus_link *, + struct pcmcia_funcs *, int)); +extern int pcmcia_read_cis __P((struct pcmcia_link *, u_char *, int, int)); +extern int pcmcia_strcmp __P((const char *, const char *, int, const char *)); +extern int pcmcia_matchvalue __P((const struct pcmcia_cardinfo *, + struct pcmciadevs *)); +extern int pcmcia_bestvalue __P((struct pcmcia_cardinfo *, + struct pcmciadevs *, + int, + struct pcmciadevs **)); +extern int pcmcia_slave_match __P((struct device *, + void *, + void *aux, + struct pcmciadevs *, + int)); +#endif /* _PCMCIA_PCMCIAVAR_H_ */ diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c index 1e9ab0ae6ba..ab73c7d2416 100644 --- a/sys/kern/subr_autoconf.c +++ b/sys/kern/subr_autoconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: subr_autoconf.c,v 1.4 1996/04/21 22:27:13 deraadt Exp $ */ +/* $OpenBSD: subr_autoconf.c,v 1.5 1996/04/29 14:17:45 hvozda Exp $ */ /* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */ /* @@ -52,6 +52,9 @@ #include <sys/malloc.h> #include <sys/systm.h> #include <machine/limits.h> +/* Extra stuff from Matthias Drochner <drochner@zelux6.zel.kfa-juelich.de> + */ +#include <sys/queue.h> /* * Autoconfiguration subroutines. @@ -75,6 +78,12 @@ struct matchinfo { int indirect, pri; }; +struct cftable_head allcftables; + +static struct cftable staticcftable = { + cfdata +}; + static char *number __P((char *, int)); static void mapply __P((struct matchinfo *, struct cfdata *)); @@ -90,6 +99,8 @@ config_init() TAILQ_INIT(&alldevs); TAILQ_INIT(&allevents); + TAILQ_INIT(&allcftables); + TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list); } /* @@ -150,6 +161,7 @@ config_search(fn, parent, aux) register struct cfdata *cf; register short *p; struct matchinfo m; + struct cftable *t; m.fn = fn; m.parent = parent; @@ -157,16 +169,18 @@ config_search(fn, parent, aux) m.aux = aux; m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; m.pri = 0; - for (cf = cfdata; cf->cf_driver; cf++) { - /* - * Skip cf if no longer eligible, otherwise scan through - * parents for one matching `parent', and try match function. - */ - if (cf->cf_fstate == FSTATE_FOUND) - continue; - for (p = cf->cf_parents; *p >= 0; p++) - if (parent->dv_cfdata == &cfdata[*p]) - mapply(&m, cf); + for(t = allcftables.tqh_first; t; t = t->list.tqe_next){ + for (cf = t->tab; cf->cf_driver; cf++) { + /* + * Skip cf if no longer eligible, otherwise scan through + * parents for one matching `parent', and try match function. + */ + if (cf->cf_fstate == FSTATE_FOUND) + continue; + for (p = cf->cf_parents; *p >= 0; p++) + if (parent->dv_cfdata == &(t->tab)[*p]) + mapply(&m, cf); + } } return (m.match); } @@ -188,23 +202,26 @@ config_scan(fn, parent) register short *p; void *match; int indirect; + struct cftable *t; indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; - for (cf = cfdata; cf->cf_driver; cf++) { - /* - * Skip cf if no longer eligible, otherwise scan through - * parents for one matching `parent', and try match function. - */ - if (cf->cf_fstate == FSTATE_FOUND) - continue; - for (p = cf->cf_parents; *p >= 0; p++) - if (parent->dv_cfdata == &cfdata[*p]) { - if (indirect) - match = config_make_softc(parent, cf); - else - match = cf; - (*fn)(parent, match); - } + for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { + for (cf = t->tab; cf->cf_driver; cf++) { + /* + * Skip cf if no longer eligible, otherwise scan through + * parents for one matching `parent', and try match function. + */ + if (cf->cf_fstate == FSTATE_FOUND) + continue; + for (p = cf->cf_parents; *p >= 0; p++) + if (parent->dv_cfdata == &(t->tab)[*p]) { + if (indirect) + match = config_make_softc(parent, cf); + else + match = cf; + (*fn)(parent, match); + } + } } } @@ -313,6 +330,7 @@ config_attach(parent, match, aux, print) register struct device *dev; register struct cfdriver *cd; register struct cfattach *ca; + struct cftable *t; if (parent && parent->dv_cfdata->cf_driver->cd_indirect) { dev = match; @@ -346,13 +364,15 @@ config_attach(parent, match, aux, print) * otherwise identical, or bump the unit number on all starred * cfdata for this device. */ - for (cf = cfdata; cf->cf_driver; cf++) - if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { + for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { + for (cf = t->tab; cf->cf_driver; cf++) + if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { if (cf->cf_fstate == FSTATE_NOTFOUND) cf->cf_fstate = FSTATE_FOUND; if (cf->cf_fstate == FSTATE_STAR) cf->cf_unit++; - } + } + } (*ca->ca_attach)(parent, dev, aux); return (dev); } @@ -447,3 +467,185 @@ evcnt_attach(dev, name, ev) strcpy(ev->ev_name, name); TAILQ_INSERT_TAIL(&allevents, ev, ev_list); } + +typedef int (*cond_predicate_t) __P((struct device*, void*)); + +static int haschild __P((struct device *)); +static int detach_devices __P((cond_predicate_t, void *, + config_detach_callback_t, void *)); + +static int +haschild(dev) + struct device *dev; +{ + struct device *d; + + for (d = alldevs.tqh_first; + d != NULL; + d = d->dv_list.tqe_next) { + if (d->dv_parent == dev) + return(1); + } + return(0); +} + +static int +detach_devices(cond, condarg, callback, arg) + cond_predicate_t cond; + void *condarg; + config_detach_callback_t callback; + void *arg; +{ + struct device *d; + int alldone = 1; + + /* + * XXX should use circleq and run around the list backwards + * to allow for predicates to match children. + */ + d = alldevs.tqh_first; + while (d != NULL) { + if ((*cond)(d, condarg)) { + struct cfdriver *drv = d->dv_cfdata->cf_driver; + + /* device not busy? */ + /* driver's detach routine decides, upper + layer (eg bus dependent code) is notified + via callback */ +#ifdef DEBUG + printf("trying to detach device %s (%p)\n", + d->dv_xname, d); +#endif + if (!haschild(d) && + d->dv_cfdata->cf_attach->ca_detach && + ((*(d->dv_cfdata->cf_attach->ca_detach))(d)) == 0) { + int needit, i; + struct device *help; + + if (callback) + (*callback)(d, arg); + + /* remove reference in driver's devicelist */ + if ((d->dv_unit >= drv->cd_ndevs) || + (drv->cd_devs[d->dv_unit]!=d)) + panic("bad unit in detach_devices"); + drv->cd_devs[d->dv_unit] = NULL; + + /* driver is not needed anymore? */ + needit = 0; + for(i = 0; i<drv->cd_ndevs; i++) + if (drv->cd_devs[i]) + needit = 1; + + if (!needit) { + /* free devices array (alloc'd + in config_make_softc) */ + free(drv->cd_devs, M_DEVBUF); + drv->cd_ndevs = 0; + } + + /* remove entry in global device list */ + help = d->dv_list.tqe_next; + TAILQ_REMOVE(&alldevs, d, dv_list); +#ifdef DEBUG + printf("%s removed\n", d->dv_xname); +#endif + d->dv_cfdata->cf_fstate = FSTATE_NOTFOUND; + /* free memory for dev data (alloc'd + in config_make_softc) */ + free(d, M_DEVBUF); + d = help; + continue; + } else + alldone = 0; + } + d = d->dv_list.tqe_next; + } + return(!alldone); +} + +int dev_matches_cfdata __P((struct device *dev, void *)); + +int +dev_matches_cfdata(dev, arg) + struct device *dev; + void *arg; +{ + struct cfdata *cfdata = arg; + return(/* device uses same driver ? */ + (dev->dv_cfdata->cf_driver == cfdata->cf_driver) + /* device instance described by this cfdata? */ + && ((cfdata->cf_fstate == FSTATE_STAR) + || ((cfdata->cf_fstate == FSTATE_FOUND) + && (dev->dv_unit == cfdata->cf_unit))) + ); +} + +int +config_detach(cf, callback, arg) + struct cfdata *cf; + config_detach_callback_t callback; + void *arg; +{ + return(detach_devices(dev_matches_cfdata, cf, callback, arg)); +} + +int +attach_loadable(parentname, parentunit, cftable) + char *parentname; + int parentunit; + struct cftable *cftable; +{ + int found = 0; + struct device *d; + + TAILQ_INSERT_TAIL(&allcftables, cftable, list); + + for(d = alldevs.tqh_first; + d != NULL; + d = d->dv_list.tqe_next) { + struct cfdriver *drv = d->dv_cfdata->cf_driver; + + if ((!strcmp(parentname, drv->cd_name)) + && ((parentunit == -1) || (parentunit == d->dv_unit))) { + int s; + + s = splhigh(); /* ??? */ + found |= (*d->dv_cfdata->cf_attach->ca_reprobe)(d, &(cftable->tab[0])); + splx(s); + } + } + + if (!found) + TAILQ_REMOVE(&allcftables, cftable, list); + + return(found); +} + +static int +devcf_intable __P((struct device *, void *)); + +static int +devcf_intable(dev, arg) + struct device *dev; + void *arg; +{ + struct cftable *tbl = arg; + struct cfdata *cf; + + for(cf = tbl->tab; cf->cf_driver; cf++) { + if (dev->dv_cfdata == cf) + return(1); + } + return(0); +} + +int +detach_loadable(cftable) + struct cftable *cftable; +{ + if (!detach_devices(devcf_intable, cftable, 0, 0)) + return(0); + TAILQ_REMOVE(&allcftables, cftable, list); + return(1); +} diff --git a/sys/sys/device.h b/sys/sys/device.h index 99208f4ac03..37df553bc2a 100644 --- a/sys/sys/device.h +++ b/sys/sys/device.h @@ -1,4 +1,4 @@ -/* $OpenBSD: device.h,v 1.4 1996/04/21 22:31:38 deraadt Exp $ */ +/* $OpenBSD: device.h,v 1.5 1996/04/29 14:17:53 hvozda Exp $ */ /* $NetBSD: device.h,v 1.15 1996/04/09 20:55:24 cgd Exp $ */ /* @@ -122,7 +122,8 @@ struct cfattach { size_t ca_devsize; /* size of dev data (for malloc) */ cfmatch_t ca_match; /* returns a match level */ void (*ca_attach) __P((struct device *, struct device *, void *)); - /* XXX should have detach */ + int (*ca_detach) __P((struct device*)); + int (*ca_reprobe) __P((struct device*, struct cfdata*)); }; struct cfdriver { @@ -153,6 +154,11 @@ struct pdevinit { }; #ifdef _KERNEL +struct cftable { + struct cfdata *tab; + TAILQ_ENTRY(cftable) list; +}; +TAILQ_HEAD(cftable_head, cftable); extern struct devicelist alldevs; /* list of all devices */ extern struct evcntlist allevents; /* list of all event counters */ @@ -169,6 +175,12 @@ void evcnt_attach __P((struct device *, const char *, struct evcnt *)); /* compatibility definitions */ #define config_found(d, a, p) config_found_sm((d), (a), (p), NULL) +extern int attach_loadable __P((char *, int, struct cftable *)); +extern int detach_loadable __P((struct cftable *)); +typedef void (*config_detach_callback_t) __P((struct device *, void *)); +extern int config_detach __P((struct cfdata *, config_detach_callback_t, + void *)); + #endif /* _KERNEL */ #endif /* !_SYS_DEVICE_H_ */ |