/* * 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.33.2.1 1999/03/03 11:15:33 kuriyama Exp $"; #endif /* not lint */ /* * Code cleanup, bug-fix and extension * by Tatsumi Hosokawa */ #include #include #include #include #include #include #include #include #include #include #include #ifdef WAVELAN #include #endif /* WAVELAN */ #include "cardd.h" #define CARD_INS_SLEEP 3 void dump_config_file(void); void dump_resource(void); void readslots(void); void slot_change(struct slot *); void card_removed(struct slot *); void card_inserted(struct slot *); static struct card_config *assign_driver(struct card *, struct slot *); static int setup_slot(struct slot *); static int assign_io(struct slot *); static void pr_cmd(struct cmd *); static void de_assign_io(struct slot *); static void read_ether(struct slot *, off_t); static void read_ether_attr2hex(struct slot *); #ifdef WAVELAN static void read_ether_wavelan(struct slot *); #endif /* WAVELAN */ /* * Dump configuration file data. */ void dump_config_file(void) { struct card *cp; struct card_config *confp; for (cp = cards; cp; cp = cp->next) { if (cp->oemid == -1) { printf("Card manuf %s, vers %s\n", cp->manuf, cp->version); } else { printf("Card [0x%x] [0x%x] manuf %s, vers %s\n", cp->oemid, cp->pcmciaid, 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); } } fflush(stdout); } void dump_resource(void) { int i; int j; printf("available ioport(kernel):"); for (i = 0; i < IOPORTS; i++) { if (!bit_test(io_kern, i)) continue; for (j = i + 1; j < IOPORTS; j++) { if (!bit_test(io_kern, j)) break; } j--; if (i == j) printf(" %04x", i); else printf(" %04x-%04x", i, j); i = j + 1; /*ugly*/ } printf("\n"); printf("available ioport(pccardd):"); for (i = 0; i < IOPORTS; i++) { if (!bit_test(io_avail, i)) continue; for (j = i + 1; j < IOPORTS; j++) { if (!bit_test(io_avail, j)) break; } j--; if (i == j) printf(" %04x", i); else printf(" %04x-%04x", i, j); i = j + 1; /*ugly*/ } printf("\n"); printf("available iomem(kernel):"); for (i = MEMSTART; i < MEMEND; i += MEMUNIT) { if (!bit_test(mem_kern, MEM2BIT(i))) continue; for (j = i + MEMUNIT; j < MEMEND; j++) { if (!bit_test(mem_kern, MEM2BIT(j))) break; } j -= MEMUNIT; if (i == j) printf(" %04x-%04x", i, i + MEMUNIT - 1); else printf(" %04x-%04x", i, j + MEMUNIT - 1); i = j + MEMUNIT; /*ugly*/ } printf("\n"); printf("available iomem(pccardd):"); for (i = MEMSTART; i < MEMEND; i += MEMUNIT) { if (!bit_test(mem_avail, MEM2BIT(i))) continue; for (j = i + MEMUNIT; j < MEMEND; j++) { if (!bit_test(mem_avail, MEM2BIT(j))) break; } j -= MEMUNIT; if (i == j) printf(" %04x-%04x", i, i + MEMUNIT - 1); else printf(" %04x-%04x", i, j + MEMUNIT - 1); i = j + MEMUNIT; /*ugly*/ } printf("\n"); 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. */ void readslots(void) { char name[128]; int i, fd; struct slot *sp; 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; sp->state = empty; /* 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); } } /* * slot_change - Card status has changed. * read new state and process. */ void slot_change(struct slot *sp) { struct slotstate state; enum cardstate laststate; current_slot = sp; if (ioctl(sp->fd, PIOCGSTATE, &state)) { logerr("ioctl (PIOCGSTATE)"); return; } laststate = sp->state; if (state.state == sp->state) return; sp->state = state.state; switch (sp->state) { case empty: case inactive: case noslot: /* Debounce potentially incorrectly reported removals */ if (laststate == filled || laststate == suspend) card_removed(sp); break; case filled: card_inserted(sp); break; case suspend: /* ignored */ break; } #ifdef SERVER stat_changed(sp); #endif /* SERVER */ } /* * 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; int in_use = 0; de_assign_io(sp); 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->cis = 0; sp->config = 0; #if FreeBSD_current /* release io */ bit_nset(io_avail, sp->io.addr, sp->io.size); #endif /* FreeBSD_current */ if (verbose) dump_resource(); } /* regex CIS string comparison (hosokawa) */ #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); p = xmalloc(n + 2); strcpy(p + 1, db); *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; int ether_mac_err; sp->flags &= ~(CFLAGS); /* things to be config'd ... */ sp->flags &= ~(AFLAGS); /* things to be assigned ... */ sleep(CARD_INS_SLEEP); sp->cis = readcis(sp->fd); if (sp->cis == 0) { logmsg("Error reading CIS on %s\n", sp->name); return; } #ifdef DEBUG dumpcis(sp->cis); #endif for (cp = cards; cp; cp = cp->next) { /* XXX - There's gotta be a better way */ switch (cp->deftype) { case DT_VERS: if (cp->oemid != -1 && (cp->oemid != sp->cis->oemid || cp->pcmciaid != sp->cis->pcmciaid)) { continue; } if (cis_strcmp(cp->manuf, sp->cis->manuf) == 0 && cis_strcmp(cp->version, sp->cis->vers) == 0) { if ( ( (cp->add_info1 && cis_strcmp(cp->add_info1, sp->cis->add_info1) == 0) || cp->add_info1 == NULL) && ( (cp->add_info2 && cis_strcmp(cp->add_info2, sp->cis->add_info2) == 0) || cp->add_info2 == NULL)) { logmsg("Card [0x%x] [0x%x] \"%s\"(\"%s\") " "[%s] [%s] ", sp->cis->oemid, sp->cis->pcmciaid, sp->cis->manuf, sp->cis->vers, sp->cis->add_info1, sp->cis->add_info2); if (cp->oemid == -1) { logmsg("matched \"%s\" (\"%s\") " "[%s] [%s]\n", cp->manuf, cp->version, cp->add_info1, cp->add_info2); } else { logmsg("matched [0x%x] [0x%x] \"%s\" (\"%s\") " "[%s] [%s]\n", cp->oemid, cp->pcmciaid, 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 [0x%x] [0x%x] \"%s\"(\"%s\") " "[%s] [%s] " "has function ID %d\n", sp->cis->oemid, sp->cis->pcmciaid, 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 [0x%x] [0x%x] \"%s\"(\"%s\") " "[%s] [%s]", sp->cis->oemid, sp->cis->pcmciaid, sp->cis->manuf, sp->cis->vers, sp->cis->add_info1, sp->cis->add_info2); return; } ether_mac_err = 0; 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)); #ifdef WAVELAN bzero(&sp->wl, sizeof(sp->wl)); #endif /* WAVELAN */ if (cp->ether) { struct ether *e = 0; if (!cp->ether->ether_mac) { logmsg("No MAC address definition for \"%s\"(\"%s\") " "[%s] [%s]", cp->manuf, cp->version, cp->add_info1, cp->add_info2); e = cp->ether; switch (e->type) { case ETHTYPE_ATTR2HEX: read_ether_attr2hex(sp); break; #ifdef WAVELAN case ETHTYPE_WAVELAN: read_ether_wavelan(sp); break; #endif /* WAVELAN */ default: read_ether(sp, (off_t)e->attr); break; } } else { for (e = cp->ether; e; e = e->next) { struct ether_mac *em; switch (e->type) { case ETHTYPE_ATTR2HEX: read_ether_attr2hex(sp); break; #ifdef WAVELAN case ETHTYPE_WAVELAN: read_ether_wavelan(sp); break; #endif /* WAVELAN */ default: read_ether(sp, (off_t)e->attr); break; } for (em = e->ether_mac; em; em = em->next) { int i; for (i = 0; i < 3; i++) if (em->addr[i] != sp->eaddr[i]) goto ether_unmatched; goto ether_matched; ether_unmatched: } } if (!e) { logmsg("Invalid MAC address. " "Check \"ether\" field of \"%s\"(\"%s\") " "[%s] [%s]", cp->manuf, cp->version, cp->add_info1, cp->add_info2); ether_mac_err = 1; } } ether_matched: } sp->config = assign_driver(cp, sp); if (sp->config == 0 || ether_mac_err) { #if 0 execute(cp->insert); #endif 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 = "IRQ allocation failed"; 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); de_assign_io(sp); 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); else de_assign_io(sp); if (verbose) dump_resource(); } /* * 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, off_t offset) { unsigned char net_addr[12]; int rw_flags; rw_flags = MDF_ATTR; ioctl(sp->fd, PIOCRWFLAG, &rw_flags); lseek(sp->fd, offset, 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_attr2hex(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++) { unsigned 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] = (unsigned char)d; } sp->flags |= EADDR_CONFIGED; } #ifdef WAVELAN /* * AT&T (NCR) Wavelan support */ static void read_ether_wavelan(struct slot *sp) { int i; int rw_flags; int netw_addr_disp; int mac_select; off_t offset; unsigned char buf[0x40 * 2]; offset = DEFAULT_PSA_OFFSET; rw_flags = MDF_ATTR; ioctl(sp->fd, PIOCRWFLAG, &rw_flags); lseek(sp->fd, offset, SEEK_SET); if (read(sp->fd, buf, sizeof(buf)) != sizeof(buf)) { logerr("read err on PSA"); return; } sp->wl.wc_confstat = buf[PSA_CONF_STATUS * 2]; sp->wl.wc_nwid[0] = buf[NETW_ID * 2]; sp->wl.wc_nwid[1] = buf[NETW_ID * 2 + 2]; netw_addr_disp = NETW_ADDR; mac_select = ((sp->wl.wc_confstat & 1) && (buf[PSA_MAC_SELECT] & 1)); if (mac_select) netw_addr_disp = NETW_ADDR_LOCAL; for (i = 0; i < 6; i++) sp->wl.wc_macaddr[i] = buf[(netw_addr_disp + i) * 2]; sp->wl.wc_comp_number = buf[PSA_COMP_NUMBER * 2]; sp->wl.wc_subband = buf[PSA_SUBBAND * 2]; sp->wl.wc_netw_id_sel = buf[PSA_NETW_ID_SELECT * 2]; sp->wl.wc_thr_pre_set = buf[PSA_THR_PRE_SET * 2]; sp->wl.wc_quality_thr = buf[PSA_QUALITY_THR * 2]; #ifndef SYSINSTALL { static char types[6][16] = { "PC-AT 915 MHz", "PC-MC 915 MHz", "PC-AT 2.4GHz", "PC-MC 2.4GHz", "PCMCIA 915MHz", "Unknown" }; int type; type = sp->wl.wc_comp_number; if (type < 0 || type > 4) type = 5; logmsg("Wavelan NWID[%02x:%02x] MAC[%02x:%02x:%02x:%02x:%02x:%02x(%s)] %s", sp->wl.wc_nwid[0], sp->wl.wc_nwid[1], sp->wl.wc_macaddr[0], sp->wl.wc_macaddr[1], sp->wl.wc_macaddr[2], sp->wl.wc_macaddr[3], sp->wl.wc_macaddr[4], sp->wl.wc_macaddr[5], (mac_select ? "Local" : "Universal"), types[type]); } #endif /* not SYSINSTALL */ sp->flags |= WL_CONFIGED; } #endif /* WAVELAN */ /* * 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 slot *sp) { 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, sp->cis->manuf); #endif 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 == NULL) { logmsg("No free configuration for card %s", sp->cis->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", sp->cis->manuf); return (NULL); } #if FreeBSD_current /* 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]) { #ifdef SLOT_ALLOC_IRQ /* quick and qirty hack for slot patch by nakagawa */ if (!pool_slots[sp->slot].flag || pool_slots[sp->slot].pool_irq[i]) { #endif /* SLOT_ALLOC_IRQ */ conf->irq = i; pool_irq[i] = 0; break; #ifdef SLOT_ALLOC_IRQ } #endif /* SLOT_ALLOC_IRQ */ } if (conf->irq == 0) { logmsg("Failed to allocate IRQ for %s\n", cp->manuf); return (NULL); } } #endif /* FreeBSD_current */ 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 cis * cis) { struct cis_config *cp; struct cis_ioblk *cio; int i; for (cp = cis->conf; cp; cp = cp->next) { if (!cp->iospace || !cp->io) continue; for (cio = cp->io; cio; cio = cio->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 0: /* use index value */ for (cisconf = cis->conf; cisconf; cisconf = cisconf->next) if (cisconf->id == sp->config->index) break; break; case 1: /* default */ default: cisconf = defconf; break; case 2: /* auto */ cisconf = assign_card_index(cis); break; } if (cisconf == 0) return (-1); sp->card_config = cisconf; sp->config->index = cisconf->id; /* * Found a matching configuration. Now look at the I/O, memory and IRQ * to create the desired parameters. Look at memory first. */ /* * XXX -- dirty hack for some 'ed' cards that have incomplete iomem * CIS tupples. */ if (!(strncmp(sp->config->driver->name, "ed", 2) == 0 && (sp->config->flags & 0x10)) && (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; } #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 sp->mem.flags = MDF_ACTIVE | MDF_16BITS; sp->mem.cardaddr = 0x4000; sp->mem.size = 0x4000; sp->flags |= MEM_ASSIGNED; } if (sp->config->card_mem) { struct card_mem *cm; struct allocblk *m, *n; for (m = sp->mem.next; m;) { n = m->next; free(m); m = n; } sp->mem.next = 0; sp->mem.addr = sp->config->card_mem->addr; sp->mem.cardaddr = sp->config->card_mem->cardaddr; sp->mem.size = sp->config->card_mem->size; sp->mem.flags = sp->config->card_mem->flags; #ifdef DEBUG printf("addr=%x cardaddr=%x size=%x\n", sp->mem.addr, sp->mem.cardaddr, sp->mem.size); #endif n = &sp->mem; for (cm = sp->config->card_mem->next; cm; cm = cm->next) { m = xmalloc(sizeof(*m)); m->addr = cm->addr; m->cardaddr = cm->cardaddr; m->size = cm->size; m->flags = cm->flags; m->next = 0; n->next = m; n = m; } sp->flags |= MEM_ASSIGNED; } /* Now look at I/O. */ bzero(&sp->io, sizeof(sp->io)); if (cisconf->iospace || (defconf && defconf->iospace) || sp->config->card_io) { struct cis_config *cp; struct cis_ioblk *cio; struct allocblk *sio; struct card_io *cdio; int x, xmax; 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 1 cio = cp->io; sio = &(sp->io); cdio = sp->config->card_io; xmax = 1; if (cdio) { struct card_io *c = cdio; for (xmax = 1; c->next; xmax++, c = c->next) continue; } else if (cio) xmax = cisconf->io_blks; for (x = 0; x < xmax; x++) { int i; if (cdio) { sio->addr = cdio->addr; sio->size = cdio->size; if (cdio->addr == -1) { /* wildcard */ i = bit_fns(io_avail, IOPORTS, sio->size, 1 << cp->io_addr); if (i < 0) return (-3); sio->addr = i; } } else if ((cio)&&(cio->addr != 0)) { sio->addr = cio->addr; sio->size = cio->size; for (i = sio->addr; i < sio->addr + sio->size - 1; i++) if (!bit_test(io_kern, i)) return (-3); } else { /* * No I/O block, assume the address * lines decode gives the size. */ #ifdef HSSYNTH if ((strncmp(sp->config->driver->name, "hss", 3) == 0) && cp->io_addr == 8) { cp->io_addr = 4; sio->size = 1 << cp->io_addr; /* 320 30 310 330 340 200 3d0 */ sio->addr = 0x320; } else { sio->size = 1 << cp->io_addr; i = bit_fns(io_avail, IOPORTS, sio->size, sio->size); if (i < 0) return (-3); sio->addr = i; } #else /* HSSYNTH */ sio->size = 1 << cp->io_addr; i = bit_fns(io_avail, IOPORTS, sio->size, sio->size); if (i < 0) return (-3); sio->addr = i; #endif /* HSSYNTH */ } bit_nclear(io_avail, sio->addr, sio->addr + sio->size - 1); bit_nclear(io_kern, sio->addr, sio->addr + sio->size - 1); /* 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; } #ifdef DEBUG logmsg("Using I/O addr 0x%x, size %d\n", sio->addr, sio->size); #endif if (cdio && cdio->next) { sio->next = xmalloc(sizeof(*sio)); sio = sio->next; cdio = cdio->next; } else if (cio && cio->next) { sio->next = xmalloc(sizeof(*sio)); sio = sio->next; cio = cio->next; } sp->flags |= IO_ASSIGNED; } #else if (!cp->io) { int i = bit_fns(io_avail, IOPORTS, sp->io.size); sp->io.size = 1 << cp->io_addr; if (i < 0) return (-3); sp->io.addr = i; bit_nclear(io_avail, sp->io.addr, sp->io.addr + sp->io.size - 1); } else { for (x = 0, cio = cp->io, sio = &(sp->io); x < cisconf->io_blks; x++) { 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 << cio->addr; bit_nclear(io_avail, sio->addr, sio->addr + sio->size - 1); /* * 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: #if 0 sio->flags = IODF_WS; #endif break; case 2: sio->flags = IODF_WS | IODF_CS16; break; case 3: sio->flags = IODF_WS | IODF_CS16 | IODF_16BIT; break; } #ifdef DEBUG logmsg("Using I/O addr 0x%x, size %d\n", sio->addr, sio->size); #endif if (cio->next) { sio->next = xmalloc(sizeof(*sio)); sio = sio->next; cio = cio->next; } } sp->flags |= IO_ASSIGNED; } #endif } sp->irq = sp->config->irq; if (sp->irq == 0) { int i; for (i = 1; i < 16; i++) if (pool_irq[i]) { #ifdef SLOT_ALLOC_IRQ /* quick and qirty hack for slot patch by nakagawa */ if (!pool_slots[sp->slot].flag || pool_slots[sp->slot].pool_irq[i]) { #endif /* SLOT_ALLOC_IRQ */ sp->irq = i; pool_irq[i] = 0; sp->flags |= IRQ_ASSIGNED; break; #ifdef SLOT_ALLOC_IRQ } #endif /* SLOT_ALLOC_IRQ */ } if (sp->irq == 0) return (-4); } if (sp->irq == 16) sp->irq = 0; 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 slotstate state; struct allocblk *sio; struct allocblk *smem; 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 0 lseek(sp->fd, offs, SEEK_SET); c = 0x80; write(sp->fd, &c, sizeof(c)); usleep(sp->card->reset_time * 1000); #endif 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(CARD_INS_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; for (smem = &sp->mem; smem; smem = smem->next) { /* * This allows cardmem directives in /etc/pccard.conf * with addr = 0x0 for cards which can tolerate arbitrary * mappings */ if (!smem->addr) smem->addr = alloc_memory(smem->size); if (smem->addr) { mem.flags = smem->flags; mem.start = (caddr_t)smem->addr; mem.card = smem->cardaddr; mem.size = smem->size; if (ioctl(sp->fd, PIOCSMEM, &mem)) { logerr("ioctl (PIOCSMEM)"); return (0); } mem.window++; } } } #if 0 io.window = 0; #endif /* XXX - is the below line depricated?? */ ioctl(sp->fd, PIOCGSTATE, &state); 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 } #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); } if (ioctl(sp->fd, PIOCGIO, &io)) { logerr("ioctl (PIOCGIO)"); return(0); } if (io.start != sio->addr){ #ifdef DEBUG logmsg("I/O base address changed from 0x%x to 0x%x\n", sio->addr, io.start); #endif sio->addr = io.start; } if (sio->next) sio = sio->next; else break; } } strcpy(drv.name, drvp->kernel); drv.unit = drvp->unit; drv.irqmask = sp->irq ? (1 << sp->irq) : 0; drv.flags = sp->config->flags; /* XXX */ if (strncmp(drvp->name, "sio", 3) == 0) { #if 0 printf("Set Irqmask = 0\n"); drv.irqmask = 0; #endif drv.flags |= 0x80; } if ((sp->flags & MEM_ASSIGNED) && sp->mem.size) { drv.mem = sp->mem.addr; drv.memsize = sp->mem.size; } else { drv.mem = 0; drv.memsize = 0; } if ((sp->flags & IO_ASSIGNED) && sp->io.size) 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 (sp->flags & EADDR_CONFIGED) memcpy(drv.misc, sp->eaddr, 6); #ifdef WAVELAN if (sp->flags & WL_CONFIGED) memcpy(drv.misc, &sp->wl, sizeof(struct wavelan_conf)); #endif /* WAVELAN */ /* * If the driver fails to be connected to the device, then * it may mean that the driver could not recognize the device. */ if (ioctl(sp->fd, PIOCSDRV, &drv)) { logmsg("driver allocation failed for %s (%s)", sp->card->manuf, strerror(errno)); return (0); } return (1); } /* * de_assign_io */ void de_assign_io(struct slot *sp) { struct allocblk *sio; struct allocblk *smem; /* free up any resources assigned */ 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); bit_nset(io_kern, sio->addr, sio->addr + sio->size - 1); } sio->addr = sio->size = 0; } } if (sp->flags & MEM_ASSIGNED) { for (smem = &sp->mem; smem; smem = smem->next) { if (smem->addr && smem->size) bit_nset(mem_avail, MEM2BIT(smem->addr), MEM2BIT(smem->addr + smem->size - 1)); smem->addr = smem->size = 0; } } if (sp->flags & IRQ_ASSIGNED) { if (sp->irq >= 1 && sp->irq <= 15) pool_irq[sp->irq] = 1; } /* turn *everything* off */ sp->flags &= ~(AFLAGS | CFLAGS); }