Date: Wed, 20 Dec 2000 14:55:01 +0900 From: Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> To: freebsd-hackers@freebsd.org Cc: yokota@zodiac.mech.utsunomiya-u.ac.jp Subject: Request for comments: ISA_PNP_SCAN() (long) Message-ID: <200012200555.OAA01093@zodiac.mech.utsunomiya-u.ac.jp>
next in thread | raw e-mail | index | archive | help
This is to propose a new ISA bus method to sys/isa/isa_common.c. The new method is to enumerate PnP device instances matching the specified PnP IDs. (Well, may be this is a kludge after all.) device_t ISA_PNP_SCAN(device_t bus, struct isa_pnp_id *ids, int *n); It will return the (n + 1)th instance of the given PnP IDs on the specified ISA bus. You set -1 to n to obtain the first PnP instance matching the given PnP IDs and can enumerate all matching instances by calling ISA_PNP_SCAN() until it returns NULL. I think this is useful for the following situation. The ISA device drivers supporting PnP look like the following in -CURRENT. struct isa_pnp_id foo_ids[] = { { 0xNNNNNNN, "Foo bar" }, }; int foo_probe(device_t dev) { if (ISA_PNP_PROBE(dev, foo_ids) == ENXIO) return ENXIO; ... } ISA_PNP_PROBE() returns 0 if the ISA device instance dev has the matching PNP ID, returns ENXIO if the ID doesn't match, and returns ENOENT if the device instance doesn't have a PNP ID (because it was created by the isahint driver, based on /boot/device.hints). This way, the driver can work correctly with both the "hint" (non-PnP) device instance and the PnP device instance. The trouble is that we always need to have hints for this driver in /boot/device.hints for those systems without a PnP BIOS. Then, the isahint driver will create a device instance. The pnpbios driver will create a PnP device instance separately if the PnP BIOS exists and reports the presence of a device. Problem 1: In -CURRENT the non-PnP device instance is probed first. If this is successful, it will become available to the system as the 'foo0' device. Probe on the PnP device instance will fail in this case because the resources for this device has already been claimed by the non-PnP device instance, and the user will see erroneous boot message "unknown: <PNP####> cannot assign resources". Problem 2: If the non-PnP device instance fails probe (because device hints are wrong), the PnP device instance will succeed (because its resource description is supposed to be correct). The PnP device instance will become available in the system as 'foo1', rather than 'foo0'. This is because the non-PnP device instance wasn't deleted after its probe failed. To avoid the second problem, we may prepare two separate drivers for non-PnP and PnP device instances as follows. /* the driver for non-PnP device instance */ driver_t foo_driver = { "foo", foo_methods, sizeof(struct foo_softc), }; int foo_probe(device_t dev) { /* proceed only if this is not a PnP device instance */ if (ISA_PNP_PROBE(dev, foo_ids) != ENOENT) return ENXIO; ... } /* the driver for PnP device instance */ driver_t foopnp_driver = { "foopnp", foopnp_methods, sizeof(struct foo_softc), }; int foopnp_probe(device_t dev) { /* proceed only if this is a PnP device instance */ if (ISA_PNP_PROBE(dev, foo_ids) != 0) return ENXIO; ... } This way, we will have 'foo0' when the PnP BIOS is not present or device hints for the non-PnP device instance are correct. Otherwise, we will have 'foopnp0'. But, we will still have the first problem: "unknown: <PNPXXXX> can't assign resources." If we have ISA_PNP_SCAN() above, we can do something like below to solve this problem. /* the driver for non-PnP device instance */ driver_t foo_driver = { "foo", foo_methods, sizeof(struct foo_softc), }; int foo_probe(device_t dev) { device_t bus; device_t pnpdev; int unit; int i; /* proceed only if this is a non-PnP device instance */ if (ISA_PNP_PROBE(dev, foo_ids) != ENOENT) return ENXIO; bus = device_get_parent(dev); unit = device_get_unit(dev); /* fail if we have a PnP sibling */ i = unit - 1; pnpdev = ISA_PNP_SCAN(bus, foo_ids, &i); if (pnpdev && device_get_state(pnpdev) == DS_NOTPRESENT) return ENXIO; ... } /* the driver for PnP device instance */ driver_t foopnp_driver = { "foopnp", foopnp_methods, sizeof(struct foo_softc), }; int foopnp_probe(device_t dev) { /* proceed only if this is a PnP device instance */ if (ISA_PNP_PROBE(dev, foo_ids) != 0) return ENXIO; ... } The non-PnP device instance will fail, regardless of device hints, if a PnP device instance for this device exists on this ISA bus. Then we will always have 'foopnp0' if the PnP BIOS reports the pretense of this device. The non-PnP device instance will succeed, as 'foo0', only if the PnP BIOS doesn't exist and device hints are correct. This way, we are now clear of the two problems described above. We can collapse the device methods for the two drivers into one. driver_t foo_driver = { "foo", foo_common_methods, sizeof(struct foo_softc), }; driver_t foopnp_driver = { "foopnp", foo_common_methods, sizeof(struct foo_softc), }; /* common methods */ int foo_probe(device_t dev) { device_t bus; device_t pnpdev; int i; switch (ISA_PNP_PROBE(dev, foo_ids)) { case ENXIO: default: return ENXIO; case ENOENT: bus = device_get_parent(dev); unit = device_get_unit(dev); i = unit - 1; pnpdev = ISA_PNP_SCAN(bus, foo_ids, &i); if (pnpdev && device_get_state(pnpdev) == DS_NOTPRESENT) return ENXIO; break; case 0: break; } .... } If we don't like the idea the device name change depending on the presence of the PnP BIOS, we may do the following. /* the driver for non-PnP device instance */ driver_t foo_driver = { "foo", foo_methods, sizeof(struct foo_softc), }; int foo_probe(device_t dev) { device_t bus; device_t pnpdev; int unit; int i; if (ISA_PNP_PROBE(dev, foo_ids) != ENOENT) return ENXIO; bus = device_get_parent(dev); unit = device_get_unit(dev); i = unit - 1; pnpdev = ISA_PNP_SCAN(bus, foo_ids, &i); if (pnpdev && device_get_state(pnpdev) == DS_NOTPRESENT) return ENXIO; ... } /* the driver for PnP device instance */ driver_t foopnp_driver = { "foopnp", foopnp_methods, sizeof(struct foo_softc), }; int foopnp_probe(device_t dev) { if (ISA_PNP_PROBE(dev, foo_ids) != 0) return ENXIO; /* do nothing else but succeed */ device_quiet(dev); return 0; } int foopnp_attach(device_t dev) { device_t bus; device_t hintdev; device_t newdev; u_int32_t flags; bus = device_get_parent(dev); unit = device_get_unit(dev); flags = 0; /* delete the existing non-PnP instance */ hintdev = device_find_child(bus, "foo", unit); if (hintdev) { flags = device_get_flags(hintdev); device_delete_child(bus, hintdev); } /* create a new non-PnP instance */ newdev = BUS_ADD_DEVICE(bus, 0, "foo", unit); /* _MOVE_ all resources from dev to newdev */ device_set_flags(newdev, flags); bus_get_resource(dev,...); bus_set_resource(newdev,...); /* probe the new non-PnP instance */ device_probe_and_attach(newdev); return 0; } In this case, the PnP device instance is nothing but a resource holder. The non-PnP driver will do all the actual probe and attach. The non-PnP device instance will be deleted during foopnp_attach() and a new, non-PnP device instance will be created, given resources recorded in the PnP device instance, and probed. Note that non-PnP's foo_probe() works correctly both in the first invocation (for the instance created by the isahint driver) and in the second invocation (for the instance created in foopnp_attach()). Comments? Kazu To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200012200555.OAA01093>