Example #1
0
/*
 * Scan the resource data in the node's data area for compatible device IDs
 * and descriptions.
 */
static void
biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn)
{
    int		tag, i, rlen, dlen;
    u_int8_t	*p;
    char	*str;

    p = dn->dn_data;			/* point to resource data */
    dlen = dn->dn_size - (p - (u_int8_t *)dn);	/* length of resource data */

    for (i = 0; i < dlen; i+= rlen) {
	tag = p[i];
	i++;
	if (PNP_RES_TYPE(tag) == 0) {
	    rlen = PNP_SRES_LEN(tag);
	    /* small resource */
	    switch (PNP_SRES_NUM(tag)) {

	    case COMP_DEVICE_ID:
		/* got a compatible device ID */
		pnp_addident(pi, pnp_eisaformat(p + i));
		break;
		
	    case END_TAG:
		return;
	    }
	} else {
	    /* large resource */
	    rlen = *(u_int16_t *)(p + i);
	    i += sizeof(u_int16_t);
	    
	    switch(PNP_LRES_NUM(tag)) {

	    case ID_STRING_ANSI:
		str = malloc(rlen + 1);
		bcopy(p + i, str, rlen);
		str[rlen] = 0;
		if (pi->pi_desc == NULL) {
		    pi->pi_desc = str;
		} else {
		    free(str);
		}
		break;
	    }
	}
    }
}
Example #2
0
u_char
*pnp_scan_resources(device_t dev, u_char *resources, int len,
		    struct isa_config *config, int ldn, pnp_scan_cb *cb)
{
	u_char *p;
	u_char tag;
	int l;

	p = resources;
	while (len > 0) {
		tag = *p++;
		len--;
		if (PNP_RES_TYPE(tag) == 0) {
			/* small resource */
			l = PNP_SRES_LEN(tag);
			if (len < l)
				break;
			if ((*cb)(dev, tag, p, l, config, ldn))
				return (p + l);
			if (PNP_SRES_NUM(tag) == PNP_TAG_END)
				return (p + l);
		} else {
			/* large resource */
			if (len < 2)
				break;
			l = I16(p);
			p += 2;
			len -= 2;
			if (len < l)
				break;
			if ((*cb)(dev, tag, p, l, config, ldn))
				return (p + l);
		}
		p += l;
		len -= l;
	}
	return NULL;
}
Example #3
0
static int
pnp_parse_desc(device_t dev, u_char tag, u_char *res, int len,
	       struct isa_config *config, int ldn)
{
	char buf[100];
	u_int32_t id;
	u_int32_t compat_id;
	int temp;

	id = isa_get_logicalid(dev);

	if (PNP_RES_TYPE(tag) == 0) {

		/* Small resource */
		switch (PNP_SRES_NUM(tag)) {

		case PNP_TAG_VERSION:
		case PNP_TAG_VENDOR:
			/* these descriptors are quietly ignored */
			break;

		case PNP_TAG_LOGICAL_DEVICE:
		case PNP_TAG_START_DEPENDANT:
		case PNP_TAG_END_DEPENDANT:
			if (bootverbose)
				pnp_printf(id, "unexpected small tag %d\n",
					   PNP_SRES_NUM(tag));
			/* shouldn't happen; quit now */
			return (1);

		case PNP_TAG_COMPAT_DEVICE:
			/*
			 * Got a compatible device id resource.
			 * Should keep a list of compat ids in the device.
			 */
			bcopy(res, &compat_id, 4);
			if (isa_get_compatid(dev) == 0)
				isa_set_compatid(dev, compat_id);
			break;
	    
		case PNP_TAG_IRQ_FORMAT:
			if (config->ic_nirq == ISA_NIRQ) {
				pnp_printf(id, "too many irqs\n");
				return (1);
			}
			if (I16(res) == 0) {
				/* a null descriptor */
				config->ic_irqmask[config->ic_nirq] = 0;
				config->ic_nirq++;
				break;
			}
			if (bootverbose)
				pnp_printf(id, "adding irq mask %#02x\n",
					   I16(res));
			config->ic_irqmask[config->ic_nirq] = I16(res);
			config->ic_nirq++;
			break;

		case PNP_TAG_DMA_FORMAT:
			if (config->ic_ndrq == ISA_NDRQ) {
				pnp_printf(id, "too many drqs\n");
				return (1);
			}
			if (res[0] == 0) {
				/* a null descriptor */
				config->ic_drqmask[config->ic_ndrq] = 0;
				config->ic_ndrq++;
				break;
			}
			if (bootverbose)
				pnp_printf(id, "adding dma mask %#02x\n",
					   res[0]);
			config->ic_drqmask[config->ic_ndrq] = res[0];
			config->ic_ndrq++;
			break;

		case PNP_TAG_IO_RANGE:
			if (config->ic_nport == ISA_NPORT) {
				pnp_printf(id, "too many ports\n");
				return (1);
			}
			if (res[6] == 0) {
				/* a null descriptor */
				config->ic_port[config->ic_nport].ir_start = 0;
				config->ic_port[config->ic_nport].ir_end = 0;
				config->ic_port[config->ic_nport].ir_size = 0;
				config->ic_port[config->ic_nport].ir_align = 0;
				config->ic_nport++;
				break;
			}
			if (bootverbose) {
				pnp_printf(id, "adding io range "
					   "%#x-%#x, size=%#x, "
					   "align=%#x\n",
					   I16(res + 1),
					   I16(res + 3) + res[6]-1,
					   res[6], res[5]);
			}
			config->ic_port[config->ic_nport].ir_start =
			    I16(res + 1);
			config->ic_port[config->ic_nport].ir_end =
			    I16(res + 3) + res[6] - 1;
			config->ic_port[config->ic_nport].ir_size = res[6];
			if (res[5] == 0) {
			    /* Make sure align is at least one */
			    res[5] = 1;
			}
			config->ic_port[config->ic_nport].ir_align = res[5];
			config->ic_nport++;
			pnp_check_quirks(isa_get_vendorid(dev),
					 isa_get_logicalid(dev), ldn, config);
			break;

		case PNP_TAG_IO_FIXED:
			if (config->ic_nport == ISA_NPORT) {
				pnp_printf(id, "too many ports\n");
				return (1);
			}
			if (res[2] == 0) {
				/* a null descriptor */
				config->ic_port[config->ic_nport].ir_start = 0;
				config->ic_port[config->ic_nport].ir_end = 0;
				config->ic_port[config->ic_nport].ir_size = 0;
				config->ic_port[config->ic_nport].ir_align = 0;
				config->ic_nport++;
				break;
			}
			if (bootverbose) {
				pnp_printf(id, "adding fixed io range "
					   "%#x-%#x, size=%#x, "
					   "align=%#x\n",
					   I16(res),
					   I16(res) + res[2] - 1,
					   res[2], 1);
			}
			config->ic_port[config->ic_nport].ir_start = I16(res);
			config->ic_port[config->ic_nport].ir_end =
			    I16(res) + res[2] - 1;
			config->ic_port[config->ic_nport].ir_size = res[2];
			config->ic_port[config->ic_nport].ir_align = 1;
			config->ic_nport++;
			break;

		case PNP_TAG_END:
			if (bootverbose)
				pnp_printf(id, "end config\n");
			return (1);

		default:
			/* Skip this resource */
			pnp_printf(id, "unexpected small tag %d\n",
				      PNP_SRES_NUM(tag));
			break;
		}
	} else {
		/* Large resource */
		switch (PNP_LRES_NUM(tag)) {

		case PNP_TAG_ID_UNICODE:
		case PNP_TAG_LARGE_VENDOR:
			/* these descriptors are quietly ignored */
			break;

		case PNP_TAG_ID_ANSI:
			if (len > sizeof(buf) - 1)
				len = sizeof(buf) - 1;
			bcopy(res, buf, len);

			/*
			 * Trim trailing spaces and garbage.
			 */
			while (len > 0 && buf[len - 1] <= ' ')
				len--;
			buf[len] = '\0';
			device_set_desc_copy(dev, buf);
			break;
			
		case PNP_TAG_MEMORY_RANGE:
			if (config->ic_nmem == ISA_NMEM) {
				pnp_printf(id, "too many memory ranges\n");
				return (1);
			}
			if (I16(res + 7) == 0) {
				/* a null descriptor */
				config->ic_mem[config->ic_nmem].ir_start = 0;
				config->ic_mem[config->ic_nmem].ir_end = 0;
				config->ic_mem[config->ic_nmem].ir_size = 0;
				config->ic_mem[config->ic_nmem].ir_align = 0;
				config->ic_nmem++;
				break;
			}
			if (bootverbose) {
				temp = I16(res + 7) << 8;
				pnp_printf(id, "adding memory range "
					   "%#x-%#x, size=%#x, "
					   "align=%#x\n",
					   I16(res + 1) << 8,
					   (I16(res + 3) << 8) + temp - 1,
					   temp, I16(res + 5));
			}
			config->ic_mem[config->ic_nmem].ir_start =
			    I16(res + 1) << 8;
			config->ic_mem[config->ic_nmem].ir_end =
			    (I16(res + 3) << 8) + (I16(res + 7) << 8) - 1;
			config->ic_mem[config->ic_nmem].ir_size =
			    I16(res + 7) << 8;
			config->ic_mem[config->ic_nmem].ir_align = I16(res + 5);
			if (!config->ic_mem[config->ic_nmem].ir_align)
				config->ic_mem[config->ic_nmem].ir_align =
				    0x10000;
			config->ic_nmem++;
			break;

		case PNP_TAG_MEMORY32_RANGE:
			if (config->ic_nmem == ISA_NMEM) {
				pnp_printf(id, "too many memory ranges\n");
				return (1);
			}
			if (I32(res + 13) == 0) {
				/* a null descriptor */
				config->ic_mem[config->ic_nmem].ir_start = 0;
				config->ic_mem[config->ic_nmem].ir_end = 0;
				config->ic_mem[config->ic_nmem].ir_size = 0;
				config->ic_mem[config->ic_nmem].ir_align = 0;
				config->ic_nmem++;
				break;
			}
			if (bootverbose) {
				pnp_printf(id, "adding memory32 range "
					   "%#x-%#x, size=%#x, "
					   "align=%#x\n",
					   I32(res + 1),
					   I32(res + 5) + I32(res + 13) - 1,
					   I32(res + 13), I32(res + 9));
			}
			config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
			config->ic_mem[config->ic_nmem].ir_end =
			    I32(res + 5) + I32(res + 13) - 1;
			config->ic_mem[config->ic_nmem].ir_size = I32(res + 13);
			config->ic_mem[config->ic_nmem].ir_align = I32(res + 9);
			config->ic_nmem++;
			break;

		case PNP_TAG_MEMORY32_FIXED:
			if (config->ic_nmem == ISA_NMEM) {
				pnp_printf(id, "too many memory ranges\n");
				return (1);
			}
			if (I32(res + 5) == 0) {
				/* a null descriptor */
				config->ic_mem[config->ic_nmem].ir_start = 0;
				config->ic_mem[config->ic_nmem].ir_end = 0;
				config->ic_mem[config->ic_nmem].ir_size = 0;
				config->ic_mem[config->ic_nmem].ir_align = 0;
				break;
			}
			if (bootverbose) {
				pnp_printf(id, "adding fixed memory32 range "
					   "%#x-%#x, size=%#x\n",
					   I32(res + 1),
					   I32(res + 1) + I32(res + 5) - 1,
					   I32(res + 5));
			}
			config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
			config->ic_mem[config->ic_nmem].ir_end =
			    I32(res + 1) + I32(res + 5) - 1;
			config->ic_mem[config->ic_nmem].ir_size = I32(res + 5);
			config->ic_mem[config->ic_nmem].ir_align = 1;
			config->ic_nmem++;
			break;

		default:
			/* Skip this resource */
			pnp_printf(id, "unexpected large tag %d\n",
				   PNP_SRES_NUM(tag));
			break;
		}
	}

	return (0);
}
Example #4
0
void
pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn)
{
	struct isa_config *configs;
	struct isa_config *config;
	device_t parent;
	int priorities[1 + MAXDEP];
	u_char *start;
	u_char *p;
	u_char tag;
	u_int32_t id;
	int ncfgs;
	int l;
	int i;

	parent = device_get_parent(dev);
	id = isa_get_logicalid(dev);

	configs = (struct isa_config *)malloc(sizeof(*configs)*(1 + MAXDEP),
					      M_DEVBUF, M_NOWAIT | M_ZERO);
	if (configs == NULL) {
		device_printf(parent, "No memory to parse PNP data\n");
		return;
	}
	config = &configs[0];
	priorities[0] = 0;
	ncfgs = 1;

	p = resources;
	start = NULL;
	while (len > 0) {
		tag = *p++;
		len--;
		if (PNP_RES_TYPE(tag) == 0) {
			/* Small resource */
			l = PNP_SRES_LEN(tag);
			if (len < l) {
				len = 0;
				continue;
			}
			len -= l;

			switch (PNP_SRES_NUM(tag)) {

			case PNP_TAG_START_DEPENDANT:
				if (start != NULL) {
					/*
					 * Copy the common resources first,
					 * then parse the "dependent" resources.
					 */
					pnp_merge_resources(dev, &configs[0],
							    config);
					pnp_parse_dependant(dev, start,
							    p - start - 1,
							    config, ldn);
				}
				start = p + l;
				if (ncfgs > MAXDEP) {
					device_printf(parent, "too many dependant configs (%d)\n", MAXDEP);
					len = 0;
					break;
				}
				config = &configs[ncfgs];
				/*
				 * If the priority is not specified,
				 * then use the default of 'acceptable'
				 */
				if (l > 0)
					priorities[ncfgs] = p[0];
				else
					priorities[ncfgs] = 1;
				if (bootverbose)
					pnp_printf(id, "start dependent (%d)\n",
						   priorities[ncfgs]);
				ncfgs++;
				break;

			case PNP_TAG_END_DEPENDANT:
				if (start == NULL) {
					device_printf(parent,
						      "malformed resources\n");
					len = 0;
					break;
				}
				/*
				 * Copy the common resources first,
				 * then parse the "dependent" resources.
				 */
				pnp_merge_resources(dev, &configs[0], config);
				pnp_parse_dependant(dev, start, p - start - 1,
						    config, ldn);
				start = NULL;
				if (bootverbose)
					pnp_printf(id, "end dependent\n");
				/*
				 * Back to the common part; clear it
				 * as its contents has already been copied
				 * to each dependant.
				 */
				config = &configs[0];
				bzero(config, sizeof(*config));
				break;

			case PNP_TAG_END:
				if (start != NULL) {
					device_printf(parent,
						      "malformed resources\n");
				}
				len = 0;
				break;

			default:
				if (start != NULL)
					/* defer parsing a dependent section */
					break;
				if (pnp_parse_desc(dev, tag, p, l, config, ldn))
					len = 0;
				break;
			}
			p += l;
		} else {
			/* Large resource */
			if (len < 2) {
				len = 0;
				break;
			}
			l = I16(p);
			p += 2;
			len -= 2;
			if (len < l) {
				len = 0;
				break;
			}
			len -= l;
			if (start == NULL &&
			    pnp_parse_desc(dev, tag, p, l, config, ldn)) {
				len = 0;
				break;
			}
			p += l;
		}
	}

	if (ncfgs == 1) {
		/* Single config without dependants */
		ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
		free(configs, M_DEVBUF);
		return;
	}

	for (i = 1; i < ncfgs; i++) {
		/*
		 * Merge the remaining part of the common resources,
		 * if any. Strictly speaking, there shouldn't be common/main
		 * resources after the END_DEPENDENT tag.
		 */
		pnp_merge_resources(dev, &configs[0], &configs[i]);
		ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
	}

	free(configs, M_DEVBUF);
}
Example #5
0
/*
 * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
 */
static int
pnpbios_identify(driver_t *driver, device_t parent)
{
    struct PnPBIOS_table	*pt = PnPBIOStable;
    struct bios_args		args;
    struct pnp_sysdev		*pd;
    struct pnp_sysdevargs	*pda;
    uint16_t			ndevs, bigdev;
    int				error, currdev;
    uint8_t			*devnodebuf, tag;
    uint32_t			*devid, *compid;
    int				idx, left;
    device_t			dev;

    /*
     * Umm, we aren't going to rescan the PnP BIOS to look for new additions.
     */
    if (device_get_state(parent) == DS_ATTACHED)
	return (0);
        
    /* no PnP BIOS information */
    if (pt == NULL)
	return (ENXIO);

    /* ACPI already active */
    if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
	return (ENXIO);
    
    bzero(&args, sizeof(args));
    args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
    args.seg.code16.limit = 0xffff;		/* XXX ? */
    args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
    args.seg.data.limit = 0xffff;
    args.entry = pt->pmentryoffset;

    if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
	kprintf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
    ndevs &= 0xff;				/* clear high byte garbage */
    if (bootverbose)
	kprintf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);

    devnodebuf = kmalloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_INTWAIT);
    pda = (struct pnp_sysdevargs *)devnodebuf;
    pd = &pda->node;

    for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
	bzero(pd, bigdev);
	pda->next = currdev;

	/* get current configuration */
	if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
	    kprintf("pnpbios: error %d making BIOS16 call\n", error);
	    break;
	}
	if (bootverbose)
	    kprintf("pnp_get_devnode cd=%d nxt=%d size=%d handle=%d devid=%08x type=%02x%02x%02x, attrib=%04x\n", currdev, pda->next, pd->size, pd->handle, pd->devid, pd->type[0], pd->type[1], pd->type[2], pd->attrib);

	if ((error = (args.r.eax & 0xff))) {
	    if (bootverbose)
		kprintf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
	    if (error & 0x80) 
		break;
	}
	currdev = pda->next;
	if (pd->size < sizeof(struct pnp_sysdev)) {
	    kprintf("pnpbios: bogus system node data, aborting scan\n");
	    break;
	}
	
	/*
	 * Ignore PICs so that we don't have to worry about the PICs
	 * claiming IRQs to prevent their use.  The PIC drivers
	 * already ensure that invalid IRQs are not used.
	 */
	if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))	/* ISA PIC */
	    continue;
	if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))	/* APIC */
	    continue;

	/* Add the device and parse its resources */
	dev = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, NULL, -1);
	isa_set_vendorid(dev, pd->devid);
	isa_set_logicalid(dev, pd->devid);

	/*
	 * It appears that some PnP BIOS doesn't allow us to re-enable
	 * the embedded system device once it is disabled.  We shall
	 * mark all system device nodes as "cannot be disabled", regardless
	 * of actual settings in the device attribute byte.  XXX
	 */
#if 0
	isa_set_configattr(dev, 
	    ((pd->attrib & PNPATTR_NODISABLE) ?  0 : ISACFGATTR_CANDISABLE) |
	    ((!(pd->attrib & PNPATTR_NOCONFIG) && 
		PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
		? ISACFGATTR_DYNAMIC : 0));
#endif
	isa_set_configattr(dev, 
	    (!(pd->attrib & PNPATTR_NOCONFIG) && 
		PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
		? ISACFGATTR_DYNAMIC : 0);

	ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
	pnp_parse_resources(dev, &pd->devdata[0],
			    pd->size - sizeof(struct pnp_sysdev), 0);
	if (!device_get_desc(dev))
	    device_set_desc_copy(dev, pnp_eisaformat(pd->devid));

	/* Find device IDs */
	devid = &pd->devid;
	compid = NULL;

	/* look for a compatible device ID too */
	left = pd->size - sizeof(struct pnp_sysdev);
	idx = 0;
	while (idx < left) {
	    tag = pd->devdata[idx++];
	    if (PNP_RES_TYPE(tag) == 0) {
		/* Small resource */
		switch (PNP_SRES_NUM(tag)) {
		case PNP_TAG_COMPAT_DEVICE:
		    compid = (uint32_t *)(pd->devdata + idx);
		    if (bootverbose)
			kprintf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
		    /* FALLTHROUGH */
		case PNP_TAG_END:
		    idx = left;
		    break;
		default:
		    idx += PNP_SRES_LEN(tag);
		    break;
		}
	    } else
		/* Large resource, skip it */
		idx += *(uint16_t *)(pd->devdata + idx) + 2;
	}
	if (bootverbose) {
	    kprintf("pnpbios: handle %d device ID %s (%08x)", 
		   pd->handle, pnp_eisaformat(*devid), *devid);
	    if (compid != NULL)
		kprintf(" compat ID %s (%08x)",
		       pnp_eisaformat(*compid), *compid);
	    kprintf("\n");
	}
    }
    return (0);
}