freebsd-dev/usr.sbin/pccard/pccardd/cardd.c
Tatsumi Hosokawa 35e42bac95 Added "iosize" directive in /etc/pccard.conf
Allocate free I/O window with given size to card.

(example)
# IBM PCMCIA Ethernet I/II
card "IBM Corp." "Ethernet"
	config	0x1 "ed0" ?
	iosize	32
	ether	0xff0

(it's currently only useful for externalizing hacks for broken CIS cards,
 but it will play an important role with "function" directive I'm planninng
 to merge)

Reviewed by:	freebsd-mobile list
Obtained from:	PAO3's "cardio" directive
1999-07-23 01:33:34 +00:00

575 lines
14 KiB
C

/*
* 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.
*/
#ifndef lint
static const char rcsid[] =
"$Id: cardd.c,v 1.34 1999/02/05 16:00:17 kuriyama Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "cardd.h"
static struct card_config *assign_driver(struct card *);
static int assign_io(struct slot *);
static int setup_slot(struct slot *);
static void card_inserted(struct slot *);
static void card_removed(struct slot *);
static void pr_cmd(struct cmd *);
static void read_ether(struct slot *);
/*
* Dump configuration file data.
*/
void
dump_config_file(void)
{
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);
}
}
}
static 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.
*/
struct slot *
readslots(void)
{
char name[128];
int i, fd;
struct slot *slots, *sp;
slots = NULL;
for (i = 0; i < MAXSLOT; i++) {
sprintf(name, CARD_DEVICE, i);
fd = open(name, O_RDWR);
if (fd < 0)
continue;
sp = xmalloc(sizeof(*sp));
sp->fd = fd;
sp->name = newstr(name);
sp->slot = i;
/* Check to see if the controller memory has been set up. */
if (slots == 0) {
unsigned long mem = 0;
if (ioctl(fd, PIOCRWMEM, &mem))
logerr("ioctl (PIOCRWMEM)");
#ifdef DEBUG
logmsg("mem=0x%x\n", mem);
#endif
if (mem == 0) {
mem = alloc_memory(4 * 1024);
if (mem == 0)
die("can't allocate memory for controller access");
if (ioctl(fd, PIOCRWMEM, &mem))
logerr("ioctl (PIOCRWMEM)");
}
}
sp->next = slots;
slots = sp;
slot_change(sp);
}
return (slots);
}
/*
* slot_change - Card status has changed.
* read new state and process.
*/
void
slot_change(struct slot *sp)
{
struct slotstate state;
if (ioctl(sp->fd, PIOCGSTATE, &state)) {
logerr("ioctl (PIOCGSTATE)");
return;
}
switch (state.state) {
case empty:
case noslot:
/* Debounce potentially incorrectly reported removals */
if (state.laststate == filled || state.laststate == suspend)
card_removed(sp);
break;
case filled:
card_inserted(sp);
break;
case suspend:
/* ignored */
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 card *cp;
if (sp->cis)
freecis(sp->cis);
if (sp->config) {
sp->config->inuse = 0;
sp->config->driver->inuse = 0;
}
if ((cp = sp->card) != 0)
execute(cp->remove, sp);
sp->cis = 0;
sp->config = 0;
/* release io */
bit_nset(io_avail, sp->io.addr, sp->io.addr + sp->io.size - 1);
/* release irq */
if (sp->irq)
pool_irq[sp->irq] = 1;
}
/*
* 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;
sleep(5);
sp->cis = readcis(sp->fd);
if (sp->cis == 0) {
logmsg("Error reading CIS on %s\n", sp->name);
return;
}
#if 0
dumpcis(sp->cis);
#endif
for (cp = cards; cp; cp = cp->next)
if (strncmp(cp->manuf, sp->cis->manuf, CIS_MAXSTR) == 0 &&
strncmp(cp->version, sp->cis->vers, CIS_MAXSTR) == 0)
break;
sp->card = cp;
#if 0
reset_slot(sp);
#endif
if (cp == 0) {
logmsg("No card in database for \"%s\"(\"%s\")",
sp->cis->manuf, sp->cis->vers);
return;
}
if (cp->ether)
read_ether(sp);
if ((sp->config = assign_driver(cp)) == NULL)
return;
if (assign_io(sp)) {
logmsg("Resource allocation failure for %s", sp->cis->manuf);
return;
}
/*
*
* Once assigned, set up the I/O & mem contexts, set up the
* windows, and then attach the driver.
*/
if (setup_slot(sp))
execute(cp->insert, sp);
#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.
*/
static void
read_ether(struct slot *sp)
{
unsigned char net_addr[12];
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];
logmsg("Ether=%02x:%02x:%02x:%02x:%02x:%02x\n",
sp->eaddr[0], sp->eaddr[1], sp->eaddr[2],
sp->eaddr[3], sp->eaddr[4], sp->eaddr[5]);
}
/*
* assign_driver - Assign driver to card.
* First, see if an existing driver is already setup.
*/
static 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 &&
conf->driver->inuse == 0) {
#ifdef DEBUG
logmsg("Found existing driver (%s) for %s\n",
conf->driver->name, cp->manuf);
#endif
conf->driver->inuse = 1;
conf->inuse = 1;
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) {
logmsg("No free configuration for card %s", cp->manuf);
return (NULL);
}
/*
* 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) {
logmsg("Driver already being used for %s", cp->manuf);
return (NULL);
}
/* Allocate a free IRQ if none has been specified */
if (conf->irq == 0) {
int i;
for (i = 1; i < 16; i++)
if (pool_irq[i]) {
conf->irq = i;
pool_irq[i] = 0;
break;
}
if (conf->irq == 0) {
logmsg("Failed to allocate IRQ for %s\n", cp->manuf);
return (NULL);
}
}
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.
*/
static 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 (-2);
sp->config->driver->mem = sp->mem.addr;
}
sp->mem.cardaddr = 0x4000;
#ifdef DEBUG
logmsg("Using mem addr 0x%x, size %d, card addr 0x%x\n",
sp->mem.addr, sp->mem.size, sp->mem.cardaddr);
#endif
}
/* Now look at I/O. */
bzero(&sp->io, sizeof(sp->io));
if (cisconf->iospace || (defconf && defconf->iospace)
|| sp->card->iosize) {
struct cis_config *cp;
int iosize;
cp = cisconf;
if (!cisconf->iospace)
cp = defconf;
iosize = sp->card->iosize;
/*
* 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 (iosize) {
sp->io.addr = 0;
sp->io.size = iosize;
}
else if (cp->io) {
sp->io.addr = cp->io->addr;
sp->io.size = cp->io->size;
} else {
/*
* No I/O block, assume the address lines
* decode gives the size.
*/
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.addr + sp->io.size - 1);
sp->flags |= IO_ASSIGNED;
/* 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
logmsg("Using I/O addr 0x%x, size %d\n",
sp->io.addr, sp->io.size);
#endif
}
sp->irq = sp->config->irq;
sp->flags |= IRQ_ASSIGNED;
return (0);
}
/*
* setup_slot - Allocate the I/O and memory contexts
* return true if completed OK.
*/
static int
setup_slot(struct slot *sp)
{
struct mem_desc mem;
struct io_desc io;
struct dev_desc drv;
struct driver *drvp = sp->config->driver;
char c;
off_t offs;
int rw_flags;
memset(&io, 0, sizeof io);
memset(&drv, 0, sizeof drv);
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;
c |= 0x40;
write(sp->fd, &c, sizeof(c));
#ifdef DEBUG
logmsg("Setting config reg at offs 0x%lx to 0x%x, Reset time = %d ms\n",
(unsigned long)offs, c, sp->card->reset_time);
#endif
sleep(5);
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));
}
if (sp->flags & MEM_ASSIGNED) {
mem.window = 0;
mem.flags = sp->mem.flags | MDF_ACTIVE | MDF_16BITS;
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->flags & IO_ASSIGNED) {
io.flags = sp->io.flags;
io.start = sp->io.addr;
io.size = sp->io.size;
#if 0
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;
}
#endif
#ifdef DEBUG
logmsg("Assigning I/O window %d, start 0x%x, size 0x%x flags 0x%x\n",
io.window, io.start, io.size, io.flags);
#endif
io.flags |= IODF_ACTIVE;
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;
drv.flags = 0x80;
if (sp->flags & MEM_ASSIGNED) {
drv.mem = sp->mem.addr;
drv.memsize = sp->mem.size;
} else {
drv.mem = 0;
drv.memsize = 0;
}
if (sp->flags & IO_ASSIGNED)
drv.iobase = sp->io.addr;
else
drv.iobase = 0;
#ifdef DEBUG
logmsg("Assign %s%d, io 0x%x, mem 0x%lx, %d bytes, irq %d, flags %x\n",
drv.name, drv.unit, drv.iobase, drv.mem, drv.memsize, sp->irq, drv.flags);
#endif
/*
* If the driver fails to be connected to the device,
* then it may mean that the driver did not recognise it.
*/
memcpy(drv.misc, sp->eaddr, 6);
if (ioctl(sp->fd, PIOCSDRV, &drv)) {
logmsg("driver allocation failed for %s", sp->card->manuf);
return (0);
}
return (1);
}