The userland part of Andrew McRae's PCMCIA/PCCARD code.

This is not quite finished yet, and therefore I have not added it to the
usr.sbin/Makefile yet.
I collected a bunch of Andrews small programs into one: pccardc /phk

Reviewed by:	phk
Submitted by:	Andrew McRae <andrew@mega.com.au>
This commit is contained in:
Poul-Henning Kamp 1995-08-24 09:03:04 +00:00
parent 0d2966d3f2
commit f2b46629be
36 changed files with 10277 additions and 0 deletions

View file

@ -0,0 +1,11 @@
Original work is copyright (C) Andrew McRae 1994,1995
Permission is granted to freely copy as long as
all attribution remains and copyright notices are
included, and source made available upon request.
Parts of this distribution is derived from work
by Barry Jaspan and Keith Moore.
DISCLAIMER:
There is no warranty for this software.
All responsibility is your own.

View file

@ -0,0 +1,12 @@
The files are:
enabler Sample enabler for connecting drivers
dumpcis Reads and dumps CIS of card.
pccardmem Sets a memory area for use by the driver
rdmap Dumps the current memory and I/O card contexts
rdreg reads a PCIC register
wrreg Writes a PCIC register
wrattr Writes a byte into the card's attribute area.
The cardd stuff has been updated for the new interfaces, but
hasn't been tested to any real extent.

View file

@ -0,0 +1,34 @@
INSTALLATION
------------
1. Copy the source files from the sys directory into your
/sys (or where-ever) tree. There are a number of .diff files
that can be used to modify the following files:
sys/conf/files
sys/i386/i386/autoconf.c
sys/i386/i386/conf.c
There is also a sys/pccard directory that should reside in
/sys/pccard
2. Make a symbolic link like:
ln -s /sys/pccard /usr/include/pccard
3. Add a line in the kernel config file:
controller crd0
and rebuild the kernel. Optionally, the PCIC slot driver can
and also built into the kernel by adding the line:
device pcic0
4. Create a device for each slot e.g.
foreach i (0 1)
mknod /dev/card$i c 50 $i
end
5. You can use lkm to load the PCIC controller. It can
be unloaded at will. Once loaded, you should assign
some memory via pccardmem for use with read/write.
Normally cardd does this, but I haven't quite finished yet.

View file

@ -0,0 +1,32 @@
#
# Makefile for PCMCIA card programs.
#
CFLAGS = -m486 -g # -DDEBUG
PROGS = dumpcis rdmap rdreg wrreg pccardmem wrattr enabler
all: $(PROGS)
clean:
rm -f *.core core *.o $(PROGS)
dumpcis: dumpcis.o readcis.o printcis.o
cc -o dumpcis -static dumpcis.o readcis.o printcis.o
wrattr: wrattr.c
cc $(CFLAGS) -o wrattr wrattr.c
pccardmem: pccardmem.c
cc $(CFLAGS) -o pccardmem pccardmem.c
enabler: enabler.c
cc $(CFLAGS) -o enabler enabler.c
rdmap: rdmap.c
cc $(CFLAGS) -o rdmap rdmap.c
rdreg: rdreg.c
cc $(CFLAGS) -o rdreg rdreg.c
wrreg: wrreg.c
cc $(CFLAGS) -o wrreg wrreg.c

View file

@ -0,0 +1,62 @@
PCMCIA Support for FreeBSD 2.0
------------------------------
This package contains the following:
- Driver for Intel 83265 PCIC PCMCIA controller
- A PCMCIA daemon for managing card insertions/removals
- Diffs to various source files for adding PCMCIA support
- A sample config file
- Some utility programs for reading card data tuples
- Some documentation.
What it doesn't include is:
- Configuration for brand XYZ PCMCIA cards
- Drivers for brand XYZ PCMCIA cards
- Diffs to modify standard drivers to handle card events
Essentially, this package contains everything required to
add PCMCIA support to FreeBSD 2.0. It does this via a
daemon that manages the PCMCIA slots via a PCIC driver. A configuration
file provides the daemon with the information required to setup
the specific cards, and to manage card insertion and removal.
This package has been tested on a NEC Versa Laptop.
The first version was developed on FreeBSD 1.1.5.1.
The main idea behind the package is for pcmciad to detect
inserted cards and match to a card ID in the config file,
then set up the I/O ports and memory window to the card
according to the data for the driver associated with the
card, then attach the kernel driver to the device. Shell
commands can be executed for both insertion and removal of cards,
and different commands can be executed for different cards,
drivers and devices.
Different kernel drivers may have to have some mods
done to recognise the card once installed; I have included
some diffs to `ed' that allows recognition of a `generic'
NS8390 card.
Some minor changes were made to ifconfig(8) to allow a
different ethernet address to be assigned to a network
interface, since each card may have different locations
the ethernet address is stored in.
This is a snapshot of a release for FreeBSD 2.0. It is
basically the 1.1.5.1 release ported for 2.0. I have only
just received a developer's guide for PCMCIA, so I should
have some more stable support soon.
For instructions on installation, see INSTALL.
There is a man entry on the daemon (pcmciad.8),
and the config file (pcmcia.conf.5).
Please send mail with any bugs or new card descriptions.
Enjoy!
Andrew McRae inet: andrew@mega.com.au
MITS Real Time Ltd, uucp: ..!uunet!mega.com.au!andrew
North Ryde 2113 Phone: +61 2 805 0899
NSW AUSTRALIA Fax: +61 2 887 4847

113
usr.sbin/pccard/misc/rpti.c Normal file
View file

@ -0,0 +1,113 @@
/*
* Enabler for PCCARD. Used for testing drivers etc.
* Options:
* enabler slot driver [ -m card addr size ] [ -i iobase ] [ -q irq ]
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
#include <pccard/cis.h>
void usage();
main(argc, argv)
int argc;
char *argv[];
{
struct drv_desc drv;
struct mem_desc mem;
struct io_desc io;
int fd, err, slot, i, card_addr;
char name[32], cmd[256];
char *p;
if (argc == 2)
slot = atoi(argv[1]);
else
slot = 0;
if (slot < 0 || slot >= MAXSLOT)
usage("Illegal slot number");
sprintf(cmd, "util/wrattr %d 100 80", slot);
printf("%s\n", cmd);
system(cmd);
usleep(200*1000);
sprintf(cmd, "util/wrattr %d 100 0", slot);
printf("%s\n", cmd);
system(cmd);
usleep(200*1000);
sprintf(cmd, "util/wrattr %d 100 30", slot);
printf("%s\n", cmd);
system(cmd);
usleep(200*1000);
bzero(&drv, sizeof(drv));
drv.unit = 0;
strcpy(drv.name, "ed");
drv.irqmask = 1 << 5;
sprintf(name, "/dev/card%d", slot);
fd = open(name, 2);
if (fd < 0)
{
perror(name);
exit(1);
}
/*
* Map the memory and I/O contexts.
*/
drv.mem = 0xD4000;
if (drv.mem)
{
mem.window = 0;
mem.flags = MDF_ACTIVE;
mem.start = (caddr_t)drv.mem;
mem.size = 16*1024;
mem.card = 0x4000;
if (ioctl(fd, PIOCSMEM, &mem))
{
perror("Set memory context");
exit(1);
}
}
drv.iobase = 0x300;
if (drv.iobase)
{
io.window = 0;
io.flags = IODF_ACTIVE|IODF_CS16|IODF_WS;
io.start = drv.iobase;
io.size = 32; /* Blah... */
if (ioctl(fd, PIOCSIO, &io))
{
perror("Set I/O context");
exit(1);
}
#ifdef 0
io.window = 1;
io.flags = IODF_ACTIVE|IODF_16BIT;
io.start = drv.iobase+16;
io.size = 16; /* Blah... */
if (ioctl(fd, PIOCSIO, &io))
{
perror("Set I/O context");
exit(1);
}
#endif
}
if (ioctl(fd, PIOCSDRV, &drv))
perror("set driver");
close(fd);
}
/*
* usage - print usage and exit
*/
void
usage(msg)
char *msg;
{
fprintf(stderr, "rpti: %s\n", msg);
fprintf(stderr, "Usage: rpti slot driver\n");
exit(1);
}

View file

@ -0,0 +1,37 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
#include <pccard/cis.h>
main(argc, argv)
int argc;
char *argv[];
{
struct drv_desc drv;
int fd, err;
fd = open("/dev/card0", 0);
if (fd < 0)
{
perror("/dev/card0");
exit(1);
}
strcpy(drv.name, "skel");
if (argc == 2)
drv.unit = atoi(argv[1]);
else
drv.unit = 0;
drv.iobase = 0x300;
drv.mem = 0xD4000;
drv.memsize = 16*1024;
drv.irq = 0xFFFF;
drv.flags = 0x1234;
if (ioctl(fd, PIOCSDRV, &drv))
perror("set driver");
close(fd);
}

View file

@ -0,0 +1,63 @@
#
# GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks
#
# GENERIC,v 1.20 1994/11/18 19:10:25 jkh Exp
#
machine "i386"
cpu "I386_CPU"
cpu "I486_CPU"
cpu "I586_CPU"
ident LAPTOP
maxusers 4
options MATH_EMULATE #Support for x87 emulation
options INET #InterNETworking
options FFS #Berkeley Fast Filesystem
options NFS #Network Filesystem
options MSDOSFS #MSDOS Filesystem
options PROCFS #Process filesystem
options "COMPAT_43" #Compatible with BSD 4.3
options UCONSOLE #X Console support
options "FAT_CURSOR" #block cursor in syscons or pccons
options "NCONS=4" #4 virtual consoles
options ALLOW_CONFLICT_IOADDR
options PSM_NO_RESET
config kernel root on wd0 swap on wd0 and wd1 and sd0 and sd1 dumps on wd0
controller crd0
#device pcic0
controller isa0
controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr
disk fd0 at fdc0 drive 0
controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
disk wd0 at wdc0 drive 0
device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr
device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
device psm0 at isa? port "IO_KBD" tty irq 12 vector psmintr
#device pcic0 at isa? port 0x3E0 tty irq 3 iomem 0xd0000
device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr
device sio1 at isa? port "IO_COM2" tty irq 10 vector siointr
#device sio1 at isa? port "IO_COM2" tty flags 0x80 irq 10 vector siointr
device lpt0 at isa? port? tty irq 7 vector lptintr
#device ed0 at isa? port 0x300 net flags 0x20 irq 5 iomem 0xd4000 vector edintr
device apm0 at isa?
pseudo-device loop
pseudo-device ether
pseudo-device log
pseudo-device sl 2
pseudo-device pty 16
pseudo-device speaker
pseudo-device gzip # Exec gzipped a.out's

View file

@ -0,0 +1,25 @@
all: pcic.o ed.o
CFLAGS = -O -DINET -DKERNEL -DLKM -I/sys
pcic.o: pcic.c
cc -c $(CFLAGS) pcic.c
load: pcic.o
modload -e lkm_pcic pcic.o
pccardmem d0000
unload:
modunload -n pcic
skel.o: skel.c
cc -c $(CFLAGS) skel.c
ldskel: skel.o
modload -e lkm_skel skel.o
ed.o: if_ed.o lkm_ed.o
ld -r -o ed.o if_ed.o lkm_ed.o
lded: ed.o
modload -e lkm_ed ed.o

View file

@ -0,0 +1,13 @@
This directory contains:
card.h Include file for driver interface
cis.h CIS defines
slot.h Kernel interface for card/slot driver/devices interface
i82365.h Defines for PCIC device
pccard.c Mainline PC-Card support functions (card services).
pcic.c PCIC or compatible slot driver (loadable).
skel.c Skeleton loadable driver interfacing to card services.
The other files are working copies of drivers.

View file

@ -0,0 +1 @@
#define NBPFILTER 0

View file

@ -0,0 +1 @@
#define NED 2

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,989 @@
/*
* Copyright (C) 1993, David Greenman. 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.
*
* $Id: if_edreg.h,v 1.17 1995/01/23 19:06:08 davidg Exp $
*/
/*
* National Semiconductor DS8390 NIC register definitions
*
*
* Modification history
*
* Revision 2.2 1993/11/29 16:33:39 davidg
* From Thomas Sandford <t.d.g.sandford@comp.brad.ac.uk>
* Add support for the 8013W board type
*
* Revision 2.1 1993/11/22 10:52:33 davidg
* patch to add support for SMC8216 (Elite-Ultra) boards
* from Glen H. Lowe
*
* Revision 2.0 93/09/29 00:37:15 davidg
* changed double buffering flag to multi buffering
* made changes/additions for 3c503 multi-buffering
* ...companion to Rev. 2.0 of 'ed' driver.
*
* Revision 1.1 93/06/23 03:01:07 davidg
* Initial revision
*
*/
/*
* Page 0 register offsets
*/
#define ED_P0_CR 0x00 /* Command Register */
#define ED_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */
#define ED_P0_PSTART 0x01 /* Page Start register (write) */
#define ED_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */
#define ED_P0_PSTOP 0x02 /* Page Stop register (write) */
#define ED_P0_BNRY 0x03 /* Boundary Pointer */
#define ED_P0_TSR 0x04 /* Transmit Status Register (read) */
#define ED_P0_TPSR 0x04 /* Transmit Page Start (write) */
#define ED_P0_NCR 0x05 /* Number of Collisions Reg (read) */
#define ED_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */
#define ED_P0_FIFO 0x06 /* FIFO register (read) */
#define ED_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */
#define ED_P0_ISR 0x07 /* Interrupt Status Register */
#define ED_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */
#define ED_P0_RSAR0 0x08 /* Remote Start Address low (write) */
#define ED_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */
#define ED_P0_RSAR1 0x09 /* Remote Start Address high (write) */
#define ED_P0_RBCR0 0x0a /* Remote Byte Count low (write) */
#define ED_P0_RBCR1 0x0b /* Remote Byte Count high (write) */
#define ED_P0_RSR 0x0c /* Receive Status (read) */
#define ED_P0_RCR 0x0c /* Receive Configuration Reg (write) */
#define ED_P0_CNTR0 0x0d /* frame alignment error counter (read) */
#define ED_P0_TCR 0x0d /* Transmit Configuration Reg (write) */
#define ED_P0_CNTR1 0x0e /* CRC error counter (read) */
#define ED_P0_DCR 0x0e /* Data Configuration Reg (write) */
#define ED_P0_CNTR2 0x0f /* missed packet counter (read) */
#define ED_P0_IMR 0x0f /* Interrupt Mask Register (write) */
/*
* Page 1 register offsets
*/
#define ED_P1_CR 0x00 /* Command Register */
#define ED_P1_PAR0 0x01 /* Physical Address Register 0 */
#define ED_P1_PAR1 0x02 /* Physical Address Register 1 */
#define ED_P1_PAR2 0x03 /* Physical Address Register 2 */
#define ED_P1_PAR3 0x04 /* Physical Address Register 3 */
#define ED_P1_PAR4 0x05 /* Physical Address Register 4 */
#define ED_P1_PAR5 0x06 /* Physical Address Register 5 */
#define ED_P1_CURR 0x07 /* Current RX ring-buffer page */
#define ED_P1_MAR0 0x08 /* Multicast Address Register 0 */
#define ED_P1_MAR1 0x09 /* Multicast Address Register 1 */
#define ED_P1_MAR2 0x0a /* Multicast Address Register 2 */
#define ED_P1_MAR3 0x0b /* Multicast Address Register 3 */
#define ED_P1_MAR4 0x0c /* Multicast Address Register 4 */
#define ED_P1_MAR5 0x0d /* Multicast Address Register 5 */
#define ED_P1_MAR6 0x0e /* Multicast Address Register 6 */
#define ED_P1_MAR7 0x0f /* Multicast Address Register 7 */
/*
* Page 2 register offsets
*/
#define ED_P2_CR 0x00 /* Command Register */
#define ED_P2_PSTART 0x01 /* Page Start (read) */
#define ED_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */
#define ED_P2_PSTOP 0x02 /* Page Stop (read) */
#define ED_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */
#define ED_P2_RNPP 0x03 /* Remote Next Packet Pointer */
#define ED_P2_TPSR 0x04 /* Transmit Page Start (read) */
#define ED_P2_LNPP 0x05 /* Local Next Packet Pointer */
#define ED_P2_ACU 0x06 /* Address Counter Upper */
#define ED_P2_ACL 0x07 /* Address Counter Lower */
#define ED_P2_RCR 0x0c /* Receive Configuration Register (read) */
#define ED_P2_TCR 0x0d /* Transmit Configuration Register (read) */
#define ED_P2_DCR 0x0e /* Data Configuration Register (read) */
#define ED_P2_IMR 0x0f /* Interrupt Mask Register (read) */
/*
* Command Register (CR) definitions
*/
/*
* STP: SToP. Software reset command. Takes the controller offline. No
* packets will be received or transmitted. Any reception or
* transmission in progress will continue to completion before
* entering reset state. To exit this state, the STP bit must
* reset and the STA bit must be set. The software reset has
* executed only when indicated by the RST bit in the ISR being
* set.
*/
#define ED_CR_STP 0x01
/*
* STA: STArt. This bit is used to activate the NIC after either power-up,
* or when the NIC has been put in reset mode by software command
* or error.
*/
#define ED_CR_STA 0x02
/*
* TXP: Transmit Packet. This bit must be set to indicate transmission of
* a packet. TXP is internally reset either after the transmission is
* completed or aborted. This bit should be set only after the Transmit
* Byte Count and Transmit Page Start register have been programmed.
*/
#define ED_CR_TXP 0x04
/*
* RD0, RD1, RD2: Remote DMA Command. These three bits control the operation
* of the remote DMA channel. RD2 can be set to abort any remote DMA
* command in progress. The Remote Byte Count registers should be cleared
* when a remote DMA has been aborted. The Remote Start Addresses are not
* restored to the starting address if the remote DMA is aborted.
*
* RD2 RD1 RD0 function
* 0 0 0 not allowed
* 0 0 1 remote read
* 0 1 0 remote write
* 0 1 1 send packet
* 1 X X abort
*/
#define ED_CR_RD0 0x08
#define ED_CR_RD1 0x10
#define ED_CR_RD2 0x20
/*
* PS0, PS1: Page Select. The two bits select which register set or 'page' to
* access.
*
* PS1 PS0 page
* 0 0 0
* 0 1 1
* 1 0 2
* 1 1 reserved
*/
#define ED_CR_PS0 0x40
#define ED_CR_PS1 0x80
/* bit encoded aliases */
#define ED_CR_PAGE_0 0x00 /* (for consistency) */
#define ED_CR_PAGE_1 0x40
#define ED_CR_PAGE_2 0x80
/*
* Interrupt Status Register (ISR) definitions
*/
/*
* PRX: Packet Received. Indicates packet received with no errors.
*/
#define ED_ISR_PRX 0x01
/*
* PTX: Packet Transmitted. Indicates packet transmitted with no errors.
*/
#define ED_ISR_PTX 0x02
/*
* RXE: Receive Error. Indicates that a packet was received with one or more
* the following errors: CRC error, frame alignment error, FIFO overrun,
* missed packet.
*/
#define ED_ISR_RXE 0x04
/*
* TXE: Transmission Error. Indicates that an attempt to transmit a packet
* resulted in one or more of the following errors: excessive
* collisions, FIFO underrun.
*/
#define ED_ISR_TXE 0x08
/*
* OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network
* would exceed (has exceeded?) the boundry pointer, resulting in data
* that was previously received and not yet read from the buffer to be
* overwritten.
*/
#define ED_ISR_OVW 0x10
/*
* CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley
* Counters has been set.
*/
#define ED_ISR_CNT 0x20
/*
* RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed.
*/
#define ED_ISR_RDC 0x40
/*
* RST: Reset status. Set when the NIC enters the reset state and cleared when a
* Start Command is issued to the CR. This bit is also set when a receive
* ring-buffer overrun (OverWrite) occurs and is cleared when one or more
* packets have been removed from the ring. This is a read-only bit.
*/
#define ED_ISR_RST 0x80
/*
* Interrupt Mask Register (IMR) definitions
*/
/*
* PRXE: Packet Received interrupt Enable. If set, a received packet will cause
* an interrupt.
*/
#define ED_IMR_PRXE 0x01
/*
* PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when
* a packet transmission completes.
*/
#define ED_IMR_PTXE 0x02
/*
* RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a
* packet is received with an error.
*/
#define ED_IMR_RXEE 0x04
/*
* TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever
* a transmission results in an error.
*/
#define ED_IMR_TXEE 0x08
/*
* OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever
* the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded.
*/
#define ED_IMR_OVWE 0x10
/*
* CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever
* the MSB of one or more of the Network Statistics counters has been set.
*/
#define ED_IMR_CNTE 0x20
/*
* RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated
* when a remote DMA transfer has completed.
*/
#define ED_IMR_RDCE 0x40
/*
* bit 7 is unused/reserved
*/
/*
* Data Configuration Register (DCR) definitions
*/
/*
* WTS: Word Transfer Select. WTS establishes byte or word transfers for
* both remote and local DMA transfers
*/
#define ED_DCR_WTS 0x01
/*
* BOS: Byte Order Select. BOS sets the byte order for the host.
* Should be 0 for 80x86, and 1 for 68000 series processors
*/
#define ED_DCR_BOS 0x02
/*
* LAS: Long Address Select. When LAS is 1, the contents of the remote
* DMA registers RSAR0 and RSAR1 are used to provide A16-A31
*/
#define ED_DCR_LAS 0x04
/*
* LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2
* of the TCR must also be programmed for loopback operation.
* When 1, normal operation is selected.
*/
#define ED_DCR_LS 0x08
/*
* AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer
* under program control. When 1, remote DMA is automatically initiated
* and the boundry pointer is automatically updated
*/
#define ED_DCR_AR 0x10
/*
* FT0, FT1: Fifo Threshold select.
* FT1 FT0 Word-width Byte-width
* 0 0 1 word 2 bytes
* 0 1 2 words 4 bytes
* 1 0 4 words 8 bytes
* 1 1 8 words 12 bytes
*
* During transmission, the FIFO threshold indicates the number of bytes
* or words that the FIFO has filled from the local DMA before BREQ is
* asserted. The transmission threshold is 16 bytes minus the receiver
* threshold.
*/
#define ED_DCR_FT0 0x20
#define ED_DCR_FT1 0x40
/*
* bit 7 (0x80) is unused/reserved
*/
/*
* Transmit Configuration Register (TCR) definitions
*/
/*
* CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC
* is not appended by the transmitter.
*/
#define ED_TCR_CRC 0x01
/*
* LB0, LB1: Loopback control. These two bits set the type of loopback that is
* to be performed.
*
* LB1 LB0 mode
* 0 0 0 - normal operation (DCR_LS = 0)
* 0 1 1 - internal loopback (DCR_LS = 0)
* 1 0 2 - external loopback (DCR_LS = 1)
* 1 1 3 - external loopback (DCR_LS = 0)
*/
#define ED_TCR_LB0 0x02
#define ED_TCR_LB1 0x04
/*
* ATD: Auto Transmit Disable. Clear for normal operation. When set, allows
* another station to disable the NIC's transmitter by transmitting to
* a multicast address hashing to bit 62. Reception of a multicast address
* hashing to bit 63 enables the transmitter.
*/
#define ED_TCR_ATD 0x08
/*
* OFST: Collision Offset enable. This bit when set modifies the backoff
* algorithm to allow prioritization of nodes.
*/
#define ED_TCR_OFST 0x10
/*
* bits 5, 6, and 7 are unused/reserved
*/
/*
* Transmit Status Register (TSR) definitions
*/
/*
* PTX: Packet Transmitted. Indicates successful transmission of packet.
*/
#define ED_TSR_PTX 0x01
/*
* bit 1 (0x02) is unused/reserved
*/
/*
* COL: Transmit Collided. Indicates that the transmission collided at least
* once with another station on the network.
*/
#define ED_TSR_COL 0x04
/*
* ABT: Transmit aborted. Indicates that the transmission was aborted due to
* excessive collisions.
*/
#define ED_TSR_ABT 0x08
/*
* CRS: Carrier Sense Lost. Indicates that carrier was lost during the
* transmission of the packet. (Transmission is not aborted because
* of a loss of carrier)
*/
#define ED_TSR_CRS 0x10
/*
* FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/
* transmission memory before the FIFO emptied. Transmission of the
* packet was aborted.
*/
#define ED_TSR_FU 0x20
/*
* CDH: CD Heartbeat. Indicates that the collision detection circuitry
* isn't working correctly during a collision heartbeat test.
*/
#define ED_TSR_CDH 0x40
/*
* OWC: Out of Window Collision: Indicates that a collision occurred after
* a slot time (51.2us). The transmission is rescheduled just as in
* normal collisions.
*/
#define ED_TSR_OWC 0x80
/*
* Receiver Configuration Register (RCR) definitions
*/
/*
* SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1,
* packets with CRC and frame errors are not discarded.
*/
#define ED_RCR_SEP 0x01
/*
* AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded.
* If set to 1, packets with less than 64 byte are not discarded.
*/
#define ED_RCR_AR 0x02
/*
* AB: Accept Broadcast. If set, packets sent to the broadcast address will be
* accepted.
*/
#define ED_RCR_AB 0x04
/*
* AM: Accept Multicast. If set, packets sent to a multicast address are checked
* for a match in the hashing array. If clear, multicast packets are ignored.
*/
#define ED_RCR_AM 0x08
/*
* PRO: Promiscuous Physical. If set, all packets with a physical addresses are
* accepted. If clear, a physical destination address must match this
* station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM
* must also be set. In addition, the multicast hashing array must be set
* to all 1's so that all multicast addresses are accepted.
*/
#define ED_RCR_PRO 0x10
/*
* MON: Monitor Mode. If set, packets will be checked for good CRC and framing,
* but are not stored in the ring-buffer. If clear, packets are stored (normal
* operation).
*/
#define ED_RCR_MON 0x20
/*
* bits 6 and 7 are unused/reserved.
*/
/*
* Receiver Status Register (RSR) definitions
*/
/*
* PRX: Packet Received without error.
*/
#define ED_RSR_PRX 0x01
/*
* CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame
* alignment errors.
*/
#define ED_RSR_CRC 0x02
/*
* FAE: Frame Alignment Error. Indicates that the incoming packet did not end on
* a byte boundry and the CRC did not match at the last byte boundry.
*/
#define ED_RSR_FAE 0x04
/*
* FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA)
* causing it to overrun. Reception of the packet is aborted.
*/
#define ED_RSR_FO 0x08
/*
* MPA: Missed Packet. Indicates that the received packet couldn't be stored in
* the ring-buffer because of insufficient buffer space (exceeding the
* boundry pointer), or because the transfer to the ring-buffer was inhibited
* by RCR_MON - monitor mode.
*/
#define ED_RSR_MPA 0x10
/*
* PHY: Physical address. If 0, the packet received was sent to a physical address.
* If 1, the packet was accepted because of a multicast/broadcast address
* match.
*/
#define ED_RSR_PHY 0x20
/*
* DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor
* mode. Cleared when the receiver exits monitor mode.
*/
#define ED_RSR_DIS 0x40
/*
* DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs
* are active, and the transceiver has set the CD line as a result of the
* jabber.
*/
#define ED_RSR_DFR 0x80
/*
* receive ring discriptor
*
* The National Semiconductor DS8390 Network interface controller uses
* the following receive ring headers. The way this works is that the
* memory on the interface card is chopped up into 256 bytes blocks.
* A contiguous portion of those blocks are marked for receive packets
* by setting start and end block #'s in the NIC. For each packet that
* is put into the receive ring, one of these headers (4 bytes each) is
* tacked onto the front. The first byte is a copy of the receiver status
* register at the time the packet was received.
*/
struct ed_ring {
u_char rsr; /* receiver status */
u_char next_packet; /* pointer to next packet */
u_short count; /* bytes in packet (length + 4) */
};
/*
* Common constants
*/
#define ED_PAGE_SIZE 256 /* Size of RAM pages in bytes */
#define ED_TXBUF_SIZE 6 /* Size of TX buffer in pages */
/*
* Vendor types
*/
#define ED_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */
#define ED_VENDOR_3COM 0x01 /* 3Com */
#define ED_VENDOR_NOVELL 0x02 /* Novell */
#define ED_VENDOR_PCMCIA 0x03 /* Generic pcmcia */
/*
* Compile-time config flags
*/
/*
* this sets the default for enabling/disablng the tranceiver
*/
#define ED_FLAGS_DISABLE_TRANCEIVER 0x0001
/*
* This forces the board to be used in 8/16bit mode even if it
* autoconfigs differently
*/
#define ED_FLAGS_FORCE_8BIT_MODE 0x0002
#define ED_FLAGS_FORCE_16BIT_MODE 0x0004
/*
* This disables the use of double transmit buffers.
*/
#define ED_FLAGS_NO_MULTI_BUFFERING 0x0008
/*
* This forces all operations with the NIC memory to use Programmed
* I/O (i.e. not via shared memory)
*/
#define ED_FLAGS_FORCE_PIO 0x0010
/*
* Definitions for Western digital/SMC WD80x3 series ASIC
*/
/*
* Memory Select Register (MSR)
*/
#define ED_WD_MSR 0
/* next three definitions for Toshiba */
#define ED_WD_MSR_POW 0x02 /* 0 = power save, 1 = normal (R/W) */
#define ED_WD_MSR_BSY 0x04 /* gate array busy (R) */
#define ED_WD_MSR_LEN 0x20 /* data bus width, 0 = 16 bits,
1 = 8 bits (R/W) */
#define ED_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */
#define ED_WD_MSR_MENB 0x40 /* Memory enable */
#define ED_WD_MSR_RST 0x80 /* Reset board */
/*
* Interface Configuration Register (ICR)
*/
#define ED_WD_ICR 1
#define ED_WD_ICR_16BIT 0x01 /* 16-bit interface */
#define ED_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */
#define ED_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */
#define ED_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */
#define ED_WD_ICR_RLA 0x10 /* recall LAN address */
#define ED_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */
#define ED_WD_ICR_RIO 0x40 /* recall i/o address */
#define ED_WD_ICR_STO 0x80 /* store to non-volatile memory */
#ifdef TOSH_ETHER
#define ED_WD_ICR_MEM 0xe0 /* shared mem address A15-A13 (R/W) */
#define ED_WD_ICR_MSZ1 0x0f /* memory size, 0x08 = 64K, 0x04 = 32K,
0x02 = 16K, 0x01 = 8K */
/* 64K can only be used if mem address
above 1Mb */
/* IAR holds address A23-A16 (R/W) */
#endif
/*
* IO Address Register (IAR)
*/
#define ED_WD_IAR 2
/*
* EEROM Address Register
*/
#define ED_WD_EAR 3
/*
* Interrupt Request Register (IRR)
*/
#define ED_WD_IRR 4
#define ED_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */
#define ED_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */
#define ED_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */
#define ED_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */
#define ED_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */
/*
* The three bits of the encoded IRQ are decoded as follows:
*
* IR2 IR1 IR0 IRQ
* 0 0 0 2/9
* 0 0 1 3
* 0 1 0 5
* 0 1 1 7
* 1 0 0 10
* 1 0 1 11
* 1 1 0 15
* 1 1 1 4
*/
#define ED_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
#define ED_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
#define ED_WD_IRR_IEN 0x80 /* Interrupt enable */
/*
* LA Address Register (LAAR)
*/
#define ED_WD_LAAR 5
#define ED_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */
#define ED_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */
#define ED_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */
#define ED_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */
/* i/o base offset to station address/card-ID PROM */
#define ED_WD_PROM 8
/*
* 83C790 specific registers
*/
/*
* Hardware Support Register (HWR) ('790)
*/
#define ED_WD790_HWR 4
#define WD_WD790_HWR_NUKE 0x10 /* hardware reset */
#define ED_WD790_HWR_LPRM 0x40 /* LAN PROM select */
#define ED_WD790_HWR_SWH 0x80 /* switch register set */
/*
* ICR790 Interrupt Control Register for the 83C790
*/
#define ED_WD790_ICR 6
#define ED_WD790_ICR_EIL 0x01 /* enable interrupts */
/*
* REV/IOPA Revision / I/O Pipe register for the 83C79X
*/
#define ED_WD790_REV 7
#define ED_WD790 0x20
#define ED_WD795 0x40
/*
* 79X RAM Address Register (RAR)
* Enabled with SWH bit=1 in HWR register
*/
#define ED_WD790_RAR 0x0b
#define ED_WD790_RAR_SZ8 0x00 /* 8k memory buffer */
#define ED_WD790_RAR_SZ16 0x10 /* 16k memory buffer */
#define ED_WD790_RAR_SZ32 0x20 /* 32k memory buffer */
#define ED_WD790_RAR_SZ64 0x30 /* 64k memory buffer */
/*
* General Control Register (GCR)
* Enabled with SWH bit=1 in HWR register
*/
#define ED_WD790_GCR 0x0d
#define ED_WD790_GCR_IR0 0x04 /* bit 0 of encoded IRQ */
#define ED_WD790_GCR_IR1 0x08 /* bit 1 of encoded IRQ */
#define ED_WD790_GCR_ZWSEN 0x20 /* zero wait state enable */
#define ED_WD790_GCR_IR2 0x40 /* bit 2 of encoded IRQ */
#define ED_WD790_GCR_LIT 0x01 /* Link Integrity Test Enable */
/*
* The three bits of the encoded IRQ are decoded as follows:
*
* IR2 IR1 IR0 IRQ
* 0 0 0 none
* 0 0 1 9
* 0 1 0 3
* 0 1 1 5
* 1 0 0 7
* 1 0 1 10
* 1 1 0 11
* 1 1 1 15
*/
/* i/o base offset to CARD ID */
#define ED_WD_CARD_ID ED_WD_PROM+6
/* Board type codes in card ID */
#define ED_TYPE_WD8003S 0x02
#define ED_TYPE_WD8003E 0x03
#define ED_TYPE_WD8013EBT 0x05
#define ED_TYPE_TOSHIBA1 0x11 /* named PCETA1 */
#define ED_TYPE_TOSHIBA2 0x12 /* named PCETA2 */
#define ED_TYPE_TOSHIBA3 0x13 /* named PCETB */
#define ED_TYPE_TOSHIBA4 0x14 /* named PCETC */
#define ED_TYPE_WD8003W 0x24
#define ED_TYPE_WD8003EB 0x25
#define ED_TYPE_WD8013W 0x26
#define ED_TYPE_WD8013EP 0x27
#define ED_TYPE_WD8013WC 0x28
#define ED_TYPE_WD8013EPC 0x29
#define ED_TYPE_SMC8216T 0x2a
#define ED_TYPE_SMC8216C 0x2b
#define ED_TYPE_WD8013EBP 0x2c
/* Bit definitions in card ID */
#define ED_WD_REV_MASK 0x1f /* Revision mask */
#define ED_WD_SOFTCONFIG 0x20 /* Soft config */
#define ED_WD_LARGERAM 0x40 /* Large RAM */
#define ED_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */
/*
* Checksum total. All 8 bytes in station address PROM will add up to this
*/
#ifdef TOSH_ETHER
#define ED_WD_ROM_CHECKSUM_TOTAL 0xA5
#else
#define ED_WD_ROM_CHECKSUM_TOTAL 0xFF
#endif
#define ED_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */
#define ED_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */
#define ED_WD_IO_PORTS 32 /* # of i/o addresses used */
#define ED_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */
/*
* Definitions for 3Com 3c503
*/
#define ED_3COM_NIC_OFFSET 0
#define ED_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */
/*
* XXX - The I/O address range is fragmented in the 3c503; this is the
* number of regs at iobase.
*/
#define ED_3COM_IO_PORTS 16 /* # of i/o addresses used */
/* tx memory starts in second bank on 8bit cards */
#define ED_3COM_TX_PAGE_OFFSET_8BIT 0x20
/* tx memory starts in first bank on 16bit cards */
#define ED_3COM_TX_PAGE_OFFSET_16BIT 0x0
/* ...and rx memory starts in second bank */
#define ED_3COM_RX_PAGE_OFFSET_16BIT 0x20
/*
* Page Start Register. Must match PSTART in NIC
*/
#define ED_3COM_PSTR 0
/*
* Page Stop Register. Must match PSTOP in NIC
*/
#define ED_3COM_PSPR 1
/*
* Drq Timer Register. Determines number of bytes to be transfered during
* a DMA burst.
*/
#define ED_3COM_DQTR 2
/*
* Base Configuration Register. Read-only register which contains the
* board-configured I/O base address of the adapter. Bit encoded.
*/
#define ED_3COM_BCFR 3
#define ED_3COM_BCFR_2E0 0x01
#define ED_3COM_BCFR_2A0 0x02
#define ED_3COM_BCFR_280 0x04
#define ED_3COM_BCFR_250 0x08
#define ED_3COM_BCFR_350 0x10
#define ED_3COM_BCFR_330 0x20
#define ED_3COM_BCFR_310 0x40
#define ED_3COM_BCFR_300 0x80
/*
* EPROM Configuration Register. Read-only register which contains the
* board-configured memory base address. Bit encoded.
*/
#define ED_3COM_PCFR 4
#define ED_3COM_PCFR_C8000 0x10
#define ED_3COM_PCFR_CC000 0x20
#define ED_3COM_PCFR_D8000 0x40
#define ED_3COM_PCFR_DC000 0x80
/*
* GA Configuration Register. Gate-Array Configuration Register.
*/
#define ED_3COM_GACFR 5
/*
* mbs2 mbs1 mbs0 start address
* 0 0 0 0x0000
* 0 0 1 0x2000
* 0 1 0 0x4000
* 0 1 1 0x6000
*
* Note that with adapters with only 8K, the setting for 0x2000 must
* always be used.
*/
#define ED_3COM_GACFR_MBS0 0x01
#define ED_3COM_GACFR_MBS1 0x02
#define ED_3COM_GACFR_MBS2 0x04
#define ED_3COM_GACFR_RSEL 0x08 /* enable shared memory */
#define ED_3COM_GACFR_TEST 0x10 /* for GA testing */
#define ED_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
#define ED_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
#define ED_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
/*
* Control Register. Miscellaneous control functions.
*/
#define ED_3COM_CR 6
#define ED_3COM_CR_RST 0x01 /* Reset GA and NIC */
#define ED_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
#define ED_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
#define ED_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
#define ED_3COM_CR_SHARE 0x10 /* select interrupt sharing option */
#define ED_3COM_CR_DBSEL 0x20 /* Double buffer select */
#define ED_3COM_CR_DDIR 0x40 /* DMA direction select */
#define ED_3COM_CR_START 0x80 /* Start DMA controller */
/*
* Status Register. Miscellaneous status information.
*/
#define ED_3COM_STREG 7
#define ED_3COM_STREG_REV 0x07 /* GA revision */
#define ED_3COM_STREG_DIP 0x08 /* DMA in progress */
#define ED_3COM_STREG_DTC 0x10 /* DMA terminal count */
#define ED_3COM_STREG_OFLW 0x20 /* Overflow */
#define ED_3COM_STREG_UFLW 0x40 /* Underflow */
#define ED_3COM_STREG_DPRDY 0x80 /* Data port ready */
/*
* Interrupt/DMA Configuration Register
*/
#define ED_3COM_IDCFR 8
#define ED_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
#define ED_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
#define ED_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
#define ED_3COM_IDCFR_UNUSED 0x08 /* not used */
#define ED_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
#define ED_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
#define ED_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
#define ED_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
/*
* DMA Address Register MSB
*/
#define ED_3COM_DAMSB 9
/*
* DMA Address Register LSB
*/
#define ED_3COM_DALSB 0x0a
/*
* Vector Pointer Register 2
*/
#define ED_3COM_VPTR2 0x0b
/*
* Vector Pointer Register 1
*/
#define ED_3COM_VPTR1 0x0c
/*
* Vector Pointer Register 0
*/
#define ED_3COM_VPTR0 0x0d
/*
* Register File Access MSB
*/
#define ED_3COM_RFMSB 0x0e
/*
* Register File Access LSB
*/
#define ED_3COM_RFLSB 0x0f
/*
* Definitions for Novell NE1000/2000 boards
*/
/*
* Board type codes
*/
#define ED_TYPE_NE1000 0x01
#define ED_TYPE_NE2000 0x02
/*
* Register offsets/total
*/
#define ED_NOVELL_NIC_OFFSET 0x00
#define ED_NOVELL_ASIC_OFFSET 0x10
#define ED_NOVELL_IO_PORTS 32
/*
* Remote DMA data register; for reading or writing to the NIC mem
* via programmed I/O (offset from ASIC base)
*/
#define ED_NOVELL_DATA 0x00
/*
* Reset register; reading from this register causes a board reset
*/
#define ED_NOVELL_RESET 0x0f
#define ED_TYPE_PCMCIA 0x01
#define ED_PCMCIA_PAGE_OFFSET 0x40 /* True for all cards I have seen */
#define ED_PCMCIA_IO_PORTS 32
#define ED_PCMCIA_RESET 0xF /* Reset port */

View file

@ -0,0 +1,143 @@
/*
* Loadable kernel module if_ed driver
* 11 July 1995 Andrew McRae
*
*-------------------------------------------------------------------------
*
* Copyright (c) 1995 Andrew McRae. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/sysent.h>
#include <sys/exec.h>
#include <sys/lkm.h>
#include <sys/errno.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <pccard/card.h>
#include <pccard/slot.h>
/*
* This defines the lkm_misc module use by modload
* to define the module name.
*/
MOD_MISC( "ed")
int edintr(struct pccard_dev *); /* Interrupt handler */
void edunload(struct pccard_dev *); /* Disable driver */
void edsuspend(struct pccard_dev *); /* Suspend driver */
int edinit(struct pccard_dev *, int); /* init device */
static struct pccard_drv ed_info =
{
"ed",
edintr,
edunload,
edsuspend,
edinit,
0,
&net_imask
};
static int opened; /* Rather minimal device state... */
/*
* Module handler that processes loads and unloads.
* Once the module is loaded, the add driver routine is called
* to register the driver.
* If an unload is requested the remove driver routine is
* called to deregister the driver before unloading.
*/
static int
ed_handle( lkmtp, cmd)
struct lkm_table *lkmtp;
int cmd;
{
int i;
struct lkm_misc *args = lkmtp->private.lkm_misc;
int err = 0; /* default = success*/
switch( cmd) {
case LKM_E_LOAD:
/*
* Don't load twice! (lkmexists() is exported by kern_lkm.c)
*/
if( lkmexists( lkmtp))
return( EEXIST);
/*
* Now register the driver
*/
pccard_add_driver(&ed_info);
break; /* Success*/
/*
* Attempt to deregister the driver.
*/
case LKM_E_UNLOAD:
pccard_remove_driver(&ed_info);
break; /* Success*/
default: /* we only understand load/unload*/
err = EINVAL;
break;
}
return( err);
}
/*
* External entry point; should generally match name of .o file. The
* arguments are always the same for all loaded modules. The "load",
* "unload", and "stat" functions in "DISPATCH" will be called under
* their respective circumstances unless their value is "nosys". If
* called, they are called with the same arguments (cmd is included to
* allow the use of a single function, ver is included for version
* matching between modules and the kernel loader for the modules).
*
* Since we expect to link in the kernel and add external symbols to
* the kernel symbol name space in a future version, generally all
* functions used in the implementation of a particular module should
* be static unless they are expected to be seen in other modules or
* to resolve unresolved symbols alread existing in the kernel (the
* second case is not likely to ever occur).
*
* The entry point should return 0 unless it is refusing load (in which
* case it should return an errno from errno.h).
*/
int
lkm_ed(lkmtp, cmd, ver)
struct lkm_table *lkmtp;
int cmd;
int ver;
{
DISPATCH(lkmtp,cmd,ver,ed_handle,ed_handle,nosys)
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
#
# Makefile pccardc
#
# $Id$
#
.PATH: ${.CURDIR}/../pccardd
PROG= pccardc
SRCS= pccardc.c \
dumpcis.c readcis.c printcis.c \
enabler.c \
pccardmem.c \
rdmap.c \
rdreg.c \
wrattr.c \
wrreg.c
CFLAGS+= -I. -I${.CURDIR}/../pccardd
.include <bsd.prog.mk>
##
## Makefile for PCMCIA card programs.
##
#
#PROGS = rdmap rdreg wrreg pccardmem wrattr enabler
#
#enabler: enabler.c
# cc $(CFLAGS) -o enabler enabler.c
#
#rdmap: rdmap.c
# cc $(CFLAGS) -o rdmap rdmap.c
#
#rdreg: rdreg.c
# cc $(CFLAGS) -o rdreg rdreg.c
#
#wrreg: wrreg.c
# cc $(CFLAGS) -o wrreg wrreg.c

View file

@ -0,0 +1,79 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
#include <pccard/cis.h>
#include "readcis.h"
int nocards;
int
dumpcis_main(int argc, char **argv)
{
int node;
for (node = 0; node < 8; node++)
scan(node);
printf("%d slots found\n", nocards);
}
scan(slot)
int slot;
{
int fd, i;
char name[64];
struct cis *cp;
struct slotstate st;
sprintf(name, "/dev/card%d", slot);
fd = open(name, 0);
if (fd < 0)
return;
nocards++;
ioctl(fd, PIOCGSTATE, &st);
if (st.state == filled)
{
cp = readcis(fd);
if (cp)
{
printf("Configuration data for card in slot %d\n",
slot);
dumpcis(cp);
freecis(cp);
}
}
}
dump(p, sz)
unsigned char *p;
int sz;
{
int ad = 0, i;
while (sz > 0)
{
printf("%03x: ", ad);
for (i = 0; i < ((sz < 16) ? sz : 16); i++)
printf(" %02x", p[i]);
printf("\n");
sz -= 16;
p += 16;
ad += 16;
}
}
void *
xmalloc(int sz)
{
void *p;
sz = (sz + 7) & ~7;
p = malloc(sz);
if (p)
bzero(p, sz);
else
{
perror("malloc");
exit(1);
}
return(p);
}

View file

@ -0,0 +1,144 @@
/*
* Enabler for PCCARD. Used for testing drivers etc.
* Options:
* enabler slot driver [ -m card addr size ] [ -i iobase ] [ -q irq ]
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
#include <pccard/cis.h>
void usage();
int
enabler_main(argc, argv)
int argc;
char *argv[];
{
struct drv_desc drv;
struct mem_desc mem;
struct io_desc io;
int fd, err, slot, i, card_addr;
char name[32];
char *p;
bzero(&drv, sizeof(drv));
if (argc < 3)
usage("arg count");
slot = atoi(argv[1]);
if (slot < 0 || slot >= MAXSLOT)
usage("Illegal slot number");
p = argv[2];
while (*p && (*p < '0' || *p > '9'))
p++;
if (*p == 0)
usage("No unit on device name");
drv.unit = atoi(p);
*p = 0;
strcpy(drv.name, argv[2]);
argv += 3;
argc -= 3;
while (argc > 1)
{
if (strcmp(argv[0], "-m")==0)
{
if (argc < 4)
usage("Memory argument error");
if (sscanf(argv[1], "%x", &card_addr)!=1)
usage("Bad card address");
if (sscanf(argv[2], "%x", &drv.mem)!=1)
usage("Bad memory address");
if (sscanf(argv[3], "%d", &i)!=1)
usage("Bad memory size");
drv.memsize = i * 1024;
argc -= 2;
argv += 2;
}
else if (strcmp(argv[0], "-f")==0)
{
if (sscanf(argv[1], "%x", &drv.flags)!=1)
usage("Bad driver flags");
}
else if (strcmp(argv[0], "-a")==0)
{
if (sscanf(argv[1], "%x", &drv.iobase)!=1)
usage("Bad I/O address");
}
else if (strcmp(argv[0], "-i")==0)
{
if (sscanf(argv[1], "%d", &i)!=1 ||
i < 1 || i > 15)
usage("Illegal IRQ");
drv.irqmask = 1 << i;
}
argc -= 2;
argv += 2;
}
if (argc)
usage("no parameter for argument");
printf("drv %s%d, mem 0x%x, size %d, io %d, irq 0x%x, flags 0x%x\n",
drv.name, drv.unit, drv.mem, drv.memsize, drv.iobase,
drv.irqmask, drv.flags);
sprintf(name, "/dev/card%d", slot);
fd = open(name, 2);
if (fd < 0)
{
perror(name);
exit(1);
}
/*
* Map the memory and I/O contexts.
*/
if (drv.mem)
{
mem.window = 0;
mem.flags = MDF_ACTIVE|MDF_16BITS;
mem.start = (caddr_t)drv.mem;
mem.size = drv.memsize;
mem.card = card_addr;
if (ioctl(fd, PIOCSMEM, &mem))
{
perror("Set memory context");
exit(1);
}
}
if (drv.iobase)
{
io.window = 0;
io.flags = IODF_ACTIVE|IODF_CS16;
io.start = drv.iobase;
io.size = 32; /* Blah... */
if (ioctl(fd, PIOCSIO, &io))
{
perror("Set I/O context");
exit(1);
}
}
if (ioctl(fd, PIOCSDRV, &drv))
perror("set driver");
close(fd);
}
/*
* usage - print usage and exit
*/
void
usage(msg)
char *msg;
{
fprintf(stderr, "enabler: %s\n", msg);
fprintf(stderr,
"Usage: enabler slot driver [ -m addr size ] [ -a iobase ] [ -i irq ]\n");
fprintf(stderr,
" -m card addr size : Card address (hex), host address (hex) & size (Kb)\n");
fprintf(stderr,
" -a iobase : I/O port address (hex)\n");
fprintf(stderr,
" -i irq : Interrupt request number (1-15)\n");
fprintf(stderr,
" Example: enabler 0 ed0 -m 2000 d4000 16 -a 300 -i 3\n");
exit(1);
}

View file

@ -0,0 +1,59 @@
#include <stdio.h>
#include <stdlib.h>
typedef int (*main_t)(int , char **);
#define DECL(foo) int foo(int, char**);
DECL(dumpcis_main);
DECL(enabler_main);
DECL(help_main);
DECL(pccardmem_main);
DECL(rdmap_main);
DECL(rdreg_main);
DECL(wrattr_main);
DECL(wrreg_main);
struct {
char *name;
main_t func;
char *help;
} subcommands[] = {
{ "dumpcis", dumpcis_main, "Prints CIS for all cards"},
{ "enabler", enabler_main, "Device driver enabler"},
{ "help", help_main, "Prints command summary"},
{ "pccardmem", pccardmem_main, "Allocate memory for pccard driver"},
{ "rdmap", rdmap_main, "Read pcic mappings"},
{ "rdreg", rdreg_main, "Read pcic register"},
{ "wrattr", wrattr_main, "Write byte to attribute memory"},
{ "wrreg", wrreg_main, "Write pcic register"},
{0, 0}
};
int
main(int argc, char **argv)
{
int i;
for(i=0; argc > 1 && subcommands[i].name; i++) {
if (!strcmp(argv[1],subcommands[i].name)) {
argv[1] = argv[0];
return (*subcommands[i].func)(argc-1,argv+1);
}
}
if (argc > 1)
fprintf(stderr,"Unknown Subcommand.\n");
return help_main(argc,argv);
}
int
help_main(int argc, char **argv)
{
int i;
fprintf(stderr,"Usage:\n");
fprintf(stderr,"\t%s <subcommand> <arg> ...\n",argv[0]);
fprintf(stderr,"Subcommands:\n");
for(i=0; subcommands[i].name; i++)
fprintf(stderr,"\t%s\n\t\t%s\n",
subcommands[i].name, subcommands[i].help);
return 1;
}

View file

@ -0,0 +1,38 @@
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
int
pccardmem_main(argc, argv)
int argc;
char *argv[];
{
int addr = 0;
int fd;
if (argc > 2)
{
fprintf(stderr, "usage: %s [ memory-address ]\n", argv[0]);
exit(1);
}
fd = open("/dev/card0", 0);
if (fd < 0)
{
perror("/dev/card0");
exit(1);
}
if (argc == 2)
{
if (sscanf(argv[1], "%x", &addr) != 1)
{
fprintf(stderr, "arg error\n");
exit(1);
}
}
if (ioctl(fd, PIOCRWMEM, &addr))
perror("ioctl");
else
printf("PCCARD Memory address set to 0x%x\n", addr);
exit(0);
}

View file

@ -0,0 +1,711 @@
/* set tab=4
* dump CIS tuples.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
#include <pccard/cis.h>
#include "readcis.h"
int dump_pwr_desc(unsigned char *);
void print_ext_speed(unsigned char, int);
void
dumpcis(struct cis *cp)
{
struct tuple *tp;
struct tuple_list *tl;
int count = 0, sz, ad, i;
unsigned char *p;
for (tl = cp->tlist; tl; tl = tl->next)
for (tp = tl->tuples; tp; tp = tp->next)
{
printf("Tuple #%d, code = 0x%x (%s), length = %d\n",
++count, tp->code, tuple_name(tp->code), tp->length);
p = tp->data;
sz = tp->length;
ad = 0;
while (sz > 0)
{
printf(" %03x: ", ad);
for (i = 0; i < ((sz < 16) ? sz : 16); i++)
printf(" %02x", p[i]);
printf("\n");
sz -= 16;
p += 16;
ad += 16;
}
switch(tp->code)
{
default:
break;
case CIS_MEM_COMMON: /* 0x01 */
dump_device_desc(tp->data, tp->length, "Common");
break;
case CIS_CHECKSUM: /* 0x10 */
if (tp->length == 5)
{
printf("\tChecksum from offset %d, length %d, value is 0x%x\n",
(short)((tp->data[1] << 8) | tp->data[0]),
(tp->data[3] << 8) | tp->data[2],
tp->data[4]);
}
else
printf("\tIllegal length for checksum!\n");
break;
case CIS_LONGLINK_A: /* 0x11 */
printf("\tLong link to attribute memory, address 0x%x\n",
(tp->data[3] << 24) |
(tp->data[2] << 16) |
(tp->data[1] << 8) |
tp->data[0]);
break;
case CIS_LONGLINK_C: /* 0x12 */
printf("\tLong link to common memory, address 0x%x\n",
(tp->data[3] << 24) |
(tp->data[2] << 16) |
(tp->data[1] << 8) |
tp->data[0]);
break;
break;
case CIS_INFO_V1: /* 0x15 */
dump_info_v1(tp->data, tp->length);
break;
case CIS_ALTSTR: /* 0x16 */
break;
case CIS_MEM_ATTR: /* 0x17 */
dump_device_desc(tp->data, tp->length, "Attribute");
break;
case CIS_JEDEC_C: /* 0x18 */
break;
case CIS_JEDEC_A: /* 0x19 */
break;
case CIS_CONF_MAP: /* 0x1A */
dump_config_map(tp);
break;
case CIS_CONFIG: /* 0x1B */
dump_cis_config(tp);
break;
case CIS_DEVICE_OC: /* 0x1C */
dump_other_cond(tp->data);
break;
case CIS_DEVICE_OA: /* 0x1D */
dump_other_cond(tp->data);
break;
case CIS_DEVICEGEO: /* 0x1E */
break;
case CIS_DEVICEGEO_A: /* 0x1F */
break;
case CIS_MANUF_ID: /* 0x20 */
printf("\tPCMCIA ID = 0x%x, OEM ID = 0x%x\n",
(tp->data[1] << 8) | tp->data[0],
(tp->data[3] << 8) | tp->data[2]);
break;
case CIS_FUNC_ID: /* 0x21 */
switch(tp->data[0])
{
default:
printf("\tUnknown function");
break;
case 0:
printf("\tMultifunction card");
break;
case 1:
printf("\tMemory card");
break;
case 2:
printf("\tSerial port/modem");
break;
case 3:
printf("\tParallel port");
break;
case 4:
printf("\tFixed disk card");
break;
case 5:
printf("\tVideo adapter");
break;
case 6:
printf("\tNetwork/LAN adapter");
break;
case 7:
printf("\tAIMS");
break;
}
printf("%s%s\n", (tp->data[1] & 1) ? " - POST initialize" : "",
(tp->data[1] & 2) ? " - Card has ROM" : "");
break;
case CIS_FUNC_EXT: /* 0x22 */
dump_func_ext(tp->data, tp->length);
break;
case CIS_VERS_2: /* 0x40 */
break;
}
}
}
/*
* Dump configuration map tuple.
*/
dump_config_map(struct tuple *tp)
{
unsigned char *p, x;
int rlen, mlen;
int i;
union {
unsigned long l;
unsigned char b[4];
}u;
rlen = (tp->data[0] & 3)+1;
mlen = ((tp->data[0] >> 2) & 3)+1;
u.l = 0;
p = tp->data + 2;
for (i = 0 ; i < rlen; i++)
u.b[i] = *p++;
printf("\tReg len = %d, config register addr = 0x%x, last config = 0x%x\n",
rlen, u.l, tp->data[1]);
if (mlen)
printf("\tRegisters: ");
for (i = 0; i < mlen; i++, p++)
{
for (x = 0x1; x; x <<= 1)
printf("%c", x & *p ? 'X' : '-');
printf(" ");
}
printf("\n");
}
/*
* Dump a config entry.
*/
dump_cis_config(struct tuple *tp)
{
unsigned char *p, feat;
int i, j;
char c;
union {
unsigned long l;
unsigned char b[4];
}u;
p = tp->data;
printf("\tConfig index = 0x%x%s\n", *p & 0x3F,
*p & 0x40 ? "(default)" : "");
if (*p & 0x80)
{
p++;
printf("\tInterface byte = 0x%x ", *p);
switch (*p & 0xF)
{
default:
printf("(reserved)");
break;
case 0:
printf("(memory)");
break;
case 1:
printf("(I/O)");
break;
case 4:
case 5:
case 6:
case 7:
case 8:
printf("(custom)");
break;
}
c = ' ';
if (*p & 0x10)
{
printf(" BVD1/2 active");
c = ',';
}
if (*p & 0x20)
{
printf("%c card WP active", c); /* Write protect */
c = ',';
}
if (*p & 0x40)
{
printf("%c +RDY/-BSY active", c);
c = ',';
}
if (*p & 0x80)
printf("%c wait signal supported", c);
printf("\n");
}
p++;
feat = *p++;
switch(feat & 3)
{
case 0:
break;
case 1:
printf("\tVcc pwr:\n");
p += dump_pwr_desc(p);
break;
case 2:
printf("\tVcc pwr:\n");
p += dump_pwr_desc(p);
printf("\tVpp pwr:\n");
p += dump_pwr_desc(p);
break;
case 3:
printf("\tVcc pwr:\n");
p += dump_pwr_desc(p);
printf("\tVpp1 pwr:\n");
p += dump_pwr_desc(p);
printf("\tVpp2 pwr:\n");
p += dump_pwr_desc(p);
break;
}
if (feat & 0x4)
{
i = *p & 3;
j = (*p >> 2) & 7;
p++;
if (i != 3)
{
printf("\tWait scale ");
print_ext_speed(*p, i);
while (*p & 0x80)
p++;
printf("\n");
}
if (j != 7)
{
printf("\tRDY/BSY scale ");
print_ext_speed(*p, j);
while (*p & 0x80)
p++;
printf("\n");
}
}
if (feat & 0x8)
{
if (*p & 0x1F)
printf("\tCard decodes %d address lines", *p & 0x1F);
else
printf("\tCard provides address decode");
switch((*p >> 5) & 3)
{
case 0:
break;
case 1:
printf(", 8 Bit I/O only");
break;
case 2:
printf(", limited 8/16 Bit I/O");
break;
case 3:
printf(", full 8/16 Bit I/O");
break;
}
printf("\n");
if (*p & 0x80)
{
p++;
c = *p++;
for (i = 0; i <= (c & 0xF); i++)
{
printf("\t\tI/O address # %d: ", i + 1);
switch ((c >> 4) & 3)
{
case 0:
break;
case 1:
printf("block start = 0x%x", *p++);
break;
case 2:
printf("block start = 0x%x", (p[1] << 8) | *p);
p += 2;
break;
case 3:
printf("block start = 0x%x",
(p[3] << 24) | (p[2] << 16) |
(p[1] << 8) | *p);
p += 4;
break;
}
switch ((c >> 6) & 3)
{
case 0:
break;
case 1:
printf(" block length = 0x%x", *p++ + 1);
break;
case 2:
printf(" block length = 0x%x", ((p[1] << 8) | *p)+1);
p += 2;
break;
case 3:
printf(" block length = 0x%x",
((p[3] << 24) | (p[2] << 16) |
(p[1] << 8) | *p) + 1);
p += 4;
break;
}
printf("\n");
}
}
}
/*
* IRQ descriptor
*/
if (feat & 0x10)
{
printf("\t\tIRQ modes:");
c = ' ';
if (*p & 0x20)
{
printf(" Level");
c = ',';
}
if (*p & 0x40)
{
printf("%c Pulse", c);
c = ',';
}
if (*p & 0x80)
printf("%c Shared", c);
printf("\n");
if (*p & 0x10)
{
i = p[0] | (p[1] << 8);
printf("\t\tIRQs: ");
if (*p & 1)
printf(" NMI");
if (*p & 0x2)
printf(" IOCK");
if (*p & 0x4)
printf(" BERR");
if (*p & 0x8)
printf(" VEND");
for ( j = 0; j < 16; j++)
if (i & (1 << j))
printf(" %d", j);
printf("\n");
p += 3;
}
else
{
printf("\t\tIRQ level = %d\n", *p & 0xF);
p++;
}
}
switch((feat >> 5) & 3)
{
case 0:
break;
case 1:
printf("\tMemory space length = 0x%x\n", (p[1] << 8) | p[0]);
p += 2;
break;
case 2:
printf("\tMemory space address = 0x%x, length = 0x%x\n",
(p[3] << 8) | p[2],
(p[1] << 8) | p[0]);
p += 4;
break;
/*
* Memory descriptors.
*/
case 3:
c = *p++;
for (i = 0; i <= (c & 7); i++)
{
printf("\tMemory descriptor %d\n\t\t", i+1);
switch ((c >> 3) & 3)
{
case 0:
break;
case 1:
printf(" blk length = 0x%x00", *p++);
break;
case 2:
printf(" blk length = 0x%x00", (p[1] << 8) | *p);
p += 2;
break;
case 3:
printf(" blk length = 0x%x00",
(p[3] << 24) | (p[2] << 16) |
(p[1] << 8) | *p);
p += 4;
break;
}
switch ((c >> 5) & 3)
{
case 0:
break;
case 1:
printf(" card addr = 0x%x00", *p++);
break;
case 2:
printf(" card addr = 0x%x00", (p[1] << 8) | *p);
p += 2;
break;
case 3:
printf(" card addr = 0x%x00",
(p[3] << 24) | (p[2] << 16) |
(p[1] << 8) | *p);
p += 4;
break;
}
if (c & 0x80)
switch ((c >> 5) & 3)
{
case 0:
break;
case 1:
printf(" host addr = 0x%x00", *p++);
break;
case 2:
printf(" host addr = 0x%x00", (p[1] << 8) | *p);
p += 2;
break;
case 3:
printf(" host addr = 0x%x00",
(p[3] << 24) | (p[2] << 16) |
(p[1] << 8) | *p);
p += 4;
break;
}
printf("\n");
}
break;
}
if (feat & 0x80)
{
printf("\tMax twin cards = %d\n", *p & 7);
printf("\tMisc attr:");
if (*p & 0x8)
printf(" (Audio-BVD2)");
if (*p & 0x10)
printf(" (Read-only)");
if (*p & 0x20)
printf(" (Power down supported)");
if (*p & 0x80)
{
printf(" (Ext byte = 0x%x)", p[1]);
p++;
}
printf("\n");
p++;
}
}
/*
* dump_other_cond - Dump other conditions.
*/
dump_other_cond(unsigned char *p)
{
if (p[0])
{
printf("\t");
if (p[0] & 1)
printf("(MWAIT)");
if (p[0] & 2)
printf(" (3V card)");
if (p[0] & 0x80)
printf(" (Extension bytes follow)");
printf("\n");
}
}
/*
* Dump power descriptor.
*/
int
dump_pwr_desc(unsigned char *p)
{
int len = 1, i;
unsigned char mask;
char **expp;
static char *pname[] =
{ "Nominal operating supply voltage",
"Minimum operating supply voltage",
"Maximum operating supply voltage",
"Continuous supply current",
"Max current average over 1 second",
"Max current average over 10 ms",
"Power down supply current",
"Reserved"
};
static char *vexp[] =
{ "10uV", "100uV", "1mV", "10mV", "100mV", "1V", "10V", "100V" };
static char *cexp[] =
{ "10nA", "1uA", "10uA", "100uA", "1mA", "10mA", "100mA", "1A" };
static char *mant[] =
{ "1", "1.2", "1.3", "1.5", "2", "2.5", "3", "3.5", "4", "4.5",
"5", "5.5", "6", "7", "8", "9" };
mask = *p++;
expp = vexp;
for (i = 0; i < 8; i++)
if (mask & (1 << i))
{
len++;
if (i >= 3)
expp = cexp;
printf("\t\t%s: ", pname[i]);
printf("%s x %s",
mant[(*p >> 3) & 0xF],
expp[*p & 7]);
while (*p & 0x80)
{
len++;
p++;
printf(", ext = 0x%x", *p);
}
printf("\n");
p++;
}
return(len);
}
dump_device_desc(unsigned char *p, int len, char *type)
{
static char *un_name[] =
{ "512b", "2Kb", "8Kb", "32Kb",
"128Kb", "512Kb", "2Mb", "reserved"};
static char *speed[] =
{ "No speed", "250nS", "200nS", "150nS",
"100nS", "Reserved", "Reserved" };
static char *dev[] =
{ "No device", "Mask ROM", "OTPROM", "UV EPROM",
"EEPROM", "FLASH EEPROM", "SRAM", "DRAM",
"Reserved", "Reserved", "Reserved", "Reserved",
"Reserved", "Function specific", "Extended",
"Reserved" };
int count = 0;
while (*p != 0xFF && len > 0)
{
unsigned char x;
x = *p++;
len -= 2;
if (count++ == 0)
printf("\t%s memory device information:\n", type);
printf("\t\tDevice number %d, type %s, WPS = %s\n",
count, dev[x >> 4], (x & 0x8) ? "ON" : "OFF");
if ((x & 7) == 7)
{
len--;
if (*p)
{
printf("\t\t");
print_ext_speed(*p, 0);
while (*p & 0x80)
{
p++;
len--;
}
}
p++;
}
else
printf("\t\tSpeed = %s", speed[x & 7]);
printf(", Memory block size = %s, %d units\n",
un_name[*p & 7], (*p >> 3) + 1);
p++;
}
}
/*
* Print version info
*/
dump_info_v1(unsigned char *p, int len)
{
printf("\tVersion = %d.%d", p[0], p[1]);
p += 2;
printf(", Manuf = [%s],", p);
while (*p++)
;
printf("card vers = [%s]\n", p);
while (*p++)
;
printf("\tAddit. info = [%s]", p);
while (*p++)
;
printf(",[%s]\n", p);
}
/*
* dump functional extension tuple.
*/
dump_func_ext(unsigned char *p, int len)
{
if (len == 0)
return;
switch(p[0])
{
case 0:
case 8:
case 10:
if (len != 4)
{
printf("\tWrong length for serial extension\n");
return;
}
printf("\tSerial interface extension:\n");
switch(p[1] & 0x1F)
{
default:
printf("\t\tUnkn device");
break;
case 0:
printf("\t\t8250 UART");
break;
case 1:
printf("\t\t16450 UART");
break;
case 2:
printf("\t\t16550 UART");
break;
}
printf(", Parity - %s%s%s%s",
(p[2] & 1) ? "Space," : "",
(p[2] & 2) ? "Mark," : "",
(p[2] & 4) ? "Odd," : "",
(p[2] & 8) ? "Even," : "");
printf("\n");
break;
case 1:
case 5:
case 6:
case 7:
printf("\tModem interface capabilities:\n");
break;
case 2:
printf("\tData modem services available:\n");
break;
case 9:
printf("\tFax/modem services available:\n");
break;
case 4:
printf("\tVoice services available:\n");
break;
}
}
/*
* print_ext_speed - Print extended speed.
*/
void
print_ext_speed(unsigned char x, int scale)
{
static char *mant[] =
{ "Reserved", "1.0", "1.2", "1.3", "1.5", "2.0", "2.5", "3.0",
"3.5", "4.0", "4.5", "5.0", "5.5", "6.0", "7.0", "8.0" };
static char *exp[] =
{ "1 ns", "10 ns", "100 ns", "1 us", "10 us", "100 us",
"1 ms", "10 ms" };
static char *scale_name[] =
{ "None", "10", "100", "1,000", "10,000", "100,000",
"1,000,000", "10,000,000" };
printf("Speed = %s x %s", mant[(x >> 3) & 0xF], exp[x & 7]);
if (scale)
printf(", scaled by %s", scale_name[scale & 7]);
}

View file

@ -0,0 +1,71 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
#include <pccard/cis.h>
int
rdmap_main(argc, argv)
int argc;
char *argv[];
{
int node, mask;
struct card *cp;
for (node = 0; node < 8; node++)
scan(node);
exit(0);
}
static scan(slot)
int slot;
{
int fd, mask;
char blk[1024];
char name[64];
struct slotstate st;
sprintf(name, "/dev/card%d", slot);
fd = open(name, 0);
if (fd < 0)
return;
ioctl(fd, PIOCGSTATE, &st);
/*
if (st.state == filled)
*/
{
dump_mem(fd, st.maxmem);
dump_io(fd, st.maxio);
}
close(fd);
}
dump_mem(fd, nmem)
int fd, nmem;
{
struct mem_desc mem;
int i;
for (i = 0; i < nmem; i++)
{
mem.window = i;
ioctl(fd, PIOCGMEM, &mem);
printf("Mem %d: flags 0x%03x host 0x%6x card %04x size %d bytes\n",
mem.window, mem.flags, mem.start, mem.card, mem.size);
}
}
dump_io(fd, nio)
int fd, nio;
{
struct io_desc io;
int i;
for (i = 0; i < nio; i++)
{
io.window = i;
ioctl(fd, PIOCGIO, &io);
printf("I/O %d: flags 0x%03x port 0x%3x size %d bytes\n",
io.window, io.flags, io.start, io.size);
}
}

View file

@ -0,0 +1,48 @@
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
int
rdreg_main(argc, argv)
int argc;
char *argv[];
{
if (argc != 2)
{
dumpslot(0);
dumpslot(1);
}
else
dumpslot(atoi(argv[1]));
}
dumpslot(sl)
int sl;
{
char name[64];
int fd;
struct pcic_reg r;
sprintf(name, "/dev/card%d", sl);
fd = open(name, 2);
if (fd < 0)
{
perror(name);
return;
}
printf("Registers for slot %d\n", sl);
for (r.reg = 0; r.reg < 0x40; r.reg++)
{
if (ioctl(fd, PIOCGREG, &r))
{
perror("ioctl");
break;
}
if ((r.reg % 16)==0)
printf("%02x:", r.reg);
printf(" %02x", r.value);
if ((r.reg % 16)==15)
printf("\n");
}
close(fd);
}

View file

@ -0,0 +1,46 @@
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
int
wrattr_main(argc, argv)
int argc;
char *argv[];
{
int reg,value;
char name[64], c;
int fd;
off_t offs;
if (argc != 4)
{
fprintf(stderr, "usage: wrmem slot offs value\n");
exit(1);
}
sprintf(name, "/dev/card%d", atoi(argv[1]));
fd = open(name, 2);
if (fd < 0)
{
perror(name);
exit(1);
}
reg = MDF_ATTR;
if (ioctl(fd, PIOCRWFLAG, &reg))
{
perror("ioctl (PIOCRWFLAG)");
exit(1);
}
if (sscanf(argv[2], "%x", &reg) != 1 ||
sscanf(argv[3], "%x", &value) != 1)
{
fprintf(stderr, "arg error\n");
exit(1);
}
offs = reg;
c = value;
lseek(fd, offs, SEEK_SET);
if (write(fd, &c, 1) != 1)
perror(name);
}

View file

@ -0,0 +1,39 @@
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
int
wrreg_main(argc, argv)
int argc;
char *argv[];
{
int reg,value;
char name[64];
int fd;
struct pcic_reg r;
if (argc != 4)
{
fprintf(stderr, "usage: wrreg slot reg value\n");
exit(1);
}
sprintf(name, "/dev/card%d", atoi(argv[1]));
fd = open(name, 2);
if (fd < 0)
{
perror(name);
exit(1);
}
if (sscanf(argv[2], "%x", &reg) != 1 ||
sscanf(argv[3], "%x", &value) != 1)
{
fprintf(stderr, "arg error\n");
exit(1);
}
r.reg = reg;
r.value = value;
if (ioctl(fd, PIOCSREG, &r))
perror("ioctl");
}

View file

@ -0,0 +1,10 @@
# Makefile for pccardd
PROG= pccardd
SRCS= cardd.c file.c util.c readcis.c
MAN8= pccardd.8
MAN5= pccard.conf.5
DPADD= ${LIBUTIL}
LDADD= -lutil
.include <bsd.prog.mk>

View file

@ -0,0 +1,701 @@
#define HERE() printf("<%d>\n",__LINE__)
/*
* pcmciad
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <syslog.h>
#include <varargs.h>
#include "cardd.h"
char *config_file = "/etc/card.conf";
struct card_config *assign_driver(struct card *);
int setup_slot(struct slot *);
void read_ether(struct slot *);
void dump_config_file();
void pr_cmd(struct cmd *);
void readslots();
void slot_change(struct slot *);
void card_removed(struct slot *);
void card_inserted(struct slot *);
/*
* mainline code for cardd
*/
main(int argc, char *argv[])
{
struct slot *sp;
int mask, count, debug = 0, err = 0;
int verbose = 0;
extern char *optarg;
extern int optind, optopt;
while ((count = getopt(argc, argv, ":dvf:")) != -1) {
switch(count) {
case 'd':
setbuf(stdout,0);
setbuf(stderr,0);
debug = 1;
break;
case 'v':
verbose = 1;
break;
case 'f':
config_file = optarg;
break;
case ':':
die("No config file argument");
break;
case '?':
die("Illegal option");
break;
}
}
#ifdef DEBUG
debug = 1;
#endif
io_avail = bit_alloc(IOPORTS); /* Only supports ISA ports */
/*
* Mem allocation done in MEMUNIT units.
*/
mem_avail = bit_alloc(MEMBLKS);
readfile(config_file);
if (verbose)
dump_config_file();
if (!debug)
{
if (daemon(0, 0))
die("fork failed");
openlog("cardd", LOG_PID, LOG_DAEMON);
do_log = 1;
}
printf("Before readslots\n");
readslots();
printf("After readslots\n");
if (slots == 0)
die("No PC-CARD slots");
for (;;)
{
mask = 0;
for (sp = slots; sp; sp = sp->next)
mask |= sp->mask;
printf("Doing select\n");
count = select(32, 0, 0, &mask, 0);
if (count == -1)
{
perror("Select");
continue;
}
if (count)
for (sp = slots; sp; sp = sp->next)
if (mask & sp->mask)
slot_change(sp);
}
}
/*
* Dump configuration file data.
*/
void
dump_config_file()
{
struct driver *drvp;
struct device *devp;
struct card *cp;
struct card_config *confp;
for (cp = cards; cp; cp = cp->next)
{
printf("Card manuf %s, vers %s\n", cp->manuf, cp->version);
printf("Configuration entries:\n");
for (confp = cp->config; confp; confp = confp->next)
printf("\tIndex code = 0x%x, driver name = %s\n",
confp->index, confp->driver->name);
if (cp->insert)
{
printf("Insert commands are:\n");
pr_cmd(cp->insert);
}
if (cp->remove)
{
printf("Remove commands are:\n");
pr_cmd(cp->remove);
}
}
#if 0
for (devp = devlist; devp; devp = devp->next)
{
if (devp->insert)
{
printf("Insert commands are:\n");
pr_cmd(devp->insert);
}
if (devp->remove)
{
printf("Remove commands are:\n");
pr_cmd(devp->remove);
}
}
#endif
}
void
pr_cmd(struct cmd *cp)
{
while (cp)
{
printf("\t%s\n", cp->line);
cp = cp->next;
}
}
/*
* readslots - read all the PCMCIA slots, and build
* a list of the slots.
*/
void
readslots()
{
char name[128];
int i, fd;
struct slot *sp;
for (i = 0; i < MAXSLOT; i++)
{
sprintf(name, CARD_DEVICE, i);
fd = open(name, 2);
if (fd < 0)
continue;
printf("opened %s\n",name);
sp = xmalloc(sizeof(*sp));
sp->fd = fd;
sp->mask = 1 << fd;
sp->name = newstr(name);
sp->slot = i;
sp->state = empty;
/*
* Check to see if the controller memory has been set up.
*/
if (slots == 0)
{
unsigned long mem = 0;
HERE();
if (ioctl(fd, PIOCRWMEM, &mem))
perror("ioctl (PIOCRWMEM)");
if (mem == 0)
{
HERE();
mem = alloc_memory(4*1024);
HERE();
if (mem == 0)
die("Can't allocate memory for controller access");
if (ioctl(fd, PIOCRWMEM, &mem))
perror("ioctl (PIOCRWMEM)");
}
}
sp->next = slots;
slots = sp;
HERE();
#if 0
slot_change(sp);
#endif
HERE();
}
}
/*
* slot_change - Card status has changed.
* read new state and process.
*/
void
slot_change(struct slot *sp)
{
int state;
current_slot = sp;
HERE();
if (ioctl(sp->fd, PIOCGSTATE, &state))
{
perror("ioctl (PIOCGSTATE)");
return;
}
HERE();
if (state == sp->state)
return;
HERE();
sp->state = state;
HERE();
switch (sp->state)
{
case empty:
case noslot:
HERE();
card_removed(sp);
HERE();
break;
case filled:
HERE();
card_inserted(sp);
HERE();
break;
}
}
/*
* card_removed - card has been removed from slot.
* Execute the remove commands, and clear the slot's state.
* Execute the device commands, then the driver commands
* and then the card commands. This is the reverse
* order to the insertion commands
*/
void
card_removed(struct slot *sp)
{
struct driver *drvp;
struct card *cp;
HERE();
if (sp->cis)
freecis(sp->cis);
if (sp->config)
{
sp->config->inuse = 0;
sp->config->driver->inuse = 0;
}
HERE();
if (cp = sp->card)
execute(cp->remove);
HERE();
sp->cis = 0;
sp->config = 0;
HERE();
}
/*
* card_inserted - Card has been inserted;
* - Read the CIS
* - match the card type.
* - Match the driver and allocate a driver instance.
* - Allocate I/O ports, memory and IRQ.
* - Set up the slot.
* - assign the driver (if failed, then terminate).
* - Run the card commands.
* - Run the driver commands
* - Run the device commands
*/
void
card_inserted(struct slot *sp)
{
struct card *cp;
sp->cis = readcis(sp->fd);
if (sp->cis == 0)
{
log_1s("Error reading CIS on %s\n", sp->name);
return;
}
for (cp = cards; cp; cp = cp->next)
if (strcmp(cp->manuf, sp->cis->manuf) == 0 &&
strcmp(cp->version, sp->cis->vers) == 0)
break;
sp->card = cp;
/*
reset_slot(sp);
*/
if (cp == 0)
{
log_1s("No card in database for %s", sp->cis->manuf);
return;
}
if (cp->ether)
read_ether(sp);
sp->config = assign_driver(cp);
if (sp->config == 0)
{
execute(cp->insert);
return;
}
if (assign_io(sp))
{
log_1s("Resource allocation failure for %s", sp->cis->manuf);
return;
}
/*
* Once assigned, then set up the I/O & mem contexts, and
* set up the windows, and then attach the driver.
*/
if (setup_slot(sp))
execute(cp->insert);
#if 0
else
reset_slot(sp);
#endif
}
/*
* read_ether - read ethernet address from card. Offset is
* the offset into the attribute memory of the card.
*/
void
read_ether(struct slot *sp)
{
unsigned char net_addr[12], *p;
lseek(sp->fd, (off_t)sp->card->ether, SEEK_SET);
if (read(sp->fd, net_addr, sizeof(net_addr)) != sizeof(net_addr))
{
logerr("read err on net addr");
return;
}
sp->eaddr[0] = net_addr[0];
sp->eaddr[1] = net_addr[2];
sp->eaddr[2] = net_addr[4];
sp->eaddr[3] = net_addr[6];
sp->eaddr[4] = net_addr[8];
sp->eaddr[5] = net_addr[10];
}
/*
* assign_driver - Assign driver to card.
* First, see if an existing driver is already setup.
*/
struct card_config *
assign_driver(struct card *cp)
{
struct driver *drvp;
struct card_config *conf;
for (conf = cp->config; conf; conf = conf->next)
if (conf->inuse == 0 && conf->driver->card == cp &&
conf->driver->config == conf)
{
#ifdef DEBUG
fprintf(stderr, "Found existing driver (%s) for %s\n",
conf->driver->name, cp->manuf);
#endif /* DEBUG */
return(conf);
}
/*
* New driver must be allocated. Find one that matches the
* any configurations not in use.
*/
for (conf = cp->config; conf; conf = conf->next)
if (conf->inuse == 0 && conf->driver->card == 0)
break;
if (conf == 0)
{
log_1s("No free configuration for card %s", cp->manuf);
return(0);
}
/*
* Now we have a free driver and a matching configuration.
* Before assigning and allocating everything, check to
* see if a device class can be allocated to this.
*/
drvp = conf->driver;
/*
* If none available, then we can't use this card.
*/
if (drvp->inuse)
{
log_1s("Driver already being used for %s", cp->manuf);
return(0);
}
#if 0
/*
* Allocate I/O, memory and IRQ resources.
*/
for (ap = drvp->io; ap; ap = ap->next)
{
if (ap->addr == 0 && ap->size)
{
int i = bit_fns(io_avail, IOPORTS, ap->size);
if (i < 0)
{
log_1s("Failed to allocate I/O ports for %s\n",
cp->manuf);
return(0);
}
ap->addr = i;
bit_nclear(io_avail, i, ap->size);
}
}
if (drvp->irq == 0)
{
int i;
for (i = 1; i < 16; i++)
if (pool_irq[i])
{
drvp->irq = i;
pool_irq[i] = 0;
break;
}
if (drvp->irq == 0)
{
log_1s("Failed to allocate IRQ for %s\n", cp->manuf);
return(0);
}
}
for (ap = drvp->mem; ap; ap = ap->next)
{
if (ap->addr == 0 && ap->size)
{
ap->addr = alloc_memory(ap->size);
if (ap->addr == 0)
{
log_1s("Failed to allocate memory for %s\n",
cp->manuf);
return(0);
}
}
}
#endif /* 0 */
drvp->card = cp;
drvp->config = conf;
drvp->inuse = 1;
conf->inuse = 1;
return(conf);
}
/*
* assign_io - Allocate resources to slot matching the
* configuration index selected.
*/
int
assign_io(struct slot *sp)
{
struct cis *cis;
struct cis_config *cisconf, *defconf;
cis = sp->cis;
defconf = cis->def_config;
for (cisconf = cis->conf; cisconf; cisconf = cisconf->next)
if (cisconf->id == sp->config->index)
break;
if (cisconf == 0)
return(-1);
sp->card_config = cisconf;
/*
* Found a matching configuration. Now look at the I/O, memory and IRQ
* to create the desired parameters. Look at memory first.
*/
if (cisconf->memspace || (defconf && defconf->memspace))
{
struct cis_memblk *mp;
mp = cisconf->mem;
if (!cisconf->memspace)
mp = defconf->mem;
sp->mem.size = mp->length;
sp->mem.cardaddr = mp->address;
/*
* For now, we allocate our own memory from the pool.
*/
sp->mem.addr = sp->config->driver->mem;
/*
* Host memory address is required. Allocate one
* from our pool.
*/
if (sp->mem.size && sp->mem.addr == 0)
{
sp->mem.addr = alloc_memory(mp->length);
if (sp->mem.addr == 0)
return(-1);
sp->config->driver->mem = sp->mem.addr;
}
#ifdef DEBUG
fprintf(stderr, "Using mem addr 0x%x, size %d, card addr 0x%x\n",
sp->mem.addr, sp->mem.cardaddr, sp->mem.size);
#endif /* DEBUG */
}
/*
* Now look at I/O.
*/
bzero(&sp->io, sizeof(sp->io));
if (cisconf->iospace || (defconf && defconf->iospace))
{
struct cis_config *cp;
cp = cisconf;
if (!cisconf->iospace)
cp = defconf;
/*
* If # of I/O lines decoded == 10, then card does its
* own decoding.
*/
/*
* If an I/O block exists, then use it.
* If no address (but a length) is available, allocate
* from the pool.
*/
if (cp->io)
{
sp->io.addr = cp->io->addr;
sp->io.size = cp->io->size;
}
/*
* No I/O block, assume the address lines decode gives the size.
*/
else
sp->io.size = 1 << cp->io_addr;
if (sp->io.addr == 0)
{
int i = bit_fns(io_avail, IOPORTS, sp->io.size);
if (i < 0)
return(-1);
sp->io.addr = i;
}
bit_nclear(io_avail, sp->io.addr, sp->io.size);
/*
* Set up the size to take into account the decode lines.
*/
sp->io.cardaddr = cp->io_addr;
switch(cp->io_bus)
{
case 0:
break;
case 1:
sp->io.flags = IODF_WS;
break;
case 2:
sp->io.flags = IODF_WS|IODF_CS16;
break;
case 3:
sp->io.flags = IODF_WS|IODF_CS16|IODF_16BIT;
break;
}
#ifdef DEBUG
fprintf(stderr, "Using I/O addr 0x%x, size %d\n",
sp->io.addr, sp->io.size);
#endif /* DEBUG */
}
sp->irq = sp->config->irq;
return(0);
}
/*
* setup_slot - Allocate the I/O and memory contexts
* return true if completed OK.
*/
int
setup_slot(struct slot *sp)
{
struct mem_desc mem;
struct io_desc io;
struct drv_desc drv;
struct allocblk *ap;
struct driver *drvp = sp->config->driver;
char c;
off_t offs;
int rw_flags;
offs = sp->cis->reg_addr;
rw_flags = MDF_ATTR;
ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
lseek(sp->fd, offs, SEEK_SET);
c = 0x80;
write(sp->fd, &c, sizeof(c));
usleep(sp->card->reset_time*1000);
lseek(sp->fd, offs, SEEK_SET);
c = 0x00;
write(sp->fd, &c, sizeof(c));
usleep(sp->card->reset_time*1000);
lseek(sp->fd, offs, SEEK_SET);
c = sp->config->index;
write(sp->fd, &c, sizeof(c));
#ifdef DEBUG
printf("Setting config reg at offs 0x%x to 0x%x\n",
sp->cis->reg_addr, c);
printf("Reset time = %d ms\n", sp->card->reset_time);
#endif
usleep(sp->card->reset_time*1000);
/*
* If other config registers exist, set them up.
*/
if (sp->cis->ccrs & 2) /* CCSR */
{
c = 0;
if (sp->cis->def_config && sp->cis->def_config->misc_valid &&
(sp->cis->def_config->misc & 0x8))
c |= 0x08;
if (sp->card_config->io_bus == 1)
c |= 0x20;
lseek(sp->fd, offs+2, SEEK_SET);
write(sp->fd, &c, sizeof(c));
#ifdef DEBUG
printf("Setting CCSR reg to 0x%x\n", c);
#endif
}
mem.window = 0;
if (sp->mem.size)
{
mem.window = 0;
mem.flags = sp->mem.flags;
mem.start = (caddr_t)sp->mem.addr;
mem.card = sp->mem.cardaddr;
mem.size = sp->mem.size;
if (ioctl(sp->fd, PIOCSMEM, &mem))
{
logerr("ioctl (PIOCSMEM)");
return(0);
}
}
io.window = 0;
if (sp->io.size)
{
io.flags = sp->io.flags;
io.start = sp->io.addr;
io.size = sp->io.size;
/*
io.start = sp->io.addr & ~((1 << sp->io.cardaddr)-1);
io.size = 1 << sp->io.cardaddr;
if (io.start < 0x100)
{
io.start = 0x100;
io.size = 0x300;
}
*/
#ifdef DEBUG
printf("Assigning I/O window 0, start 0x%x, size 0x%x\n",
io.start, io.size);
#endif
if (ioctl(sp->fd, PIOCSIO, &io))
{
logerr("ioctl (PIOCSIO)");
return(0);
}
}
strcpy(drv.name, drvp->kernel);
drv.unit = drvp->unit;
drv.irqmask = 1 << sp->irq;
if (sp->mem.size)
{
drv.mem = sp->mem.addr;
drv.memsize = sp->mem.size;
}
else
{
drv.mem = 0;
drv.memsize = 0;
}
if (sp->io.size)
drv.iobase = sp->io.addr;
else
drv.iobase = 0;
#ifdef DEBUG
fprintf(stderr, "Assign %s%d, io 0x%x, mem 0x%x, %d bytes, irq %x\n",
drv.name, drv.unit, drv.iobase, drv.mem, drv.memsize, drv.irqmask);
#endif /* DEBUG */
/*
* If the driver fails to be connected to the device,
* then it may mean that the driver did not recognise it.
*/
if (ioctl(sp->fd, PIOCSDRV, &drv))
{
#ifdef DEBUG
perror(sp->card->manuf);
#endif
log_1s("driver allocation failed for %s", sp->card->manuf);
return(0);
}
return(1);
}

View file

@ -0,0 +1,129 @@
/*
* Common include file for PCMCIA daemon
*/
#include <bitstring.h>
#include <pccard/card.h>
#include <pccard/cis.h>
#include "readcis.h"
struct cmd
{
struct cmd *next;
char *line; /* Command line */
int macro; /* Contains macros */
};
struct card_config
{
struct card_config *next;
unsigned char index;
struct driver *driver;
int irq;
int flags;
char inuse;
};
struct card
{
struct card *next;
char *manuf;
char *version;
int ether; /* For net cards, ether at offset */
int reset_time; /* Reset time */
struct card_config *config; /* List of configs */
struct cmd *insert; /* Insert commands */
struct cmd *remove; /* Remove commands */
};
struct driver
{
struct driver *next;
char *name;
char *kernel; /* Kernel driver base name */
int unit; /* Unit of driver */
/*
* The rest of the structure is allocated dynamically.
* Once allocated, it stays allocated.
*/
struct card *card; /* Current card, if any */
struct card_config *config; /* Config back ptr */
/* struct device *device;*/ /* System device info */
unsigned int mem; /* Allocated host address (if any) */
int inuse;
};
#if 0
struct device
{
struct device *next; /* List of devices */
int inuse; /* Driver being used */
struct cmd *insert; /* Insert commands */
struct cmd *remove; /* Remove commands */
};
#endif
/*
* Defines one allocation block i.e a starting address
* and size. Used for either memory or I/O ports
*/
struct allocblk
{
struct allocblk *next;
int addr; /* Address */
int size; /* Size */
int flags; /* Flags for block */
int cardaddr; /* Card address */
};
/*
* Slot structure - data held for each slot.
*/
struct slot
{
struct slot *next;
int fd;
int mask;
int slot;
char *name;
enum cardstate state;
struct cis *cis;
struct card *card; /* Current card */
struct card_config *config; /* Current configuration */
struct cis_config *card_config;
char devname[16];
unsigned char eaddr[6]; /* If any */
struct allocblk io; /* I/O block spec */
struct allocblk mem; /* Memory block spec */
int irq; /* Irq value */
};
struct slot *slots, *current_slot;
struct allocblk *pool_ioblks; /* I/O blocks in the pool */
struct allocblk *pool_mem; /* Memory in the pool */
int pool_irq[16]; /* IRQ allocations */
struct driver *drivers; /* List of drivers */
struct card *cards;
/*struct device *devlist; */
bitstr_t *mem_avail;
bitstr_t *io_avail;
int verbose, do_log;
char *newstr();
void die(char *);
void *xmalloc(int);
void log_1s(char *, char *);
void logerr(char *);
void reset_slot(struct slot *);
void execute(struct cmd *);
unsigned long alloc_memory(int size);
#define IOPORTS 0x400
#define MEMUNIT 0x1000
#define MEMSTART 0xA0000
#define MEMEND 0x100000
#define MEMBLKS ((MEMEND-MEMSTART)/MEMUNIT)
#define MEM2BIT(x) (((x)-MEMSTART)/MEMUNIT)
#define BIT2MEM(x) (((x)*MEMUNIT)+MEMSTART)

View file

@ -0,0 +1,873 @@
/*
* Decode pcmciad file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "cardd.h"
static FILE *in;
static int pushc, pusht;
static int lineno;
static char *filename;
static char *keys[] =
{
"io", /* 1 */
"irq", /* 2 */
"memory", /* 3 */
"card", /* 4 */
"device", /* 5 */
"config", /* 6 */
"__EOF__", /* 7 */
"reset", /* 8 */
"ether", /* 9 */
"insert", /* 10 */
"remove", /* 11 */
"iosize", /* 12 */
"memsize", /* 13 */
0
};
struct flags
{
char *name;
int mask;
};
void parsefile();
char *token();
char *getline();
char *next_tok();
int num_tok();
void error(char *);
int keyword(char *);
struct allocblk *ioblk_tok(int);
struct allocblk *memblk_tok(int);
int irq_tok(int);
void setflags(struct flags *, int *);
struct driver *new_driver(char *);
/*
* Read a file and parse the pcmcia configuration data.
* After parsing, verify the links.
*/
readfile(char *name)
{
struct card *cp;
struct driver *drvp;
in = fopen(name, "r");
if (in == 0)
{
perror(name);
exit(1);
}
parsefile();
for (cp = cards; cp; cp = cp->next)
{
if (cp->config == 0)
fprintf(stderr, "warning: card %s(%s) has no valid configuration\n",
cp->manuf, cp->version);
}
}
void
parsefile()
{
int i;
char *s;
struct allocblk *bp;
pushc = 0;
lineno = 1;
for(;;)
switch(keyword(next_tok()))
{
default:
error("Syntax error");
pusht = 0;
break;
case 7:
return;
/*
* reserved I/O blocks
*/
case 1:
while (bp = ioblk_tok(0))
{
if (bp->size == 0 || bp->addr == 0)
{
free(bp);
continue;
}
bit_nset(io_avail, bp->addr, bp->addr+bp->size-1);
bp->next = pool_ioblks;
pool_ioblks = bp;
}
pusht = 1;
break;
/*
* reserved irqs
*/
case 2:
while ((i = irq_tok(0)) > 0)
pool_irq[i] = 1;
pusht = 1;
break;
/*
* reserved memory blocks.
*/
case 3:
while (bp = memblk_tok(0))
{
if (bp->size == 0 || bp->addr == 0)
{
free(bp);
continue;
}
bit_nset(mem_avail, MEM2BIT(bp->addr),
MEM2BIT(bp->addr+bp->size)-1);
bp->next = pool_mem;
pool_mem = bp;
}
pusht = 1;
break;
/*
* Card definition.
*/
case 4:
parse_card();
break;
/*
* Device description
*/
#if 0
case 5:
parse_device();
break;
#endif
}
}
/*
* Parse a card definition.
*/
parse_card()
{
char *man, *vers;
struct card *cp;
int i;
struct card_config *confp, *lastp;
man = newstr(next_tok());
vers = newstr(next_tok());
cp = xmalloc(sizeof(*cp));
cp->manuf = man;
cp->version = vers;
cp->reset_time = 50;
cp->next = cards;
cards = cp;
for (;;)
{
switch(keyword(next_tok()))
{
default:
pusht = 1;
return;
case 8:
i = num_tok();
if (i == -1)
{
error("Illegal card reset time");
break;
}
cp->reset_time = i;
break;
case 6:
i = num_tok();
if (i == -1)
{
error("Illegal card config index");
break;
}
confp = xmalloc(sizeof(*confp));
man = next_tok();
confp->driver = new_driver(man);
confp->irq = num_tok();
confp->flags = num_tok();
if (confp->flags == -1)
{
pusht = 1;
confp->flags = 0;
}
if (confp->irq < 0 || confp->irq > 15)
{
error("Illegal card IRQ value");
break;
}
confp->index = i & 0x3F;
/*
* If no valid driver for this config, then do not save
* this configuration entry.
*/
if (confp->driver)
{
if (cp->config == 0)
cp->config = confp;
else
{
for (lastp = cp->config; lastp->next;
lastp = lastp->next)
;
lastp->next = confp;
}
}
else
free(confp);
break;
case 9:
cp->ether = num_tok();
if (cp->ether == -1)
{
error("Illegal ether address offset");
cp->ether = 0;
}
break;
case 10:
addcmd(&cp->insert);
break;
case 11:
addcmd(&cp->remove);
break;
}
}
}
/*
* Generate a new driver structure. If one exists, use
* that one after confirming the correct class.
*/
struct driver *
new_driver(char *name)
{
struct driver *drvp;
char *p;
for (drvp = drivers; drvp; drvp = drvp->next)
if (strcmp(drvp->name, name)==0)
return(drvp);
drvp = xmalloc(sizeof(*drvp));
drvp->next = drivers;
drivers = drvp;
drvp->name = newstr(name);
drvp->kernel = newstr(name);
p = drvp->kernel;
while (*p++)
if (*p >= '0' && *p <= '9')
{
drvp->unit = atoi(p);
*p = 0;
break;
}
#ifdef DEBUG
if (verbose)
printf("Drv %s%d created\n", drvp->kernel, drvp->unit);
#endif
return(drvp);
}
#if 0
/*
* Parse the device description.
*/
parse_device()
{
enum drvclass type = drvclass_tok();
struct device *dp;
static struct device *lastp;
if (type == drv_none)
{
error("Unknown driver class");
return;
}
dp = xmalloc(sizeof(*dp));
dp->type = type;
if (devlist == 0)
devlist = dp;
else
lastp->next = dp;
lastp = dp;
for (;;)
switch(keyword(next_tok()))
{
default:
pusht = 1;
return;
case 10:
addcmd(&dp->insert);
break;
case 11:
addcmd(&dp->remove);
break;
}
}
/*
* Parse the driver description.
*/
parse_driver()
{
char *name, *dev, *p;
struct driver *dp;
static struct driver *lastp;
int i;
struct allocblk *bp;
static struct flags io_flags[] =
{
{ "ws", 0x01 },
{ "16bit", 0x02 },
{ "cs16", 0x04 },
{ "zerows", 0x08 },
{ 0, 0 }
};
static struct flags mem_flags[] =
{
{ "16bit", 0x01 },
{ "zerows", 0x02 },
{ "ws0", 0x04 },
{ "ws1", 0x08 },
{ 0, 0 }
};
name = newstr(next_tok());
dev = newstr(next_tok());
type = drvclass_tok();
if (type == drv_none)
{
error("Unknown driver class");
return;
}
dp = xmalloc(sizeof(*dp));
dp->name = name;
dp->kernel = dev;
dp->type = type;
dp->unit = -1;
dp->irq = -1;
/*
* Check for unit number in driver name.
*/
p = dev;
while (*p++)
if (*p >= '0' && *p <= '9')
{
dp->unit = atoi(p);
*p = 0;
break;
}
if (dp->unit < 0)
error("Illegal kernel driver unit");
/*
* Place at end of list.
*/
if (lastp == 0)
drivers = dp;
else
lastp->next = dp;
lastp = dp;
for (;;)
switch(keyword(next_tok()))
{
default:
pusht = 1;
return;
case 1:
bp = ioblk_tok(1);
if (bp)
{
setflags(io_flags, &bp->flags);
if (dp->io)
{
error("Duplicate I/O spec");
free(bp);
}
else
{
bit_nclear(io_avail, bp->addr,
bp->addr+bp->size-1);
dp->io = bp;
}
}
break;
case 2:
dp->irq = irq_tok(1);
if (dp->irq > 0)
pool_irq[i] = 0;
break;
case 3:
bp = memblk_tok(1);
if (bp)
{
setflags(mem_flags, &bp->flags);
if (dp->mem)
{
error("Duplicate memory spec");
free(bp);
}
else
{
bit_nclear(mem_avail,
MEM2BIT(bp->addr),
MEM2BIT(bp->addr+bp->size)-1);
dp->mem = bp;
}
}
break;
case 10:
addcmd(&dp->insert);
break;
case 11:
addcmd(&dp->remove);
break;
/*
* iosize - Don't allocate an I/O port, but specify
* a size for the range of ports. The actual port number
* will be allocated dynamically.
*/
case 12:
i = num_tok();
if (i <= 0 || i > 128)
error("Illegal iosize");
else
{
int flags = 0;
setflags(io_flags, &flags);
if (dp->io)
error("Duplicate I/O spec");
else
{
dp->io = xmalloc(sizeof(*dp->io));
dp->io->flags = flags;
dp->io->size = i;
}
}
break;
case 13:
i = num_tok();
if (i <= 0 || i > 256*1024)
error("Illegal memsize");
else
{
int flags = 0;
setflags(mem_flags, &flags);
if (dp->mem)
error("Duplicate memory spec");
else
{
dp->mem = xmalloc(sizeof(*dp->mem));
dp->mem->flags = flags;
dp->mem->size = i;
}
}
break;
}
}
/*
* drvclass_tok - next token is expected to
* be a driver class.
*/
enum drvclass
drvclass_tok()
{
char *s = next_tok();
if (strcmp(s, "tty")==0)
return(drv_tty);
else if (strcmp(s, "net")==0)
return(drv_net);
else if (strcmp(s, "bio")==0)
return(drv_bio);
else if (strcmp(s, "null")==0)
return(drv_null);
return(drv_none);
}
#endif /* 0 */
/*
* Parse one I/O block.
*/
struct allocblk *
ioblk_tok(int force)
{
struct allocblk *io;
int i, j;
if ((i = num_tok()) >= 0)
{
if (strcmp("-", next_tok()) || (j = num_tok()) < 0 || j < i)
{
error("I/O block format error");
return(0);
}
io = xmalloc(sizeof(*io));
io->addr = i;
io->size = j - i + 1;
if (j > IOPORTS)
{
error("I/O port out of range");
if (force)
{
free(io);
io = 0;
}
else
io->addr = io->size = 0;
}
return(io);
}
if (force)
error("Illegal or missing I/O block spec");
return(0);
}
/*
* Parse a memory block.
*/
struct allocblk *
memblk_tok(int force)
{
struct allocblk *mem;
int i, j;
if ((i = num_tok()) >= 0)
if ((j = num_tok()) < 0)
error("Illegal memory block");
else
{
mem = xmalloc(sizeof(*mem));
mem->addr = i & ~(MEMUNIT-1);
mem->size = (j + MEMUNIT - 1) & ~(MEMUNIT-1);
if (i < MEMSTART || (i + j) > MEMEND)
{
error("Memory address out of range");
if (force)
{
free(mem);
mem = 0;
}
else
mem->addr = mem->size = 0;
}
return(mem);
}
if (force)
error("Illegal or missing memory block spec");
return(0);
}
/*
* IRQ token. Must be number > 0 && < 16.
* If force is set, IRQ must exist, and can also be '?'.
*/
int
irq_tok(int force)
{
int i;
if (strcmp("?", next_tok())==0 && force)
return(0);
pusht = 1;
i = num_tok();
if (i > 0 && i < 16)
return(i);
if (force)
error("Illegal IRQ value");
return(-1);
}
/*
* search the table for a match.
*/
int
keyword(char *str)
{
char **s;
int i = 1;
for (s = keys; *s; s++, i++)
if (strcmp(*s, str)==0)
return(i);
return(0);
}
/*
* Set/clear flags
*/
void
setflags(struct flags *flags, int *value)
{
char *s;
struct flags *fp;
int set = 1;
do {
s = next_tok();
if (*s == '!')
{
s++;
set = 0;
}
for (fp = flags; fp->name; fp++)
if (strcmp(s, fp->name)==0)
{
if (set)
*value |= fp->mask;
else
*value &= ~fp->mask;
break;
}
} while (fp->name);
pusht = 1;
}
/*
* addcmd - Append the command line to the list of
* commands.
*/
addcmd(struct cmd **cp)
{
char *s = getline();
struct cmd *ncp;
if (*s)
{
ncp = xmalloc(sizeof(*ncp));
ncp->line = s;
while (*cp)
cp = &(*cp)->next;
*cp = ncp;
}
}
void
error(char *msg)
{
pusht = 1;
fprintf(stderr, "%s: %s at line %d, near %s\n",
filename, msg, lineno, next_tok());
pusht = 1;
}
int last_char;
int
get()
{
int c;
if (pushc)
c = pushc;
else
c = getc(in);
pushc = 0;
while (c == '\\')
{
c = getc(in);
switch(c)
{
case '#':
return(last_char = c);
case '\n':
lineno++;
c = getc(in);
continue;
}
pushc = c;
return('\\');
}
if (c == '\n')
lineno++;
if (c == '#')
{
while (get() != '\n')
;
return(last_char = '\n');
}
return(last_char = c);
}
/*
* num_tok - expecting a number token. If not a number,
* return -1.
* Handles octal (who uses octal anymore?)
* hex
* decimal
* Looks for a 'k' at the end of decimal numbers
* and multiplies by 1024.
*/
int
num_tok()
{
char *s = next_tok(), c;
int val=0, base, term=0;
base = 10;
c = *s++;
if (c == '0')
{
base = 8;
c = *s++;
if (c == 'x' || c == 'X')
{
c = *s++;
base = 16;
}
}
do {
switch(c)
{
case 'k':
case 'K':
if (val && base == 10 && *s == 0)
return(val * 1024);
return(-1);
default:
return(-1);
case '0': case '1':
case '2': case '3':
case '4': case '5':
case '6': case '7':
val = val * base + c - '0';
break;
case '8': case '9':
if (base == 8)
return(-1);
else
val = val * base + c - '0';
break;
case 'a': case 'b':
case 'c': case 'd':
case 'e': case 'f':
if (base == 16)
val = val * base + c - 'a' + 10;
else
return(-1);
break;
case 'A': case 'B':
case 'C': case 'D':
case 'E': case 'F':
if (base == 16)
val = val * base + c - 'A' + 10;
else
return(-1);
break;
}
} while (c = *s++);
return(val);
}
char *_next_tok();
char *
next_tok()
{
char *s = _next_tok();
#if 0
printf("Tok = %s\n", s);
#endif
return(s);
}
/*
* get one token. Handles string quoting etc.
*/
char *
_next_tok()
{
static char buf[1024];
char *p = buf, instr = 0;
int c;
if (pusht)
{
pusht = 0;
return(buf);
}
for(;;)
{
c = get();
switch(c)
{
default:
*p++ = c;
break;
case '"':
if (instr)
{
*p++ = 0;
return(buf);
}
instr = 1;
break;
case '\n':
if (instr)
{
error("Unterminated string");
break;
}
/*
* Eat whitespace unless in a string.
*/
case ' ':
case '\t':
if (!instr)
{
if (p!=buf)
{
*p++ = 0;
return(buf);
}
}
else
*p++ = c;
break;
/*
* Special characters that must be tokens on their own.
*/
case '-':
case '?':
case '*':
if (instr)
*p++ = c;
else
{
if (p != buf)
pushc = c;
else
*p++ = c;
*p++ = 0;
return(buf);
}
break;
case EOF:
if (p != buf)
{
*p++ = 0;
return(buf);
}
strcpy(buf, "__EOF__");
return(buf);
}
}
}
/*
* get the rest of the line. If the
* last character scanned was a newline, then
* return an empty line. If this isn't checked, then
* a getline may incorrectly return the next line.
*/
char *
getline()
{
char buf[1024], *p = buf;
int c, i = 0;
if (last_char == '\n')
return(newstr(""));
do {
c = get();
} while (c == ' ' || c == '\t');
for (;c != '\n' && c != EOF; c = get())
if (i++ < sizeof(buf)-10)
*p++ = c;
*p = 0;
return(newstr(buf));
}

View file

@ -0,0 +1,183 @@
.\" Copyright (c) 1994 Andrew McRae
.\" All rights reserved.
.\"
.Dd Novemeber 2, 1994
.Dt CARD.CONF 5
.Os FreeBSD
.Sh NAME
.Nm card.conf
.Nd
.Xr cardd 8
configuration file
.Sh DESCRIPTION
The
.Nm card.conf
file is the configuration file for the
.Xr cardd 8
PC-CARD slot management daemon.
It provides information to allow card
identification, and the matching of drivers (along
with driver resources) to the PC-CARD cards.
.Pp
There are four basic elements within the configuration file;
An optional
.Em "resource pool"
preceding the other sections,
and one or more
.Em "card identifiers" ,
and
.Em "device instances" .
The latter two may appear in any order, and may be
interspersed as desired.
.Pp
Each PC-CARD card contains configuration tuples that provide
the manufacturer and card version; these are used
to identify the card specification in the configuration
file, and from this find a driver that can be used to
interface to the particular card. There is a many-to-one mapping
between cards to drivers i.e a single driver may interface to
multiple types of cards. To aid this, card parameters may be
specified separately from the driver to initialise the card or
extract (in the case of a network card) an ethernet address.
.Pp
Once a driver is allocated to a card, it stays
allocated to that particular card.
However, multiple instances of the same type of driver can be
configured, so that if two cards are plugged in that map to a
similar type of driver, other driver instances of the same name
can be configured.
.Pp
The
.Em insert
and
.Em remove
commands allow a shell command line to be executed.
The command to be executed is the rest of the line after
the keyword. The line can be continued using a backslash.
A simple
macro substitution allows the current kernel device name
.Em ( $device )
and
network card ethernet address
.Em ( $ether )
to be inserted into the command line.
.Xr Pcmciad 8
uses the
.Xr system 3
subroutine to execute the command line.
.Pp
Numeric values may be expressed as octal, hex or decimal.
If a decimal number has
.Em k
or
.Em K
appended to it, the value is multiplied by 1024. Names may be
quoted using double quotes if spaces are required.
A hash character comments out the rest of the line.
.Ss "Resource pool"
The (optional) section specifies a pool of system resources
such as ISA bus memory address space, Input/Output ports and
interrupt request numbers. This resource pool is used
to allocate address space and interrupt numbers dynamically
according to the requirements specified in each driver
description.
.Pp
The syntax of the resources is as follows:
.Pp
.Dl io Ar start - end ...
.Dl memory Ar address size ...
.Dl irq Ar irq-number ...
.Pp
Each of the statements define I/O, memory or IRQ
blocks that can be used to allocate to drivers when
they are initialised.
.Pp
Multiple lines of any of the above statements may be
present to allow separate blocks of each resource to be
defined.
.Ss "Card Identifiers"
The syntax for card identifiers is:
.Pp
.Dl card Ar manufacturer version class
.Dl config Ar index driver interrupt [ flags ]
.Dl ether Ar offset
.Dl insert Ar command
.Dl remove Ar command
.Pp
The first line is mandatory;
the latter statements are optional and can appear in
any order. There may be multiple
.Em config
lines.
The
.Em card
parameters are the Manufacturer name and card version that
is used to match the values from the card's CIS memory. The
.Em class
identifies the card as one of the device classes listed below.
The
.Em config
parameters select the particular card's configuration index
from the range available in the card's CIS, the driver that
is to be associated with this configuration, and the interrupt
level (if any) to be assigned. An optional set of flags may
be assigned.
.Pp
The optional
.Em ether
keyword is used when network cards have their physical Ethernet address
located within the attribute memory of the card. The parameter of this
statement indicates the offset within the attribute memory of the
Ethernet address. This value can be used within insert/remove
commands using the
.Em $ether
macro.
.Pp
The
.Em insert
and
.Em remove
sections allow shell commands to be specified that are executed
when the card is inserted or removed. Multiple
.Em insert
and
.Em remove
commands are allowed, and they are executed in the order they
are listed.
.Sh EXAMPLE
A typical configuration file may appear thus:
.Bd -literal
#
# Sample configuration file.
#
# Pool parameters.
#
io 0x280 - 0x2F0 0x300 - 0x360
irq 5 6 8 9 10 15
memory 0xd4000 96k
memory 0xc4000 32k
#
# Card database.
#
card "RPTI LTD." "EP400" # NE2000 clone
ether 0x110
config 0x21 "ed0" 5
insert ifconfig $device physical $ether
insert ifconfig $device bean
remove ifconfig $device down
card "XYZZY" "FAX/1.0" tty
config 0x30 "sio1" 11
insert echo start getty
remove echo stop getty
.Ed
.Sh FILES
.Bl -tag -width /etc/card.conf -compact
.It Pa /etc/card.conf
The
.Xr cardd 8
configuration file.
.El
.Sh SEE ALSO
.Xr cardd 8

View file

@ -0,0 +1,133 @@
.\" Copyright (c) 1994 Andrew McRae
.\" All rights reserved.
.\"
.Dd November 1, 1994
.Dt PC-CARD 8
.Os FreeBSD
.Sh NAME
.Nm cardd
.Nd PC-CARD (PCMCIA) management daemon
.Sh SYNOPSIS
.Nm cardd
.Op Fl d
.Op Fl v
.Op Fl f Ar configfile
.Sh DESCRIPTION
.Nm Cardd
is normally started at boot time, and manages the insertion
and removal of PC-CARD cards.
.Pp
When started,
.Nm cardd
will read the configuration file (default name
.Pa /etc/card.conf )
and scans the available PC-CARD slots for cards.
.Nm Cardd
then waits for
.Em "card events" ,
such as the insertion of a new card or the removal
of a card.
.Pp
When a card is inserted, the following
actions are taken:
.Bl -enum
.It
The kernel driver detects the card insertion and applies
power to the card.
.It
.Nm Cardd
reads the
.Em CIS
data from the attribute memory of the card, and uses
the manufacturer name and card version to match
the card description in the configuration file.
.It
Once matched, a driver is allocated.
.It
Once a free driver and device instance is located,
.Nm cardd
will (if required) allocate resources such as an ISA memory
block and Input/Output ports from a common pool.
.It
The PC-CARD slot is configured with the I/O and memory
contexts allocated, and the kernel driver is attached to
this card.
.It
If the attach succeeds, then specific shell commands
may be executed to configure the device, such as
.Xr ifconfig 8
to set up a network interface. Separate commands may be specified
for each card, driver or device, and are executed in that order.
.El
.Pp
When
.Nm cardd
detects that a card has been removed, the following sequence occurs:
.Bl -enum
.It
The shell commands associated with card removal are executed. These
are intended to reset any device associated with the removed card.
Separate commands may exist for card, driver and device instances.
.It
The PC-CARD slot resources are freed.
.El
.Pp
Once a card/driver instance is configured, the resources
bound to that instance are remembered, and if the card is removed
and reinserted, the same driver is allocated. The primary reason
is that once a driver is associated with a card, the
driver's
.Fn probe
routine has been called, and this usually causes driver specific
data areas to be initialised with the I/O ports or memory resources
allocated to the card. Most drivers are not designed to be
disassociated from the hardware and then reassociated with different
parameters. This will change significantly when loadable kernel
modules are supported.
.Pp
The start options understood by
.Nm cardd
are:
.Bl -tag -width Ds
.It Fl d
Do not run as a daemon, but run in the foreground and
display error messages.
.It Fl v
After reading the configuration file, print out a summary
of it.
.It Fl f Ar configfile
Specifies a different configuration file to be used
in placed of the default file
.Pa /etc/card.conf.
The file format is detailed in
.Xr card.conf 5 ,
and lists the PC-CARD cards recognized by
.Nm cardd ,
and the kernel drivers and devices that are used to
interface to the card.
.Pp
.Sh FILES
.Bl -tag -width /etc/card.conf -compact
.It Pa /etc/card.conf
.El
.Sh SEE ALSO
.Xr card.conf 5
.Xr ifconfig 8
.Sh AUTHOR
Developed by Andrew McRae (andrew@mega.com.au).
.Sh BUGS
.Nm Cardd
can set up card parameters, but cannot guarantee that
particular drivers can work with the card.
.Pp
Since
.Nm FreeBSD
does not currently support loadable kernel modules, any
.Em irq
specifications in the configuration file must match the
.Nm config
entry for the kernel.
.Pp
Removing cards may cause problems if system resources
have been associated with the card, such as network
mounted filesystems.

View file

@ -0,0 +1,633 @@
/* set tab=4
* Read/dump CIS tuples.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pccard/card.h>
#include <pccard/cis.h>
#include "readcis.h"
static int read_attr(int fd, char *bp, int len);
struct tuple_list *read_one_tuplelist(int, int, off_t);
int ck_linktarget(int, off_t, int);
struct tuple_info tuple_info[] =
{
"Null tuple", 0x00, 0,
"Common memory descriptor", 0x01, 255,
"Checksum", 0x10, 5,
"Long link to attribute memory", 0x11, 4,
"Long link to common memory", 0x12, 4,
"Link target", 0x13, 3,
"No link", 0x14, 0,
"Version 1 info", 0x15, 255,
"Alternate language string", 0x16, 255,
"Attribute memory descriptor", 0x17, 255,
"JEDEC descr for common memory", 0x18, 255,
"JEDEC descr for attribute memory", 0x19, 255,
"Configuration map", 0x1A, 255,
"Configuration entry", 0x1B, 255,
"Other conditions for common memory", 0x1C, 255,
"Other conditions for attribute memory", 0x1D, 255,
"Geometry info for common memory", 0x1E, 255,
"Geometry info for attribute memory", 0x1F, 255,
"Manufacturer ID", 0x20, 4,
"Functional ID", 0x21, 255,
"Functional EXT", 0x22, 255,
"Software interleave", 0x23, 2,
"Version 2 Info", 0x40, 255,
"Data format", 0x41, 255,
"Geometry", 0x42, 4,
"Byte order", 0x43, 2,
"Card init date", 0x44, 4,
"Battery replacement", 0x45, 4,
"Organisation", 0x46, 255,
"Terminator", 0xFF, 255,
0, 0, 0
};
/*
* After reading the tuples, decode the relevant ones.
*/
struct cis *
readcis(int fd)
{
struct tuple_list *tl;
struct tuple *tp;
struct cis *cp;
cp = xmalloc(sizeof(*cp));
cp->tlist = read_tuples(fd);
if (cp->tlist == 0)
return(NULL);
for (tl = cp->tlist; tl; tl = tl->next)
for (tp = tl->tuples; tp; tp = tp->next)
{
#if 0
printf("tuple code = 0x%02x, data is\n", tp->code);
dump(tp->data, tp->length);
#endif
switch(tp->code)
{
case CIS_MEM_COMMON: /* 0x01 */
device_desc(tp->data, tp->length, &cp->common_mem);
break;
case CIS_INFO_V1: /* 0x15 */
cis_info(cp, tp->data, tp->length);
break;
case CIS_MEM_ATTR: /* 0x17 */
device_desc(tp->data, tp->length, &cp->attr_mem);
break;
case CIS_CONF_MAP: /* 0x1A */
config_map(cp, tp->data, tp->length);
break;
case CIS_CONFIG: /* 0x1B */
cis_config(cp, tp->data, tp->length);
break;
}
}
return(cp);
}
/*
* free_cis - delete cis entry.
*/
void
freecis(struct cis *cp)
{
struct cis_ioblk *io;
struct cis_memblk *mem;
struct cis_config *conf;
struct tuple *tp;
struct tuple_list *tl;
while (tl = cp->tlist)
{
cp->tlist = tl->next;
while (tp = tl->tuples)
{
tl->tuples = tp->next;
if (tp->data)
free(tp->data);
}
}
while (conf = cp->conf)
{
cp->conf = conf->next;
while (io = conf->io)
{
conf->io = io->next;
free(io);
}
while (mem = conf->mem)
{
conf->mem = mem->next;
free(mem);
}
free(conf);
}
free(cp);
}
/*
* Fills in CIS version data.
*/
cis_info(struct cis *cp, unsigned char *p, int len)
{
cp->maj_v = *p++;
cp->min_v = *p++;
strncpy(cp->manuf, p, MAXSTR-1);
while (*p++)
;
strncpy(cp->vers, p, MAXSTR-1);
while (*p++)
;
strncpy(cp->add_info1, p, MAXSTR-1);
while (*p++)
;
strncpy(cp->add_info2, p, MAXSTR-1);
}
/*
* device_desc - decode device descriptor.
*/
device_desc(p, len, dp)
unsigned char *p;
int len;
struct dev_mem *dp;
{
while (len > 0 && *p != 0xFF)
{
dp->valid = 1;
dp->type = (*p & 0xF0) >> 4;
dp->wps = !!(*p & 0x8);
dp->speed = *p & 7;
p++;
if (*p != 0xFF)
{
dp->addr = *p >> 3;
dp->units = *p & 7;
}
p++;
len -= 2;
}
}
/*
* configuration map of card control register.
*/
config_map(cp, p, len)
struct cis *cp;
unsigned char *p;
int len;
{
unsigned char *p1;
int i;
union {
unsigned long l;
unsigned char b[4];
}u;
p1 = p + 1;
cp->last_config = *p1++ & 0x3F;
u.l = 0;
for (i = 0 ; i <= (*p & 3); i++)
u.b[i] = *p1++;
cp->reg_addr = u.l;
cp->ccrs = *p1;
}
/*
* CIS config entry - Decode and build configuration entry.
*/
cis_config(cp, p, len)
struct cis *cp;
unsigned char *p;
int len;
{
int blks, x;
int i, j;
union {
unsigned long l;
unsigned char b[4];
}u;
struct cis_config *conf, *last;
struct cis_memblk *mem;
unsigned char feat;
conf = xmalloc(sizeof(*conf));
if (last = cp->conf)
{
while (last->next)
last = last->next;
last->next = conf;
}
else
cp->conf = conf;
conf->id = *p & 0x3F;
if (*p & 0x40)
cp->def_config = conf;
if (*p++ & 0x80)
p++;
feat = *p++;
for (i = 0; i < CIS_FEAT_POWER(feat); i++)
{
unsigned char parms = *p++;
conf->pwr = 1;
for (j = 0; j < 8; j++)
if (parms & (1 << j))
while (*p++ & 0x80)
;
}
if (feat & CIS_FEAT_TIMING)
{
conf->timing = 1;
i = *p++;
if (CIS_WAIT_SCALE(i) != 3)
p++;
if (CIS_READY_SCALE(i) != 7)
p++;
if (CIS_RESERVED_SCALE(i) != 7)
p++;
}
if (feat & CIS_FEAT_I_O)
{
conf->iospace = 1;
if (CIS_IO_RANGE & *p)
conf->io_blks = CIS_IO_BLKS(p[1])+1;
conf->io_addr = CIS_IO_ADDR(*p);
conf->io_bus = (*p >> 5) & 3;
if (*p++ & CIS_IO_RANGE)
{
struct cis_ioblk *io, *last = 0;
i = CIS_IO_ADSZ(*p);
j = CIS_IO_BLKSZ(*p++);
for (x = 0; x < conf->io_blks; x++)
{
io = xmalloc(sizeof(*io));
if (last)
last->next = io;
else
conf->io = io;
last = io;
u.l = 0;
switch(i)
{
case 0:
break;
case 1:
u.b[0] = *p++;
break;
case 2:
u.b[0] = *p++;
u.b[1] = *p++;
break;
case 3:
u.b[0] = *p++;
u.b[1] = *p++;
u.b[2] = *p++;
u.b[3] = *p++;
break;
}
io->addr = u.l;
u.l = 0;
switch(j)
{
case 0:
break;
case 1:
u.b[0] = *p++;
u.l++;
break;
case 2:
u.b[0] = *p++;
u.b[1] = *p++;
u.l++;
break;
case 3:
u.b[0] = *p++;
u.b[1] = *p++;
u.b[2] = *p++;
u.b[3] = *p++;
u.l++;
break;
}
io->size = u.l;
}
}
}
if (feat & CIS_FEAT_IRQ)
{
conf->irq = 1;
conf->irqlevel = *p & 0xF;
conf->irq_flags = *p & 0xF0;
if (*p++ & CIS_IRQ_MASK)
{
conf->irq_mask = (p[1] << 8) | p[0];
p += 2;
}
}
switch(CIS_FEAT_MEMORY(feat))
{
case 0:
break;
case 1:
conf->memspace = 1;
conf->mem = xmalloc(sizeof(*conf->mem));
conf->mem->length = ((p[1] << 8) | p[0])<<8;
break;
case 2:
conf->memspace = 1;
conf->mem = xmalloc(sizeof(*conf->mem));
conf->mem->length = ((p[1] << 8) | p[0]) << 8;
conf->mem->address = ((p[3] << 8) | p[2]) << 8;
break;
case 3:
conf->memspace = 1;
x = *p++;
conf->memwins = CIS_MEM_WINS(x);
for (i = 0; i < conf->memwins; i++)
{
struct cis_memblk *last;
mem = xmalloc(sizeof(*mem));
if (i == 0)
conf->mem = mem;
else
last->next = mem;
last = mem;
u.l = 0;
for (j = 0 ; j < CIS_MEM_LENSZ(x); j++)
u.b[j] = *p++;
mem->length = u.l << 8;
u.l = 0;
for (j = 0 ; j < CIS_MEM_ADDRSZ(x); j++)
u.b[j] = *p++;
mem->address = u.l << 8;
if (x & CIS_MEM_HOST)
{
u.l = 0;
for (j = 0 ; j < CIS_MEM_ADDRSZ(x); j++)
u.b[j] = *p++;
mem->host_address = u.l << 8;
}
}
break;
}
if (feat & 0x80)
{
conf->misc_valid = 1;
conf->misc = *p++;
}
}
/*
* Read the tuples from the card.
* The processing of tuples is as follows:
* - Read tuples at attribute memory, offset 0.
* - If a CIS_END is the first tuple, look for
* a tuple list at common memory offset 0; this list
* must start with a LINKTARGET.
* - If a long link tuple was encountered, execute the long
* link.
* - If a no-link tuple was seen, terminate processing.
* - If no no-link tuple exists, and no long link tuple
* exists while processing the primary tuple list,
* then look for a LINKTARGET tuple in common memory.
* - If a long link tuple is found in any list, then process
* it. Only one link is allowed per list.
*/
static struct tuple_list *tlist;
struct tuple_list *
read_tuples(int fd)
{
struct tuple_list *tl = 0, *last_tl;
struct tuple *tp;
int flag;
off_t offs;
tlist = 0;
last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t)0);
/*
* Now start processing the links (if any).
*/
do
{
flag = MDF_ATTR;
tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A);
if (tp == 0)
{
flag = 0;
tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C);
}
if (tp && tp->length == 4)
{
offs = tp->data[0] |
(tp->data[1] << 8) |
(tp->data[2] << 16) |
(tp->data[3] << 24);
#ifdef DEBUG
printf("Checking long link at %ld (%s memory)\n",
offs, flag ? "Attribute" : "Common");
#endif
if (ck_linktarget(fd, offs, flag))
{
/*
* If a link was found, then read the tuple list from it.
*/
tl = read_one_tuplelist(fd, flag, offs);
last_tl->next = tl;
last_tl = tl;
}
}
} while (tl);
/*
* If the primary list had no NOLINK tuple, and no LINKTARGET,
* then try to read a tuple list at common memory (offset 0).
*/
if (find_tuple_in_list(tlist, CIS_NOLINK)==0 && tlist->next == 0 &&
ck_linktarget(fd, (off_t)0, 0))
{
#ifdef DEBUG
printf("Reading long link at %ld (%s memory)\n",
offs, flag ? "Attribute" : "Common");
#endif
tlist->next = read_one_tuplelist(fd, 0, (off_t)0);
}
return(tlist);
}
/*
* Read one tuple list from the card.
*/
struct tuple_list *
read_one_tuplelist(int fd, int flags, off_t offs)
{
struct tuple *tp, *last_tp, *first = 0;
struct tuple_list *tl;
struct tuple_info *tinfo;
int i, total = 0;
unsigned char code, length;
/*
* Check to see if this memory has already been scanned.
*/
for (tl = tlist; tl; tl = tl->next)
if (tl->offs == offs && tl->flags == (flags & MDF_ATTR))
return(0);
tl = xmalloc(sizeof(*tl));
tl->offs = offs;
tl->flags = flags & MDF_ATTR;
ioctl(fd, PIOCRWFLAG, &flags);
lseek(fd, offs, SEEK_SET);
do {
if (read_attr(fd, &code, 1) != 1)
{
perror("CIS code read");
break;
}
total++;
if (code == CIS_NULL)
continue;
tp = xmalloc(sizeof(*tp));
tp->code = code;
if (read_attr(fd, &length, 1) != 1)
{
perror("CIS len read");
break;
}
total++;
tp->length = length;
#ifdef DEBUG
fprintf(stderr, "Tuple code = 0x%x, len = %d\n",
code, length);
#endif
if (length == 0xFF)
{
length = tp->length = 0;
code = CIS_END;
}
if (length != 0)
{
total += length;
tp->data = xmalloc(length);
if (read_attr(fd, tp->data, length) != length)
{
perror("CIS read");
break;
}
}
/*
* Check the tuple, and ignore it if it isn't in the table
* or the length is illegal.
*/
tinfo = get_tuple_info(code);
if (tinfo == 0 || (tinfo->length != 255 && tinfo->length != length))
{
printf("code %s ignored\n", tuple_name(code));
tp->code = CIS_NULL;
}
if (tl->tuples==0)
tl->tuples = tp;
else
last_tp->next = tp;
last_tp = tp;
} while (code != CIS_END && total < 1024);
return(tl);
}
/*
* return true if the offset points to a LINKTARGET tuple.
*/
int
ck_linktarget(int fd, off_t offs, int flag)
{
char blk[5];
ioctl(fd, PIOCRWFLAG, &flag);
lseek(fd, offs, SEEK_SET);
if (read_attr(fd, blk, 5) != 5)
return(0);
if (blk[0] == 0x13 &&
blk[1] == 0x3 &&
blk[2] == 'C' &&
blk[3] == 'I' &&
blk[4] == 'S')
return(1);
return(0);
}
/*
* find_tuple - find the indicated tuple in the CIS
*/
struct tuple *
find_tuple(struct cis *sp, unsigned char code)
{
struct tuple_list *tl;
struct tuple *tp;
for (tl = sp->tlist; tl; tl = tl->next)
if (tp = find_tuple_in_list(tl, code))
return(tp);
return(0);
}
/*
* find_tuple_in_list - find a tuple within a
* single tuple list.
*/
struct tuple *
find_tuple_in_list(struct tuple_list *tl, unsigned char code)
{
struct tuple *tp;
for (tp = tl->tuples; tp; tp = tp->next)
if (tp->code == code)
break;
return(tp);
}
static int
read_attr(int fd, char *bp, int len)
{
char blk[1024], *p = blk;
int i,l;
if (len > sizeof(blk)/2)
len = sizeof(blk)/2;
l = i = read(fd, blk, len*2);
if (i <= 0)
{
printf("Read return %d bytes (expected %d)\n", i, len*2);
return(i);
}
while (i > 0)
{
*bp++ = *p++;
p++;
i -= 2;
}
return(l/2);
}
/*
* return table entry for code.
*/
struct tuple_info *
get_tuple_info(unsigned char code)
{
struct tuple_info *tp;
for (tp = tuple_info; tp->name; tp++)
if (tp->code == code)
return(tp);
printf("Code %d not found\n", code);
return(0);
}
char *
tuple_name(unsigned char code)
{
struct tuple_info *tp;
tp = get_tuple_info(code);
if (tp)
return(tp->name);
return("Unknown");
}

View file

@ -0,0 +1,116 @@
#define MAXSTR 20
/*
* Storage of one tuple.
*/
struct tuple
{
struct tuple *next;
unsigned char code;
int length;
unsigned char *data;
};
struct tuple_list
{
struct tuple_list *next;
struct tuple *tuples;
off_t offs;
int flags;
};
struct tuple_info
{
char *name;
unsigned char code;
unsigned char length; /* 255 means variable length */
};
/*
* Memory device descriptor.
*/
struct dev_mem
{
unsigned char valid;
unsigned char type;
unsigned char speed;
unsigned char wps;
unsigned char addr;
unsigned char units;
};
/*
* One I/O structure describing a possible I/O map
* of the card.
*/
struct cis_ioblk
{
struct cis_ioblk *next;
unsigned int addr;
unsigned int size;
};
/*
* A structure storing a memory map for the card.
*/
struct cis_memblk
{
struct cis_memblk *next;
unsigned int address;
unsigned int length;
unsigned int host_address;
};
/*
* One configuration entry for the card.
*/
struct cis_config
{
struct cis_config *next;
unsigned int pwr:1; /* Which values are defined. */
unsigned int timing:1;
unsigned int iospace:1;
unsigned int irq:1;
unsigned int memspace:1;
unsigned int misc_valid:1;
unsigned char id;
unsigned char io_blks;
unsigned char io_addr;
unsigned char io_bus;
struct cis_ioblk *io;
unsigned char irqlevel;
unsigned char irq_flags;
unsigned irq_mask;
unsigned char memwins;
struct cis_memblk *mem;
unsigned char misc;
};
/*
* Structure holding all data retrieved from the
* CIS block on the card.
* The default configuration contains interface defaults
* not listed in each separate configuration.
*/
struct cis
{
struct tuple_list *tlist;
char manuf[MAXSTR];
char vers[MAXSTR];
char add_info1[MAXSTR];
char add_info2[MAXSTR];
unsigned char maj_v, min_v;
unsigned char last_config;
unsigned char ccrs;
unsigned long reg_addr;
struct dev_mem attr_mem;
struct dev_mem common_mem;
struct cis_config *def_config;
struct cis_config *conf;
};
void *xmalloc(int);
struct cis *readcis(int);
void dumpcis(struct cis *);
void freecis(struct cis *);
struct tuple_list *read_tuples(int);
struct tuple *find_tuple(struct cis *, unsigned char);
struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char);
struct tuple_info *get_tuple_info(unsigned char);
char *tuple_name(unsigned char);

View file

@ -0,0 +1,22 @@
#
# Sample configuration file.
#
# Pool parameters.
#
io 0x2F8 - 0x360
irq 5 6 8 9 10 15
memory 0xd4000 96k
#
# Card database.
#
card "RPTI LTD." "EP400" # NE2000 clone
ether 0x110
config 0x30 "ed0" 5
config 0x31 "ed1" 6
insert ifconfig $device physical $ether
insert ifconfig $device bean
card "RIPICAA" "RC144ACL"
config 0x21 "sio1" 10 20
insert echo start getty here
remove echo stop getty

View file

@ -0,0 +1,209 @@
/*
* Utility subroutines.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <syslog.h>
#include <varargs.h>
#include "cardd.h"
void
log_1s(char *msg, char *arg)
{
if (do_log)
syslog(LOG_ERR, msg, arg);
else
{
fprintf(stderr, "cardd: ");
fprintf(stderr, msg, arg);
fprintf(stderr, "\n");
}
}
void
logerr(char *msg)
{
if (do_log)
syslog(LOG_ERR, "%s: %m", msg);
else
perror(msg);
}
/*
* Deliver last will and testament, and die.
*/
void
die(char *msg)
{
if (do_log)
syslog(LOG_CRIT, "fatal error: %s", msg);
else
fprintf(stderr, "cardd fatal error: %s\n", msg);
closelog();
exit(1);
}
void *
xmalloc(int sz)
{
void *p;
p = malloc(sz+8);
if (p)
bzero(p, sz);
else
die("malloc failed");
return(p);
}
char *
newstr(char *p)
{
char *s;
s = strdup(p);
if (s == 0)
die("strdup failed");
return(s);
}
/*
* Find contiguous bit string (all set) of at
* least count number.
*/
int
bit_fns(bitstr_t *nm, int nbits, int count)
{
int i;
int found = 0;
for (i = 0; i < nbits; i++)
if (bit_test(nm, i))
{
if (++found == count)
return(i - count + 1);
}
else
found = 0;
return(-1);
}
/*
* Allocate a block of memory and return the address.
*/
unsigned long
alloc_memory(int size)
{
int i;
i = bit_fns(mem_avail, MEMBLKS, size/MEMUNIT);
if (i < 0)
return(0);
bit_nclear(mem_avail, i, size/MEMUNIT);
return(BIT2MEM(i));
}
/*
* reset_slot - Power has been applied to the card.
* Now reset the card.
*/
void
reset_slot(struct slot *sp)
{
struct card *cp = sp->card;
char c;
off_t offs;
struct mem_desc mem;
struct io_desc io;
int rw_flags;
rw_flags = MDF_ATTR;
ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
#ifdef DEBUG
printf("Resetting card, writing 0x80 to offs 0x%x\n",
sp->cis->reg_addr);
#endif
offs = sp->cis->reg_addr;
lseek(sp->fd, offs, SEEK_SET);
c = 0x80;
write(sp->fd, &c, sizeof(c));
usleep(10*1000);
c = 0;
lseek(sp->fd, offs, SEEK_SET);
write(sp->fd, &c, sizeof(c));
/*
* Reset all the memory and I/O windows.
*/
bzero((caddr_t)&mem, sizeof(mem));
bzero((caddr_t)&io, sizeof(io));
for (mem.window = 0; mem.window < NUM_MEM_WINDOWS; mem.window++)
ioctl(sp->fd, PIOCSMEM, &mem);
for (io.window = 0; io.window < NUM_IO_WINDOWS; io.window++)
ioctl(sp->fd, PIOCSIO, &io);
}
/*
* execute - Execute the command strings.
* For the current slot (if any) perform macro
* substitutions.
*/
void
execute(struct cmd *cmdp)
{
char cmd[1024];
char *p, *cp, *lp;
for (;cmdp; cmdp = cmdp->next)
{
cp = cmd;
lp = cmdp->line;
if (*lp == 0)
continue;
while (p = strchr(lp, '$'))
{
/*
* copy over preceding string.
*/
while (lp != p)
*cp++ = *lp++;
/*
* stringify ethernet address and place here.
*/
if (strncmp(p, "$ether", 6)==0)
{
sprintf(cp, "%x:%x:%x:%x:%x:%x",
current_slot->eaddr[0],
current_slot->eaddr[1],
current_slot->eaddr[2],
current_slot->eaddr[3],
current_slot->eaddr[4],
current_slot->eaddr[5]);
while (*++cp)
;
lp += 6;
}
/*
* replace device name
*/
else if (strncmp(p, "$device", 7)==0)
{
sprintf(cp, "%s%d",
current_slot->config->driver->kernel,
current_slot->config->driver->unit);
while (*cp)
cp++;
lp += 7;
}
/*
* Copy the `$' and rescan.
*/
else
*cp++ = *lp++;
}
/*
* No more replacements. Copy rest of string.
*/
while (*cp++ = *lp++)
;
#ifdef DEBUG
fprintf(stderr, "Executing [%s]\n", cmd);
#endif /* DEBUG */
system(cmd);
}
}