pnpparse.c revision 58850
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 58850 2000-03-31 07:12:12Z dfr $ 2752059Sdfr */ 2852059Sdfr 2952059Sdfr#include <sys/param.h> 3052059Sdfr#include <sys/systm.h> 3152059Sdfr#include <sys/kernel.h> 3252059Sdfr#include <sys/module.h> 3352059Sdfr#include <sys/bus.h> 3452059Sdfr#include <isa/isavar.h> 3552059Sdfr#include <isa/pnpreg.h> 3652059Sdfr#include <isa/pnpvar.h> 3752059Sdfr 3858850Sdfr#define MAXDEP 8 3958850Sdfr 4052241Sdfr#define I16(p) ((p)[0] + ((p)[1] << 8)) 4152241Sdfr#define I32(p) (I16(p) + (I16(p+2) << 16)) 4252241Sdfr 4352059Sdfr/* 4452059Sdfr * Parse resource data for Logical Devices. 4552059Sdfr * 4652059Sdfr * This function exits as soon as it gets an error reading *ANY* 4758850Sdfr * Resource Data or it reaches the end of Resource Data. 4852059Sdfr */ 4952059Sdfrvoid 5052059Sdfrpnp_parse_resources(device_t dev, u_char *resources, int len) 5152059Sdfr{ 5252059Sdfr device_t parent = device_get_parent(dev); 5352059Sdfr u_char tag, *resp, *resinfo; 5452059Sdfr int large_len, scanning = len; 5552241Sdfr u_int32_t id, compat_id; 5652059Sdfr struct isa_config *config; 5758850Sdfr int ncfgs = 1; 5858850Sdfr int priorities[1 + MAXDEP]; 5958850Sdfr struct isa_config configs[1 + MAXDEP]; 6052059Sdfr char buf[100]; 6158850Sdfr int i; 6252059Sdfr 6352241Sdfr id = isa_get_logicalid(dev); 6458850Sdfr bzero(configs, sizeof configs); 6558850Sdfr config = &configs[0]; 6658850Sdfr priorities[0] = 0; 6752059Sdfr resp = resources; 6852059Sdfr while (scanning > 0) { 6952059Sdfr tag = *resp++; 7052059Sdfr scanning--; 7152059Sdfr if (PNP_RES_TYPE(tag) == 0) { 7252059Sdfr /* Small resource */ 7352059Sdfr if (scanning < PNP_SRES_LEN(tag)) { 7452059Sdfr scanning = 0; 7552059Sdfr continue; 7652059Sdfr } 7752059Sdfr resinfo = resp; 7852059Sdfr resp += PNP_SRES_LEN(tag); 7952059Sdfr scanning -= PNP_SRES_LEN(tag);; 8052059Sdfr 8152059Sdfr switch (PNP_SRES_NUM(tag)) { 8252059Sdfr case PNP_TAG_COMPAT_DEVICE: 8352059Sdfr /* 8452059Sdfr * Got a compatible device id 8552059Sdfr * resource. Should keep a list of 8652059Sdfr * compat ids in the device. 8752059Sdfr */ 8852059Sdfr bcopy(resinfo, &compat_id, 4); 8952059Sdfr isa_set_compatid(dev, compat_id); 9052059Sdfr break; 9152059Sdfr 9252059Sdfr case PNP_TAG_IRQ_FORMAT: 9352241Sdfr if (bootverbose) { 9452241Sdfr printf("%s: adding irq mask %#04x\n", 9552241Sdfr pnp_eisaformat(id), 9652241Sdfr I16(resinfo)); 9752241Sdfr } 9852059Sdfr if (config->ic_nirq == ISA_NIRQ) { 9958850Sdfr device_printf(parent, "too many irqs\n"); 10052059Sdfr scanning = 0; 10152059Sdfr break; 10252059Sdfr } 10352059Sdfr config->ic_irqmask[config->ic_nirq] = 10452241Sdfr I16(resinfo); 10552059Sdfr config->ic_nirq++; 10652059Sdfr break; 10752059Sdfr 10852059Sdfr case PNP_TAG_DMA_FORMAT: 10952241Sdfr if (bootverbose) { 11052241Sdfr printf("%s: adding dma mask %#02x\n", 11152241Sdfr pnp_eisaformat(id), 11252241Sdfr resinfo[0]); 11352241Sdfr } 11452059Sdfr if (config->ic_ndrq == ISA_NDRQ) { 11558850Sdfr device_printf(parent, "too many drqs\n"); 11652059Sdfr scanning = 0; 11752059Sdfr break; 11852059Sdfr } 11952059Sdfr config->ic_drqmask[config->ic_ndrq] = 12052059Sdfr resinfo[0]; 12152059Sdfr config->ic_ndrq++; 12252059Sdfr break; 12352059Sdfr 12452059Sdfr case PNP_TAG_START_DEPENDANT: 12552241Sdfr if (bootverbose) { 12652241Sdfr printf("%s: start dependant\n", 12752241Sdfr pnp_eisaformat(id)); 12852241Sdfr } 12958850Sdfr if (ncfgs >= MAXDEP) { 13058850Sdfr device_printf(parent, "too many dependant configs (%d)\n", MAXDEP); 13152059Sdfr scanning = 0; 13252059Sdfr break; 13352059Sdfr } 13458850Sdfr config = &configs[ncfgs]; 13552059Sdfr /* 13652059Sdfr * If the priority is not specified, 13752059Sdfr * then use the default of 13852059Sdfr * 'acceptable' 13952059Sdfr */ 14052059Sdfr if (PNP_SRES_LEN(tag) > 0) 14158850Sdfr priorities[ncfgs] = resinfo[0]; 14252059Sdfr else 14358850Sdfr priorities[ncfgs] = 1; 14458850Sdfr ncfgs++; 14552059Sdfr break; 14652059Sdfr 14752059Sdfr case PNP_TAG_END_DEPENDANT: 14852241Sdfr if (bootverbose) { 14952241Sdfr printf("%s: end dependant\n", 15052241Sdfr pnp_eisaformat(id)); 15152241Sdfr } 15258850Sdfr config = &configs[0]; /* back to main config */ 15352059Sdfr break; 15452059Sdfr 15552059Sdfr case PNP_TAG_IO_RANGE: 15652241Sdfr if (bootverbose) { 15752241Sdfr printf("%s: adding io range " 15852241Sdfr "%#x-%#x, size=%#x, " 15952241Sdfr "align=%#x\n", 16052241Sdfr pnp_eisaformat(id), 16152241Sdfr I16(resinfo + 1), 16252241Sdfr I16(resinfo + 3) + resinfo[6]-1, 16352241Sdfr resinfo[6], 16452241Sdfr resinfo[5]); 16552241Sdfr } 16652059Sdfr if (config->ic_nport == ISA_NPORT) { 16758850Sdfr device_printf(parent, "too many ports\n"); 16852059Sdfr scanning = 0; 16952059Sdfr break; 17052059Sdfr } 17152059Sdfr config->ic_port[config->ic_nport].ir_start = 17252241Sdfr I16(resinfo + 1); 17352059Sdfr config->ic_port[config->ic_nport].ir_end = 17452241Sdfr I16(resinfo + 3) + resinfo[6] - 1; 17552241Sdfr config->ic_port[config->ic_nport].ir_size = 17652059Sdfr resinfo[6]; 17752241Sdfr if (resinfo[5] == 0) { 17852241Sdfr /* Make sure align is at least one */ 17952241Sdfr resinfo[5] = 1; 18052241Sdfr } 18152059Sdfr config->ic_port[config->ic_nport].ir_align = 18252059Sdfr resinfo[5]; 18352059Sdfr config->ic_nport++; 18452059Sdfr break; 18552059Sdfr 18652059Sdfr case PNP_TAG_IO_FIXED: 18752241Sdfr if (bootverbose) { 18858850Sdfr printf("%s: adding fixed io range " 18952241Sdfr "%#x-%#x, size=%#x, " 19052241Sdfr "align=%#x\n", 19152241Sdfr pnp_eisaformat(id), 19252241Sdfr I16(resinfo), 19352241Sdfr I16(resinfo) + resinfo[2] - 1, 19452241Sdfr resinfo[2], 19552241Sdfr 1); 19652241Sdfr } 19752059Sdfr if (config->ic_nport == ISA_NPORT) { 19858850Sdfr device_printf(parent, "too many ports\n"); 19952059Sdfr scanning = 0; 20052059Sdfr break; 20152059Sdfr } 20252059Sdfr config->ic_port[config->ic_nport].ir_start = 20352241Sdfr I16(resinfo); 20452059Sdfr config->ic_port[config->ic_nport].ir_end = 20552241Sdfr I16(resinfo) + resinfo[2] - 1; 20652059Sdfr config->ic_port[config->ic_nport].ir_size 20752059Sdfr = resinfo[2]; 20852059Sdfr config->ic_port[config->ic_nport].ir_align = 1; 20952059Sdfr config->ic_nport++; 21052059Sdfr break; 21152059Sdfr 21252241Sdfr case PNP_TAG_END: 21352241Sdfr if (bootverbose) { 21458850Sdfr printf("%s: end config\n", 21552241Sdfr pnp_eisaformat(id)); 21652241Sdfr } 21752241Sdfr scanning = 0; 21852241Sdfr break; 21952241Sdfr 22052059Sdfr default: 22152059Sdfr /* Skip this resource */ 22258850Sdfr device_printf(parent, "unexpected small tag %d\n", 22352059Sdfr PNP_SRES_NUM(tag)); 22452059Sdfr break; 22552059Sdfr } 22652059Sdfr } else { 22752059Sdfr /* Large resource */ 22852059Sdfr if (scanning < 2) { 22952059Sdfr scanning = 0; 23052059Sdfr continue; 23152059Sdfr } 23252241Sdfr large_len = I16(resp); 23352059Sdfr resp += 2; 23452059Sdfr scanning -= 2; 23552059Sdfr 23652059Sdfr if (scanning < large_len) { 23752059Sdfr scanning = 0; 23852059Sdfr continue; 23952059Sdfr } 24052059Sdfr resinfo = resp; 24152059Sdfr resp += large_len; 24252059Sdfr scanning -= large_len; 24352059Sdfr 24452241Sdfr switch (PNP_LRES_NUM(tag)) { 24552241Sdfr case PNP_TAG_ID_ANSI: 24652059Sdfr if (large_len > sizeof(buf) - 1) 24752059Sdfr large_len = sizeof(buf) - 1; 24852059Sdfr bcopy(resinfo, buf, large_len); 24952059Sdfr 25052059Sdfr /* 25158850Sdfr * Trim trailing spaces and garbage. 25252059Sdfr */ 25358850Sdfr while (large_len > 0 && buf[large_len - 1] <= ' ') 25452059Sdfr large_len--; 25552059Sdfr buf[large_len] = '\0'; 25652059Sdfr device_set_desc_copy(dev, buf); 25752241Sdfr break; 25852241Sdfr 25952241Sdfr case PNP_TAG_MEMORY_RANGE: 26052241Sdfr if (bootverbose) { 26158847Sdfr int temp = I16(resinfo + 7) << 8; 26258847Sdfr 26352241Sdfr printf("%s: adding memory range " 26452241Sdfr "%#x-%#x, size=%#x, " 26552241Sdfr "align=%#x\n", 26652241Sdfr pnp_eisaformat(id), 26752241Sdfr I16(resinfo + 1)<<8, 26858847Sdfr (I16(resinfo + 3)<<8) + temp - 1, 26958847Sdfr temp, 27052241Sdfr I16(resinfo + 5)); 27152241Sdfr } 27252059Sdfr 27352241Sdfr if (config->ic_nmem == ISA_NMEM) { 27452241Sdfr device_printf(parent, "too many memory ranges"); 27552241Sdfr scanning = 0; 27652241Sdfr break; 27752241Sdfr } 27852059Sdfr 27952241Sdfr config->ic_mem[config->ic_nmem].ir_start = 28052241Sdfr I16(resinfo + 1)<<8; 28152241Sdfr config->ic_mem[config->ic_nmem].ir_end = 28252241Sdfr (I16(resinfo + 3)<<8) 28358847Sdfr + (I16(resinfo + 7) << 8) - 1; 28452241Sdfr config->ic_mem[config->ic_nmem].ir_size = 28558847Sdfr I16(resinfo + 7) << 8; 28652241Sdfr config->ic_mem[config->ic_nmem].ir_align = 28752241Sdfr I16(resinfo + 5); 28852241Sdfr if (!config->ic_mem[config->ic_nmem].ir_align) 28952241Sdfr config->ic_mem[config->ic_nmem] 29052241Sdfr .ir_align = 0x10000; 29152241Sdfr config->ic_nmem++; 29252059Sdfr break; 29352059Sdfr 29452241Sdfr case PNP_TAG_MEMORY32_RANGE: 29552241Sdfr if (bootverbose) { 29658850Sdfr printf("%s: adding memory32 range " 29752241Sdfr "%#x-%#x, size=%#x, " 29852241Sdfr "align=%#x\n", 29952241Sdfr pnp_eisaformat(id), 30052241Sdfr I32(resinfo + 1), 30152241Sdfr I32(resinfo + 5) 30252241Sdfr + I32(resinfo + 13) - 1, 30352241Sdfr I32(resinfo + 13), 30452241Sdfr I32(resinfo + 9)); 30552241Sdfr } 30652241Sdfr 30752241Sdfr if (config->ic_nmem == ISA_NMEM) { 30858850Sdfr device_printf(parent, "too many memory ranges\n"); 30952241Sdfr scanning = 0; 31052241Sdfr break; 31152241Sdfr } 31252241Sdfr 31352241Sdfr config->ic_mem[config->ic_nmem].ir_start = 31452241Sdfr I32(resinfo + 1); 31552241Sdfr config->ic_mem[config->ic_nmem].ir_end = 31652241Sdfr I32(resinfo + 5) 31752241Sdfr + I32(resinfo + 13) - 1; 31852241Sdfr config->ic_mem[config->ic_nmem].ir_size = 31952241Sdfr I32(resinfo + 13); 32052059Sdfr config->ic_mem[config->ic_nmem].ir_align = 32152241Sdfr I32(resinfo + 9); 32252241Sdfr config->ic_nmem++; 32352241Sdfr break; 32452241Sdfr 32552241Sdfr case PNP_TAG_MEMORY32_FIXED: 32652241Sdfr if (I32(resinfo + 5) == 0) { 32752241Sdfr if (bootverbose) { 32852241Sdfr printf("%s: skipping empty range\n", 32952241Sdfr pnp_eisaformat(id)); 33052241Sdfr } 33152241Sdfr continue; 33252241Sdfr } 33352241Sdfr if (bootverbose) { 33458850Sdfr printf("%s: adding fixed memory32 range " 33552241Sdfr "%#x-%#x, size=%#x\n", 33652241Sdfr pnp_eisaformat(id), 33752241Sdfr I32(resinfo + 1), 33852241Sdfr I32(resinfo + 1) 33952241Sdfr + I32(resinfo + 5) - 1, 34052241Sdfr I32(resinfo + 5)); 34152241Sdfr } 34252241Sdfr 34352241Sdfr if (config->ic_nmem == ISA_NMEM) { 34458850Sdfr device_printf(parent, "too many memory ranges\n"); 34552241Sdfr scanning = 0; 34652241Sdfr break; 34752241Sdfr } 34852241Sdfr 34952241Sdfr config->ic_mem[config->ic_nmem].ir_start = 35052241Sdfr I32(resinfo + 1); 35152241Sdfr config->ic_mem[config->ic_nmem].ir_end = 35252241Sdfr I32(resinfo + 1) 35352241Sdfr + I32(resinfo + 5) - 1; 35452241Sdfr config->ic_mem[config->ic_nmem].ir_size = 35552241Sdfr I32(resinfo + 5); 35652241Sdfr config->ic_mem[config->ic_nmem].ir_align = 1; 35752241Sdfr config->ic_nmem++; 35852241Sdfr break; 35952241Sdfr 36052241Sdfr default: 36152241Sdfr /* Skip this resource */ 36258850Sdfr device_printf(parent, "unexpected large tag %d\n", 36352241Sdfr PNP_SRES_NUM(tag)); 36452241Sdfr } 36552059Sdfr } 36652059Sdfr } 36758850Sdfr if(ncfgs == 1) { 36858850Sdfr /* Single config without dependants */ 36958850Sdfr (void)ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]); 37058850Sdfr return; 37158850Sdfr } 37258850Sdfr /* Cycle through dependant configs merging primary details */ 37358850Sdfr for(i = 1; i < ncfgs; i++) { 37458850Sdfr int j; 37558850Sdfr config = &configs[i]; 37658850Sdfr for(j = 0; j < configs[0].ic_nmem; j++) { 37758850Sdfr if (config->ic_nmem == ISA_NMEM) { 37858850Sdfr device_printf(parent, "too many memory ranges\n"); 37958850Sdfr return; 38058850Sdfr } 38158850Sdfr config->ic_mem[config->ic_nmem] = configs[0].ic_mem[j]; 38258850Sdfr config->ic_nmem++; 38358850Sdfr } 38458850Sdfr for(j = 0; j < configs[0].ic_nport; j++) { 38558850Sdfr if (config->ic_nport == ISA_NPORT) { 38658850Sdfr device_printf(parent, "too many port ranges\n"); 38758850Sdfr return; 38858850Sdfr } 38958850Sdfr config->ic_port[config->ic_nport] = configs[0].ic_port[j]; 39058850Sdfr config->ic_nport++; 39158850Sdfr } 39258850Sdfr for(j = 0; j < configs[0].ic_nirq; j++) { 39358850Sdfr if (config->ic_nirq == ISA_NIRQ) { 39458850Sdfr device_printf(parent, "too many irq ranges\n"); 39558850Sdfr return; 39658850Sdfr } 39758850Sdfr config->ic_irqmask[config->ic_nirq] = configs[0].ic_irqmask[j]; 39858850Sdfr config->ic_nirq++; 39958850Sdfr } 40058850Sdfr for(j = 0; j < configs[0].ic_ndrq; j++) { 40158850Sdfr if (config->ic_ndrq == ISA_NDRQ) { 40258850Sdfr device_printf(parent, "too many drq ranges\n"); 40358850Sdfr return; 40458850Sdfr } 40558850Sdfr config->ic_drqmask[config->ic_ndrq] = configs[0].ic_drqmask[j]; 40658850Sdfr config->ic_ndrq++; 40758850Sdfr } 40858850Sdfr (void)ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]); 40958850Sdfr } 41052059Sdfr} 411