pnpparse.c revision 59002
152059Sdfr/*- 252059Sdfr * Copyright (c) 1999 Doug Rabson 352059Sdfr * All rights reserved. 452059Sdfr * 552059Sdfr * Redistribution and use in source and binary forms, with or without 652059Sdfr * modification, are permitted provided that the following conditions 752059Sdfr * are met: 852059Sdfr * 1. Redistributions of source code must retain the above copyright 952059Sdfr * notice, this list of conditions and the following disclaimer. 1052059Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1152059Sdfr * notice, this list of conditions and the following disclaimer in the 1252059Sdfr * documentation and/or other materials provided with the distribution. 1352059Sdfr * 1452059Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1552059Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1652059Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1752059Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1852059Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1952059Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2052059Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2152059Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2252059Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2352059Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2452059Sdfr * SUCH DAMAGE. 2552059Sdfr * 2652059Sdfr * $FreeBSD: head/sys/isa/pnpparse.c 59002 2000-04-04 07:48:04Z dfr $ 2752059Sdfr */ 2852059Sdfr 2952059Sdfr#include <sys/param.h> 3052059Sdfr#include <sys/systm.h> 3152059Sdfr#include <sys/kernel.h> 3258873Sdfr#include <sys/malloc.h> 3352059Sdfr#include <sys/module.h> 3452059Sdfr#include <sys/bus.h> 3552059Sdfr#include <isa/isavar.h> 3652059Sdfr#include <isa/pnpreg.h> 3752059Sdfr#include <isa/pnpvar.h> 3852059Sdfr 3958850Sdfr#define MAXDEP 8 4058850Sdfr 4152241Sdfr#define I16(p) ((p)[0] + ((p)[1] << 8)) 4252241Sdfr#define I32(p) (I16(p) + (I16(p+2) << 16)) 4352241Sdfr 4452059Sdfr/* 4552059Sdfr * Parse resource data for Logical Devices. 4652059Sdfr * 4752059Sdfr * This function exits as soon as it gets an error reading *ANY* 4858850Sdfr * Resource Data or it reaches the end of Resource Data. 4952059Sdfr */ 5052059Sdfrvoid 5152059Sdfrpnp_parse_resources(device_t dev, u_char *resources, int len) 5252059Sdfr{ 5352059Sdfr device_t parent = device_get_parent(dev); 5452059Sdfr u_char tag, *resp, *resinfo; 5552059Sdfr int large_len, scanning = len; 5652241Sdfr u_int32_t id, compat_id; 5752059Sdfr struct isa_config *config; 5858850Sdfr int ncfgs = 1; 5958850Sdfr int priorities[1 + MAXDEP]; 6058873Sdfr struct isa_config *configs; 6152059Sdfr char buf[100]; 6258850Sdfr int i; 6352059Sdfr 6452241Sdfr id = isa_get_logicalid(dev); 6558873Sdfr configs = (struct isa_config *)malloc(sizeof(*configs) * (1 + MAXDEP), 6658873Sdfr M_DEVBUF, M_NOWAIT); 6758873Sdfr if (configs == NULL) { 6858873Sdfr device_printf(dev, "No memory to parse PNP data\n"); 6958873Sdfr return; 7058873Sdfr } 7158873Sdfr bzero(configs, sizeof(*configs) * (1 + MAXDEP)); 7258850Sdfr config = &configs[0]; 7358850Sdfr priorities[0] = 0; 7452059Sdfr resp = resources; 7552059Sdfr while (scanning > 0) { 7652059Sdfr tag = *resp++; 7752059Sdfr scanning--; 7852059Sdfr if (PNP_RES_TYPE(tag) == 0) { 7952059Sdfr /* Small resource */ 8052059Sdfr if (scanning < PNP_SRES_LEN(tag)) { 8152059Sdfr scanning = 0; 8252059Sdfr continue; 8352059Sdfr } 8452059Sdfr resinfo = resp; 8552059Sdfr resp += PNP_SRES_LEN(tag); 8652059Sdfr scanning -= PNP_SRES_LEN(tag);; 8752059Sdfr 8852059Sdfr switch (PNP_SRES_NUM(tag)) { 8952059Sdfr case PNP_TAG_COMPAT_DEVICE: 9052059Sdfr /* 9152059Sdfr * Got a compatible device id 9252059Sdfr * resource. Should keep a list of 9352059Sdfr * compat ids in the device. 9452059Sdfr */ 9552059Sdfr bcopy(resinfo, &compat_id, 4); 9652059Sdfr isa_set_compatid(dev, compat_id); 9752059Sdfr break; 9852059Sdfr 9952059Sdfr case PNP_TAG_IRQ_FORMAT: 10052241Sdfr if (bootverbose) { 10152241Sdfr printf("%s: adding irq mask %#04x\n", 10252241Sdfr pnp_eisaformat(id), 10352241Sdfr I16(resinfo)); 10452241Sdfr } 10552059Sdfr if (config->ic_nirq == ISA_NIRQ) { 10658850Sdfr device_printf(parent, "too many irqs\n"); 10752059Sdfr scanning = 0; 10852059Sdfr break; 10952059Sdfr } 11052059Sdfr config->ic_irqmask[config->ic_nirq] = 11152241Sdfr I16(resinfo); 11252059Sdfr config->ic_nirq++; 11352059Sdfr break; 11452059Sdfr 11552059Sdfr case PNP_TAG_DMA_FORMAT: 11652241Sdfr if (bootverbose) { 11752241Sdfr printf("%s: adding dma mask %#02x\n", 11852241Sdfr pnp_eisaformat(id), 11952241Sdfr resinfo[0]); 12052241Sdfr } 12152059Sdfr if (config->ic_ndrq == ISA_NDRQ) { 12258850Sdfr device_printf(parent, "too many drqs\n"); 12352059Sdfr scanning = 0; 12452059Sdfr break; 12552059Sdfr } 12652059Sdfr config->ic_drqmask[config->ic_ndrq] = 12752059Sdfr resinfo[0]; 12852059Sdfr config->ic_ndrq++; 12952059Sdfr break; 13052059Sdfr 13152059Sdfr case PNP_TAG_START_DEPENDANT: 13252241Sdfr if (bootverbose) { 13352241Sdfr printf("%s: start dependant\n", 13452241Sdfr pnp_eisaformat(id)); 13552241Sdfr } 13659002Sdfr if (ncfgs > MAXDEP) { 13758850Sdfr device_printf(parent, "too many dependant configs (%d)\n", MAXDEP); 13852059Sdfr scanning = 0; 13952059Sdfr break; 14052059Sdfr } 14158850Sdfr config = &configs[ncfgs]; 14252059Sdfr /* 14352059Sdfr * If the priority is not specified, 14452059Sdfr * then use the default of 14552059Sdfr * 'acceptable' 14652059Sdfr */ 14752059Sdfr if (PNP_SRES_LEN(tag) > 0) 14858850Sdfr priorities[ncfgs] = resinfo[0]; 14952059Sdfr else 15058850Sdfr priorities[ncfgs] = 1; 15158850Sdfr ncfgs++; 15252059Sdfr break; 15352059Sdfr 15452059Sdfr case PNP_TAG_END_DEPENDANT: 15552241Sdfr if (bootverbose) { 15652241Sdfr printf("%s: end dependant\n", 15752241Sdfr pnp_eisaformat(id)); 15852241Sdfr } 15958850Sdfr config = &configs[0]; /* back to main config */ 16052059Sdfr break; 16152059Sdfr 16252059Sdfr case PNP_TAG_IO_RANGE: 16352241Sdfr if (bootverbose) { 16452241Sdfr printf("%s: adding io range " 16552241Sdfr "%#x-%#x, size=%#x, " 16652241Sdfr "align=%#x\n", 16752241Sdfr pnp_eisaformat(id), 16852241Sdfr I16(resinfo + 1), 16952241Sdfr I16(resinfo + 3) + resinfo[6]-1, 17052241Sdfr resinfo[6], 17152241Sdfr resinfo[5]); 17252241Sdfr } 17352059Sdfr if (config->ic_nport == ISA_NPORT) { 17458850Sdfr device_printf(parent, "too many ports\n"); 17552059Sdfr scanning = 0; 17652059Sdfr break; 17752059Sdfr } 17852059Sdfr config->ic_port[config->ic_nport].ir_start = 17952241Sdfr I16(resinfo + 1); 18052059Sdfr config->ic_port[config->ic_nport].ir_end = 18152241Sdfr I16(resinfo + 3) + resinfo[6] - 1; 18252241Sdfr config->ic_port[config->ic_nport].ir_size = 18352059Sdfr resinfo[6]; 18452241Sdfr if (resinfo[5] == 0) { 18552241Sdfr /* Make sure align is at least one */ 18652241Sdfr resinfo[5] = 1; 18752241Sdfr } 18852059Sdfr config->ic_port[config->ic_nport].ir_align = 18952059Sdfr resinfo[5]; 19052059Sdfr config->ic_nport++; 19152059Sdfr break; 19252059Sdfr 19352059Sdfr case PNP_TAG_IO_FIXED: 19452241Sdfr if (bootverbose) { 19558850Sdfr printf("%s: adding fixed io range " 19652241Sdfr "%#x-%#x, size=%#x, " 19752241Sdfr "align=%#x\n", 19852241Sdfr pnp_eisaformat(id), 19952241Sdfr I16(resinfo), 20052241Sdfr I16(resinfo) + resinfo[2] - 1, 20152241Sdfr resinfo[2], 20252241Sdfr 1); 20352241Sdfr } 20452059Sdfr if (config->ic_nport == ISA_NPORT) { 20558850Sdfr device_printf(parent, "too many ports\n"); 20652059Sdfr scanning = 0; 20752059Sdfr break; 20852059Sdfr } 20952059Sdfr config->ic_port[config->ic_nport].ir_start = 21052241Sdfr I16(resinfo); 21152059Sdfr config->ic_port[config->ic_nport].ir_end = 21252241Sdfr I16(resinfo) + resinfo[2] - 1; 21352059Sdfr config->ic_port[config->ic_nport].ir_size 21452059Sdfr = resinfo[2]; 21552059Sdfr config->ic_port[config->ic_nport].ir_align = 1; 21652059Sdfr config->ic_nport++; 21752059Sdfr break; 21852059Sdfr 21952241Sdfr case PNP_TAG_END: 22052241Sdfr if (bootverbose) { 22158850Sdfr printf("%s: end config\n", 22252241Sdfr pnp_eisaformat(id)); 22352241Sdfr } 22452241Sdfr scanning = 0; 22552241Sdfr break; 22652241Sdfr 22752059Sdfr default: 22852059Sdfr /* Skip this resource */ 22958850Sdfr device_printf(parent, "unexpected small tag %d\n", 23052059Sdfr PNP_SRES_NUM(tag)); 23152059Sdfr break; 23252059Sdfr } 23352059Sdfr } else { 23452059Sdfr /* Large resource */ 23552059Sdfr if (scanning < 2) { 23652059Sdfr scanning = 0; 23752059Sdfr continue; 23852059Sdfr } 23952241Sdfr large_len = I16(resp); 24052059Sdfr resp += 2; 24152059Sdfr scanning -= 2; 24252059Sdfr 24352059Sdfr if (scanning < large_len) { 24452059Sdfr scanning = 0; 24552059Sdfr continue; 24652059Sdfr } 24752059Sdfr resinfo = resp; 24852059Sdfr resp += large_len; 24952059Sdfr scanning -= large_len; 25052059Sdfr 25152241Sdfr switch (PNP_LRES_NUM(tag)) { 25252241Sdfr case PNP_TAG_ID_ANSI: 25352059Sdfr if (large_len > sizeof(buf) - 1) 25452059Sdfr large_len = sizeof(buf) - 1; 25552059Sdfr bcopy(resinfo, buf, large_len); 25652059Sdfr 25752059Sdfr /* 25858850Sdfr * Trim trailing spaces and garbage. 25952059Sdfr */ 26058850Sdfr while (large_len > 0 && buf[large_len - 1] <= ' ') 26152059Sdfr large_len--; 26252059Sdfr buf[large_len] = '\0'; 26352059Sdfr device_set_desc_copy(dev, buf); 26452241Sdfr break; 26552241Sdfr 26652241Sdfr case PNP_TAG_MEMORY_RANGE: 26752241Sdfr if (bootverbose) { 26858847Sdfr int temp = I16(resinfo + 7) << 8; 26958847Sdfr 27052241Sdfr printf("%s: adding memory range " 27152241Sdfr "%#x-%#x, size=%#x, " 27252241Sdfr "align=%#x\n", 27352241Sdfr pnp_eisaformat(id), 27452241Sdfr I16(resinfo + 1)<<8, 27558847Sdfr (I16(resinfo + 3)<<8) + temp - 1, 27658847Sdfr temp, 27752241Sdfr I16(resinfo + 5)); 27852241Sdfr } 27952059Sdfr 28052241Sdfr if (config->ic_nmem == ISA_NMEM) { 28158873Sdfr device_printf(parent, "too many memory ranges\n"); 28252241Sdfr scanning = 0; 28352241Sdfr break; 28452241Sdfr } 28552059Sdfr 28652241Sdfr config->ic_mem[config->ic_nmem].ir_start = 28752241Sdfr I16(resinfo + 1)<<8; 28852241Sdfr config->ic_mem[config->ic_nmem].ir_end = 28952241Sdfr (I16(resinfo + 3)<<8) 29058847Sdfr + (I16(resinfo + 7) << 8) - 1; 29152241Sdfr config->ic_mem[config->ic_nmem].ir_size = 29258847Sdfr I16(resinfo + 7) << 8; 29352241Sdfr config->ic_mem[config->ic_nmem].ir_align = 29452241Sdfr I16(resinfo + 5); 29552241Sdfr if (!config->ic_mem[config->ic_nmem].ir_align) 29652241Sdfr config->ic_mem[config->ic_nmem] 29752241Sdfr .ir_align = 0x10000; 29852241Sdfr config->ic_nmem++; 29952059Sdfr break; 30052059Sdfr 30152241Sdfr case PNP_TAG_MEMORY32_RANGE: 30252241Sdfr if (bootverbose) { 30358850Sdfr printf("%s: adding memory32 range " 30452241Sdfr "%#x-%#x, size=%#x, " 30552241Sdfr "align=%#x\n", 30652241Sdfr pnp_eisaformat(id), 30752241Sdfr I32(resinfo + 1), 30852241Sdfr I32(resinfo + 5) 30952241Sdfr + I32(resinfo + 13) - 1, 31052241Sdfr I32(resinfo + 13), 31152241Sdfr I32(resinfo + 9)); 31252241Sdfr } 31352241Sdfr 31452241Sdfr if (config->ic_nmem == ISA_NMEM) { 31558850Sdfr device_printf(parent, "too many memory ranges\n"); 31652241Sdfr scanning = 0; 31752241Sdfr break; 31852241Sdfr } 31952241Sdfr 32052241Sdfr config->ic_mem[config->ic_nmem].ir_start = 32152241Sdfr I32(resinfo + 1); 32252241Sdfr config->ic_mem[config->ic_nmem].ir_end = 32352241Sdfr I32(resinfo + 5) 32452241Sdfr + I32(resinfo + 13) - 1; 32552241Sdfr config->ic_mem[config->ic_nmem].ir_size = 32652241Sdfr I32(resinfo + 13); 32752059Sdfr config->ic_mem[config->ic_nmem].ir_align = 32852241Sdfr I32(resinfo + 9); 32952241Sdfr config->ic_nmem++; 33052241Sdfr break; 33152241Sdfr 33252241Sdfr case PNP_TAG_MEMORY32_FIXED: 33352241Sdfr if (I32(resinfo + 5) == 0) { 33452241Sdfr if (bootverbose) { 33552241Sdfr printf("%s: skipping empty range\n", 33652241Sdfr pnp_eisaformat(id)); 33752241Sdfr } 33852241Sdfr continue; 33952241Sdfr } 34052241Sdfr if (bootverbose) { 34158850Sdfr printf("%s: adding fixed memory32 range " 34252241Sdfr "%#x-%#x, size=%#x\n", 34352241Sdfr pnp_eisaformat(id), 34452241Sdfr I32(resinfo + 1), 34552241Sdfr I32(resinfo + 1) 34652241Sdfr + I32(resinfo + 5) - 1, 34752241Sdfr I32(resinfo + 5)); 34852241Sdfr } 34952241Sdfr 35052241Sdfr if (config->ic_nmem == ISA_NMEM) { 35158850Sdfr device_printf(parent, "too many memory ranges\n"); 35252241Sdfr scanning = 0; 35352241Sdfr break; 35452241Sdfr } 35552241Sdfr 35652241Sdfr config->ic_mem[config->ic_nmem].ir_start = 35752241Sdfr I32(resinfo + 1); 35852241Sdfr config->ic_mem[config->ic_nmem].ir_end = 35952241Sdfr I32(resinfo + 1) 36052241Sdfr + I32(resinfo + 5) - 1; 36152241Sdfr config->ic_mem[config->ic_nmem].ir_size = 36252241Sdfr I32(resinfo + 5); 36352241Sdfr config->ic_mem[config->ic_nmem].ir_align = 1; 36452241Sdfr config->ic_nmem++; 36552241Sdfr break; 36652241Sdfr 36752241Sdfr default: 36852241Sdfr /* Skip this resource */ 36958850Sdfr device_printf(parent, "unexpected large tag %d\n", 37052241Sdfr PNP_SRES_NUM(tag)); 37152241Sdfr } 37252059Sdfr } 37352059Sdfr } 37458850Sdfr if(ncfgs == 1) { 37558850Sdfr /* Single config without dependants */ 37658850Sdfr (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]); 37758873Sdfr free(configs, M_DEVBUF); 37858850Sdfr return; 37958850Sdfr } 38058850Sdfr /* Cycle through dependant configs merging primary details */ 38158850Sdfr for(i = 1; i < ncfgs; i++) { 38258850Sdfr int j; 38358850Sdfr config = &configs[i]; 38458850Sdfr for(j = 0; j < configs[0].ic_nmem; j++) { 38558850Sdfr if (config->ic_nmem == ISA_NMEM) { 38658850Sdfr device_printf(parent, "too many memory ranges\n"); 38758873Sdfr free(configs, M_DEVBUF); 38858850Sdfr return; 38958850Sdfr } 39058850Sdfr config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j]; 39158850Sdfr config->ic_nmem++; 39258850Sdfr } 39358850Sdfr for(j = 0; j < configs[0].ic_nport; j++) { 39458850Sdfr if (config->ic_nport == ISA_NPORT) { 39558850Sdfr device_printf(parent, "too many port ranges\n"); 39658873Sdfr free(configs, M_DEVBUF); 39758850Sdfr return; 39858850Sdfr } 39958850Sdfr config->ic_port[config->ic_nport] = configs[0].ic_port[j]; 40058850Sdfr config->ic_nport++; 40158850Sdfr } 40258850Sdfr for(j = 0; j < configs[0].ic_nirq; j++) { 40358850Sdfr if (config->ic_nirq == ISA_NIRQ) { 40458850Sdfr device_printf(parent, "too many irq ranges\n"); 40558873Sdfr free(configs, M_DEVBUF); 40658850Sdfr return; 40758850Sdfr } 40858850Sdfr config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j]; 40958850Sdfr config->ic_nirq++; 41058850Sdfr } 41158850Sdfr for(j = 0; j < configs[0].ic_ndrq; j++) { 41258850Sdfr if (config->ic_ndrq == ISA_NDRQ) { 41358850Sdfr device_printf(parent, "too many drq ranges\n"); 41458873Sdfr free(configs, M_DEVBUF); 41558850Sdfr return; 41658850Sdfr } 41758850Sdfr config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j]; 41858850Sdfr config->ic_ndrq++; 41958850Sdfr } 42058850Sdfr (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]); 42158850Sdfr } 42258873Sdfr free(configs, M_DEVBUF); 42352059Sdfr} 424