fcb24d01b0
card "MELCO" "LPC2-T" and card "MELCO" "LPC2-TX" by this fix. Reported by: Kitagawa Shoichi <sk@xstar.kiu.ac.jp>, NINOMIYA Hideyuki <nin@shikoku.ne.jp> MFC after: 1 week
996 lines
24 KiB
C
996 lines
24 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[] =
|
|
"$FreeBSD$";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <regex.h>
|
|
#include <machine/resource.h>
|
|
#include <sys/ioctl.h>
|
|
#include "cardd.h"
|
|
|
|
static struct card_config *assign_driver(struct slot *, 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 *);
|
|
static void read_ether_attr2(struct slot *sp);
|
|
|
|
struct slot *slots;
|
|
|
|
/*
|
|
* 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 = ");
|
|
switch (confp->index_type) {
|
|
case DEFAULT_INDEX:
|
|
printf("default");
|
|
break;
|
|
case AUTO_INDEX:
|
|
printf("auto");
|
|
break;
|
|
default:
|
|
printf("0x%x", confp->index);
|
|
break;
|
|
}
|
|
printf(", driver name = %s\n", 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);
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
|
|
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 *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 inactive:
|
|
case noslot:
|
|
/* Debounce potentially incorrectly reported removals */
|
|
if (state.laststate == filled || state.laststate == suspend)
|
|
card_removed(sp);
|
|
break;
|
|
case filled:
|
|
/* KLUDGE: if we were suspended, remove card */
|
|
if (state.laststate == suspend)
|
|
card_removed(sp);
|
|
card_inserted(sp);
|
|
break;
|
|
case suspend:
|
|
/* ignored */
|
|
break;
|
|
}
|
|
sp->state = state.state;
|
|
stat_changed(sp);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
struct allocblk *sio;
|
|
int in_use = 0;
|
|
|
|
if (sp->config && sp->config->driver && sp->card)
|
|
logmsg("%s%d: %s removed.", sp->config->driver->kernel,
|
|
sp->config->driver->unit, sp->card->logstr);
|
|
if (sp->cis)
|
|
freecis(sp->cis);
|
|
if (sp->config) {
|
|
if (sp->config->inuse && sp->config->driver->inuse)
|
|
in_use = 1;
|
|
sp->config->inuse = 0;
|
|
sp->config->driver->inuse = 0;
|
|
}
|
|
if ((cp = sp->card) != 0 && in_use)
|
|
execute(cp->remove, sp);
|
|
sp->cis = 0;
|
|
sp->config = 0;
|
|
/* release io */
|
|
if (sp->flags & IO_ASSIGNED)
|
|
for (sio = &sp->io; sio; sio = sio->next)
|
|
if (sio->addr && sio->size)
|
|
bit_nset(io_avail, sio->addr, sio->addr + sio->size - 1);
|
|
/* release irq */
|
|
if (sp->flags & IRQ_ASSIGNED)
|
|
if (sp->irq >= 1 && sp->irq <= 15)
|
|
pool_irq[sp->irq] = 1;
|
|
}
|
|
|
|
/* CIS string comparison */
|
|
|
|
#define REGCOMP_FLAGS (REG_EXTENDED | REG_NOSUB)
|
|
#define REGEXEC_FLAGS (0)
|
|
|
|
static int
|
|
cis_strcmp(char *db, char *cis)
|
|
{
|
|
int res, err;
|
|
char buf[256];
|
|
regex_t rx;
|
|
char * p;
|
|
size_t n;
|
|
|
|
if (!db || !cis) {
|
|
return -1;
|
|
}
|
|
n = strlen(db);
|
|
if (n > 2 && db[0] == '/' && db[n-1] == '/') {
|
|
/* matching by regex */
|
|
db++;
|
|
} else {
|
|
/* otherwise, matching by strncmp() */
|
|
if (n != strlen(cis)) {
|
|
return -1;
|
|
}
|
|
|
|
return strncmp(db, cis, n);
|
|
}
|
|
p = xmalloc(n);
|
|
strncpy(p + 1, db, n-2);
|
|
*p = '^';
|
|
db = p;
|
|
if ((err = regcomp(&rx, p, REGCOMP_FLAGS))) {
|
|
regerror(err, &rx, buf, sizeof buf);
|
|
logmsg("Warning: REGEX error for\"%s\" -- %s\n", p, buf);
|
|
regfree(&rx);
|
|
free(p);
|
|
return -1;
|
|
}
|
|
res = regexec(&rx, cis, 0, NULL, REGEXEC_FLAGS);
|
|
regfree(&rx);
|
|
free(p);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
int err;
|
|
|
|
usleep(pccard_init_sleep);
|
|
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) {
|
|
switch (cp->deftype) {
|
|
case DT_VERS:
|
|
if (cis_strcmp(cp->manuf, sp->cis->manuf) == 0 &&
|
|
cis_strcmp(cp->version, sp->cis->vers) == 0) {
|
|
if (cp->add_info1 != NULL &&
|
|
cis_strcmp(cp->add_info1, sp->cis->add_info1) != 0) {
|
|
break;
|
|
}
|
|
if (cp->add_info2 != NULL &&
|
|
cis_strcmp(cp->add_info2, sp->cis->add_info2) != 0) {
|
|
break;
|
|
}
|
|
|
|
logmsg("Card \"%s\"(\"%s\") "
|
|
"[%s] [%s] "
|
|
"matched \"%s\" (\"%s\") "
|
|
"[%s] [%s] ",
|
|
sp->cis->manuf, sp->cis->vers,
|
|
sp->cis->add_info1, sp->cis->add_info2,
|
|
cp->manuf, cp->version,
|
|
cp->add_info1, cp->add_info2);
|
|
goto escape;
|
|
}
|
|
break;
|
|
case DT_FUNC:
|
|
if (cp->func_id == sp->cis->func_id1) {
|
|
logmsg("Card \"%s\"(\"%s\") "
|
|
"[%s] [%s] "
|
|
"has function ID %d\n",
|
|
sp->cis->manuf, sp->cis->vers,
|
|
sp->cis->add_info1, sp->cis->add_info2,
|
|
cp->func_id);
|
|
goto escape;
|
|
}
|
|
break;
|
|
default:
|
|
logmsg("Unknown deftype %d\n", cp->deftype);
|
|
die("cardd.c:card_inserted()");
|
|
}
|
|
}
|
|
escape:
|
|
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 (sp->cis->lan_nid && sp->cis->lan_nid[0] == sizeof(sp->eaddr)) {
|
|
bcopy(sp->cis->lan_nid + 1, sp->eaddr, sizeof(sp->eaddr));
|
|
sp->flags |= EADDR_CONFIGED;
|
|
} else {
|
|
bzero(sp->eaddr, sizeof(sp->eaddr));
|
|
}
|
|
|
|
if (cp->ether) {
|
|
struct ether *e = 0;
|
|
e = cp->ether;
|
|
switch (e->type) {
|
|
case ETHTYPE_ATTR2:
|
|
read_ether_attr2(sp);
|
|
break;
|
|
default:
|
|
read_ether(sp);
|
|
break;
|
|
}
|
|
}
|
|
if ((sp->config = assign_driver(sp, cp)) == NULL)
|
|
return;
|
|
if ((err = assign_io(sp))) {
|
|
char *reason;
|
|
|
|
switch (err) {
|
|
case -1:
|
|
reason = "specified CIS was not found";
|
|
break;
|
|
case -2:
|
|
reason = "memory block allocation failed";
|
|
break;
|
|
case -3:
|
|
reason = "I/O block allocation failed";
|
|
break;
|
|
case -4:
|
|
reason = "requires more than one memory window";
|
|
break;
|
|
default:
|
|
reason = "Unknown";
|
|
break;
|
|
}
|
|
logmsg("Resource allocation failure for \"%s\"(\"%s\") "
|
|
"[%s] [%s]; Reason %s\n",
|
|
sp->cis->manuf, sp->cis->vers,
|
|
sp->cis->add_info1, sp->cis->add_info2, reason);
|
|
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];
|
|
int flags = MDF_ATTR; /* attribute memory */
|
|
|
|
ioctl(sp->fd, PIOCRWFLAG, &flags);
|
|
lseek(sp->fd, (off_t)sp->card->ether->value, 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]);
|
|
sp->flags |= EADDR_CONFIGED;
|
|
}
|
|
|
|
/*
|
|
* Megahertz X-Jack Ethernet uses unique way to get/set MAC
|
|
* address of the card.
|
|
*/
|
|
static void
|
|
read_ether_attr2(struct slot *sp)
|
|
{
|
|
int i;
|
|
char *hexaddr;
|
|
|
|
hexaddr = sp->cis->add_info2;
|
|
for (i = 0; i < 6; i++)
|
|
sp->eaddr[i] = 0;
|
|
if (!hexaddr)
|
|
return;
|
|
if (strlen(hexaddr) != 12)
|
|
return;
|
|
for (i = 0; i < 12; i++)
|
|
if (!isxdigit(hexaddr[i]))
|
|
return;
|
|
for (i = 0; i < 6; i++) {
|
|
u_int d;
|
|
char s[3];
|
|
s[0] = hexaddr[i * 2];
|
|
s[1] = hexaddr[i * 2 + 1];
|
|
s[2] = '\0';
|
|
if (!sscanf(s, "%x", &d)) {
|
|
int j;
|
|
for (j = 0; j < 6; j++)
|
|
sp->eaddr[j] = 0;
|
|
return;
|
|
}
|
|
sp->eaddr[i] = (u_char)d;
|
|
}
|
|
sp->flags |= EADDR_CONFIGED;
|
|
}
|
|
|
|
|
|
/*
|
|
* assign_driver - Assign driver to card.
|
|
* First, see if an existing driver is already setup.
|
|
*/
|
|
static struct card_config *
|
|
assign_driver(struct slot *sp, struct card *cp)
|
|
{
|
|
struct driver *drvp;
|
|
struct card_config *conf;
|
|
struct pccard_resource res;
|
|
int i;
|
|
int irqmin, irqmax;
|
|
|
|
for (conf = cp->config; conf; conf = conf->next)
|
|
if (conf->inuse == 0 && conf->driver->card == cp &&
|
|
conf->driver->config == conf &&
|
|
conf->driver->inuse == 0) {
|
|
if (debug_level > 0) {
|
|
logmsg("Found existing driver (%s) for %s\n",
|
|
conf->driver->name, cp->manuf);
|
|
}
|
|
conf->driver->inuse = 1;
|
|
conf->inuse = 1;
|
|
return (conf);
|
|
}
|
|
/*
|
|
* New driver must be allocated. Find the first configuration
|
|
* not in use.
|
|
*/
|
|
for (conf = cp->config; conf; conf = conf->next)
|
|
if (conf->inuse == 0 && conf->driver->inuse/*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. When we're
|
|
* sharing interrupts (cardbus bridge case), then we'll use what
|
|
* the kernel tells us to use, reguardless of what the user
|
|
* configured. Asking the kernel for IRQ 0 is our way of asking
|
|
* if we should use a shared interrupt.
|
|
*/
|
|
res.type = SYS_RES_IRQ;
|
|
res.size = 1;
|
|
if (conf->irq == 0) {
|
|
irqmin = 1;
|
|
irqmax = 15;
|
|
} else {
|
|
irqmin = irqmax = conf->irq;
|
|
conf->irq = 0; /* Make sure we get it. */
|
|
}
|
|
res.min = 0;
|
|
res.max = 0;
|
|
if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
|
|
err(1, "ioctl (PIOCSRESOURCE)");
|
|
if (res.resource_addr != ~0ul) {
|
|
conf->irq = res.resource_addr;
|
|
pool_irq[conf->irq] = 0;
|
|
} else {
|
|
for (i = irqmin; i <= irqmax; i++) {
|
|
/*
|
|
* Skip irqs not in the pool.
|
|
*/
|
|
if (pool_irq[i] == 0)
|
|
continue;
|
|
/*
|
|
* -I forces us to use the interrupt, so use it.
|
|
*/
|
|
if (!use_kern_irq) {
|
|
conf->irq = i;
|
|
pool_irq[i] = 0;
|
|
break;
|
|
}
|
|
/*
|
|
* Ask the kernel if we have an free irq.
|
|
*/
|
|
res.min = i;
|
|
res.max = i;
|
|
if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
|
|
err(1, "ioctl (PIOCSRESOURCE)");
|
|
if (res.resource_addr == ~0ul)
|
|
continue;
|
|
/*
|
|
* res.resource_addr might be the kernel's
|
|
* better idea than i, so we have to check to
|
|
* see if that's in use too. If not, mark it
|
|
* in use and break out of the loop. I'm not
|
|
* sure this can happen when IRQ 0 above fails,
|
|
* but the test is cheap enough.
|
|
*/
|
|
if (pool_irq[res.resource_addr] == 0)
|
|
continue;
|
|
conf->irq = res.resource_addr;
|
|
pool_irq[conf->irq] = 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);
|
|
}
|
|
|
|
/*
|
|
* Auto select config index
|
|
*/
|
|
static struct cis_config *
|
|
assign_card_index(struct slot *sp, struct cis * cis)
|
|
{
|
|
struct cis_config *cp;
|
|
struct cis_ioblk *cio;
|
|
struct pccard_resource res;
|
|
int i;
|
|
|
|
res.type = SYS_RES_IOPORT;
|
|
for (cp = cis->conf; cp; cp = cp->next) {
|
|
if (!cp->iospace || !cp->io)
|
|
continue;
|
|
for (cio = cp->io; cio; cio = cio->next) {
|
|
res.size = cio->size;
|
|
res.min = cio->addr;
|
|
res.max = res.min + cio->size - 1;
|
|
if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
|
|
err(1, "ioctl (PIOCSRESOURCE)");
|
|
if (res.resource_addr != cio->addr)
|
|
goto next;
|
|
for (i = cio->addr; i < cio->addr + cio->size - 1; i++)
|
|
if (!bit_test(io_avail, i))
|
|
goto next;
|
|
}
|
|
return cp; /* found */
|
|
next:
|
|
}
|
|
return cis->def_config;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
switch (sp->config->index_type) {
|
|
case DEFAULT_INDEX: /* default */
|
|
cisconf = defconf;
|
|
sp->config->index = cisconf->id;
|
|
break;
|
|
case AUTO_INDEX: /* auto */
|
|
cisconf = assign_card_index(sp, cis);
|
|
if (cisconf)
|
|
sp->config->index = cisconf->id;
|
|
break;
|
|
default: /* normal, use index value */
|
|
for (cisconf = cis->conf; cisconf; cisconf = cisconf->next)
|
|
if (cisconf->id == sp->config->index)
|
|
break;
|
|
}
|
|
|
|
if (cisconf == 0) {
|
|
logmsg("Config id %d not present in this card",
|
|
sp->config->index);
|
|
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.
|
|
*/
|
|
|
|
/* Skip ed cards in PIO mode */
|
|
if ((strncmp(sp->config->driver->name, "ed", 2) == 0) &&
|
|
(sp->config->flags & 0x10))
|
|
goto memskip;
|
|
|
|
if (cisconf->memspace || (defconf && defconf->memspace)) {
|
|
struct cis_memblk *mp;
|
|
|
|
mp = cisconf->mem;
|
|
/*
|
|
* Currently we do not handle the presence of multiple windows.
|
|
* Then again neither does the interface to the kernel!
|
|
* See setup_slot() and readcis.c:cis_conf()
|
|
*/
|
|
if (cisconf->memwins > 1) {
|
|
logmsg("Card requires %d memory windows.",
|
|
cisconf->memwins);
|
|
return (-4);
|
|
}
|
|
|
|
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;
|
|
}
|
|
/* Driver specific set up */
|
|
if (strncmp(sp->config->driver->name, "ed", 2) == 0) {
|
|
sp->mem.cardaddr = 0x4000;
|
|
sp->mem.flags = MDF_ACTIVE | MDF_16BITS;
|
|
} else {
|
|
sp->mem.flags = MDF_ACTIVE | MDF_16BITS;
|
|
}
|
|
|
|
if (sp->mem.flags & MDF_ACTIVE)
|
|
sp->flags |= MEM_ASSIGNED;
|
|
if (debug_level > 0) {
|
|
logmsg("Using mem addr 0x%x, size %d, card addr 0x%x, flags 0x%x\n",
|
|
sp->mem.addr, sp->mem.size, sp->mem.cardaddr,
|
|
sp->mem.flags);
|
|
}
|
|
}
|
|
memskip:
|
|
|
|
/* Now look at I/O. */
|
|
bzero(&sp->io, sizeof(sp->io));
|
|
if (cisconf->iospace || (defconf && defconf->iospace)
|
|
|| sp->card->iosize) {
|
|
struct cis_config *cp;
|
|
struct cis_ioblk *cio;
|
|
struct allocblk *sio;
|
|
int x, xmax;
|
|
int iosize;
|
|
|
|
cp = cisconf;
|
|
if (!cisconf->iospace)
|
|
cp = defconf;
|
|
iosize = sp->card->iosize;
|
|
|
|
/* iosize auto */
|
|
if (iosize < 0) {
|
|
if (cp->io)
|
|
iosize = cp->io->size;
|
|
else
|
|
iosize = 1 << cp->io_addr;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
cio = cp->io;
|
|
sio = &(sp->io);
|
|
xmax = 1;
|
|
if (iosize == 0 && cio)
|
|
xmax = cisconf->io_blks;
|
|
for (x = 0; x < xmax; x++) {
|
|
if (iosize) {
|
|
sio->addr = 0;
|
|
sio->size = iosize;
|
|
} else if (cio) {
|
|
sio->addr = cio->addr;
|
|
sio->size = cio->size;
|
|
} else {
|
|
/*
|
|
* No I/O block, assume the address lines
|
|
* decode gives the size.
|
|
*/
|
|
sio->size = 1 << cp->io_addr;
|
|
}
|
|
if (sio->addr == 0) {
|
|
struct pccard_resource res;
|
|
int i, j;
|
|
|
|
res.type = SYS_RES_IOPORT;
|
|
res.size = sio->size;
|
|
|
|
for (i = 0; i < IOPORTS; i++) {
|
|
j = bit_fns(io_avail, IOPORTS, i,
|
|
sio->size, sio->size);
|
|
if ((j & (sio->size - 1)) != 0)
|
|
continue;
|
|
res.min = j;
|
|
res.max = j + sio->size - 1;
|
|
if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
|
|
err(1, "ioctl (PIOCSRESOURCE)");
|
|
if (res.resource_addr == j)
|
|
break;
|
|
}
|
|
if (j < 0) {
|
|
return (-3);
|
|
} else {
|
|
sio->addr = j;
|
|
}
|
|
}
|
|
bit_nclear(io_avail, sio->addr,
|
|
sio->addr + sio->size - 1);
|
|
sp->flags |= IO_ASSIGNED;
|
|
|
|
/* Set up the size to take into account the decode lines. */
|
|
sio->cardaddr = cp->io_addr;
|
|
switch (cp->io_bus) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
sio->flags = IODF_WS;
|
|
break;
|
|
case 2:
|
|
sio->flags = IODF_WS | IODF_CS16;
|
|
break;
|
|
case 3:
|
|
sio->flags = IODF_WS | IODF_CS16 | IODF_16BIT;
|
|
break;
|
|
}
|
|
if (debug_level > 0) {
|
|
logmsg("Using I/O addr 0x%x, size %d\n",
|
|
sio->addr, sio->size);
|
|
}
|
|
if (cio && cio->next) {
|
|
sio->next = xmalloc(sizeof(*sio));
|
|
sio = sio->next;
|
|
cio = cio->next;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
struct allocblk *sio;
|
|
char *p;
|
|
char c;
|
|
off_t offs;
|
|
int rw_flags;
|
|
int iowin;
|
|
|
|
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);
|
|
#if RESET_MAY_BE_HARMFUL
|
|
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);
|
|
#endif
|
|
lseek(sp->fd, offs, SEEK_SET);
|
|
c = sp->config->index;
|
|
c |= 0x40;
|
|
write(sp->fd, &c, sizeof(c));
|
|
if (debug_level > 0) {
|
|
logmsg("Setting config reg at offs 0x%lx to 0x%x, "
|
|
"Reset time = %d ms\n", (unsigned long)offs, c,
|
|
sp->card->reset_time);
|
|
}
|
|
usleep(pccard_init_sleep);
|
|
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;
|
|
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);
|
|
}
|
|
}
|
|
if (sp->flags & IO_ASSIGNED) {
|
|
for (iowin = 0, sio = &(sp->io); iowin <= 1; iowin++) {
|
|
io.window = iowin;
|
|
if (sio->size) {
|
|
io.flags = sio->flags;
|
|
io.start = sio->addr;
|
|
io.size = sio->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
|
|
if (debug_level > 0) {
|
|
logmsg("Assigning I/O window %d, start 0x%x, "
|
|
"size 0x%x flags 0x%x\n", io.window, io.start,
|
|
io.size, io.flags);
|
|
}
|
|
io.flags |= IODF_ACTIVE;
|
|
if (ioctl(sp->fd, PIOCSIO, &io)) {
|
|
logerr("ioctl (PIOCSIO)");
|
|
return (0);
|
|
}
|
|
if (ioctl(sp->fd, PIOCGIO, &io))
|
|
{
|
|
logerr("ioctl (PIOCGIO)");
|
|
return(0);
|
|
}
|
|
if (io.start != sio->addr){
|
|
logmsg("I/O base address changed from 0x%x to 0x%x\n",
|
|
sio->addr, io.start);
|
|
sio->addr = io.start;
|
|
}
|
|
if (sio->next)
|
|
sio = sio->next;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
strcpy(drv.name, drvp->kernel);
|
|
drv.unit = drvp->unit;
|
|
drv.irqmask = 1 << sp->irq;
|
|
drv.flags = sp->config->flags;
|
|
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 DEV_DESC_HAS_SIZE
|
|
drv.iosize = sp->io.size;
|
|
#endif
|
|
if (debug_level > 0) {
|
|
logmsg("Assign %s%d, io 0x%x-0x%x, mem 0x%lx, %d bytes, "
|
|
"irq %d, flags %x\n", drv.name, drv.unit, drv.iobase,
|
|
drv.iobase + sp->io.size - 1, drv.mem, drv.memsize,
|
|
sp->irq, drv.flags);
|
|
}
|
|
/*
|
|
* 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(%s): %s",
|
|
sp->card->manuf, sp->card->version, strerror(errno));
|
|
return (0);
|
|
}
|
|
drv.name[sizeof(drv.name) - 1] = '\0';
|
|
if (strncmp(drv.name, drvp->kernel, sizeof(drv.name))) {
|
|
drvp->kernel = newstr(drv.name);
|
|
p = drvp->kernel;
|
|
while (*p++)
|
|
if (*p >= '0' && *p <= '9') {
|
|
drvp->unit = atoi(p);
|
|
*p = '\0';
|
|
break;
|
|
}
|
|
}
|
|
logmsg("%s%d: %s inserted.", sp->config->driver->kernel,
|
|
sp->config->driver->unit, sp->card->logstr);
|
|
return (1);
|
|
}
|