Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 May 1997 22:45:14 +0200
From:      Stefan Esser <se@FreeBSD.ORG>
To:        Doug Rabson <dfr@nlsystems.com>
Cc:        Michael Smith <msmith@atrad.adelaide.edu.au>, current@FreeBSD.ORG
Subject:   Re: Backwards compatibiliy for isa_driver
Message-ID:  <19970514224514.26045@x14.mi.uni-koeln.de>
In-Reply-To: <Pine.BSF.3.95q.970514155612.947P-100000@herring.nlsystems.com>; from Doug Rabson on Wed, May 14, 1997 at 04:27:37PM %2B0100
References:  <19970513114307.17799@x14.mi.uni-koeln.de> <Pine.BSF.3.95q.970514155612.947P-100000@herring.nlsystems.com>

next in thread | previous in thread | raw e-mail | index | archive | help
[ I want to broaden the topic of this thread, since I think it is 
useless to look only at the representation of resources or device
data in the kernel. So this message is about driver data representation
in the kernel, but also about a persistent storage for driver config
parameters and even DEVFS state. Please excuse the long reply, and 
please let me know, what you think about it ... ]

On May 14, Doug Rabson <dfr@nlsystems.com> wrote:
> On Tue, 13 May 1997, Stefan Esser wrote:
> > 2) Structure depends on bus type, values private per
> >    device instance, maintained by the "device" above,
> >    which happens to be a "bus device":
> > 
> > 	- "wired position" (e.g. "at pci1 slot 4")
> > 	- probemessage()
> > 	- resources
> 
> While I was daydreaming about this stuff, I imagined that the resources
> could be stored in a database (sysctl?).  This would be filled in from
> various sources, isa static device data, isapnp device data, pci etc.

There are two approaches:

1) define a database that supports all types of devices and buses

2) use "methods" to access the data without knowledge about the actual format

My current sample implementation uses 1), since I can't put function 
pointers into device structures without a lot of effort.

My proposed solution is 2), since this allows to have an optimum
representation of the data for each particular bus type, and even
to change the representation without breaking old binaries (think
about LKMs, for example).

You can have sysctl working on both of them:

1) Define access procedures for the data types supported and a
   mapping from sysctl names to fields in the database

2) Make one of the "methods" specific to each bus accept a sysctl
   selector, and provide another one that gives the names and types
   of all bus specific sysctl fields s device. (The device can of 
   course still define driver specific sysctl variables in the old 
   way. The list could for example be:

	1, "mbase", INT32
	2, "msize", INT32
	...

   This list would be used to call for example the ISA bus specific 
   function to obtain and INT32 value of a memory mapped region.)

The reason I want to see the second version implemented, is that it
only needs a well defined interface, but doesn't restrict later 
extensions of the bus specific values in number or type.

For example for ISA, I need to keep a memory addr and size, a port
address and size and IRQ and DMA values. For PCI, there are 6 maps
available, which may be inactive or decode memory or port addresses
in any combination.

If I use the 2) concept, then all I need is a bus-specific way
(available through a function pointer of the parent of the device)
to query (and possibly set) resources. (There may be complex 
dependencies between the "bus device" and the device, e.g. the
memory map of a PCI device must be within the limits of the 
address window of its bridge, or accesses will be blocked without
reaching the bus the device is on).

You need to implement bus specific consistency checks on values
you want to set from sysctl, anyway, so why not keep the values 
in a bus specific optimal form, and just define read/write/check 
functions for each bus ...

> Since the location of the data is hidden, instead of in explicit memory
> structures, it can be read into the kernel from a config file.  The kernel
> could then load the appropriate drivers and call the probe routines.

Well, I sent mail to a few people a few days ago, in which I 
proposed a config database that can be loaded together with the
kernel and might be implemented as follows:

a) Make the boot loader read a text file and put it on a well known
   address in physical memory (this is already done for /boot.config,
   and I just propose to allow for more than one line to be specified
   in that file :)

b) The file should be human readable (I hate AIX's ODM database) and
   contain tuples of the folowing form:

	(type, selector, unit, values)

   The primary access function would be:

	int config_query (char *type, char* name, int unit, 
			  int max_out, int *vec_out);

   Examples:

	int data[10];
	values = config_query ("scbus", "sd", 0, 10, data);

   should put as many values as defined for sd0 at scbus into the
   data[] array, and those values would be known to encode:

	BUSNO, TARGET, LUN, ...

   A human readable way to encode the data given in the example
   above in a text file could be:

	sd:0:scbus:BUSNO:TARGET:LUN:...\n

   or, more like what you'd write into a config file:

	sd0=scbusBUSNO,TARGET,LUN,...\n (with the name ending at first digit)

   We could of course also use something modeled after termcap
   or MS resource files. The encoding should be easy to parse,
   but also easy to manually modify.

   If I want an interface to sysctl, then all that's required
   is a mapping of index into array to sysctl variable.

   Trees can easily be built by having a "selector" that matches
   the type of some other line (see: scbus0 at isa0 ...), and it
   is not hard to produce a sysctl tree from these values.

Now, this looks like I want to propose variant 1) above, here.
But this is not the case: I think the data found in those text
files should wherever possible be used to initialize bus specific
data structures. The SCSI code could for example load its table
of wired devices from all records that have a type of "scbus".
The same is true for ISA devices.

But what I really want to create this way is a extensible persistent 
store of config info, including local DEVFS permission and device 
number choices.

For this to work, I need not only a function that retrieves the data
array for a given (type, selector, unit), but also a list of units
for a given type and selector, and a list of types (char *) for a 
given selector, or a list of selectors for a given type. (E.g. list
all drivers that exist for type "isa" or "scbus", or list all types
that contain data for "ed0" which might be "isa" and "pci" ...)

Think about:

	char:13:sd:0:0640:ROOT:OPERATOR:...
	block:4:sd:0:0640:ROOT:OPERATOR:...

(where sd identifies the driver, ROOT and OPERATOR are numeric uid and 
gid values, 13 and 4 are the major numbers of /dev/rsdXX and /dev/sdXX
and the 0 after "sd" is the minor number, all values are actually stored
in decimal even if I wrote the permissions in octal above ...).

c) The configuration should be written back to the config file similar
   to the way "dset" currently updates the kernel with user_config 
   choices. If DEVFS uses this file for persistent storage of major
   device numbers, owners and permissions, then we probably want to
   also update it at shutdown time, if there have been any changes.

Since there already is a config file (which currently just takes the
default options for the "Boot: " prompt), it requires near zero code
to load multiple lines and to make the boot loader use only the first
one and the kernel all afte rthe first ...

> 	dev			Root of all device resources
> 	dev.isa			All isa devices
> 	dev.isa.ed		All resources for ed devices
> 	dev.isa.ed.0		Resources for ed0 device
> 	dev.isa.ed.0.port	Integer for first ioport
> 	dev.isa.ed.0.portsize	Integer for ioport range
> 	dev.isa.ed.0.mem	Memory region
> 	dev.isa.ed.0.memsize	Memory region size
> 	dev.isa.ed.0.irq	etc.

Sorry, I'd been carried away :)

Yes, this looks fine, but I'd rather create this tree by having a
bus dispatch function below "dev", then a driver dispatcher below
each bus (e.g. below "isa") and a unit disptacher for each driver.

For this I need a systcl function specific for the "dev" sysctl node,
then another one for each bus "driver", one for each device driver, 
and finally one for each device instance.

On each level you need a method that creates a directory of names
below it, and then iterate over those names.

> The probe (and attach) routine would get a handle to the node containing
> its resources:
> 
> edprobe(..., sysctl_node_t resources)
> {
> 	...
> 	port = sysctl_read_integer(resources, "port");
> 	sysctl_write_integer(resources, "portsize", ED_PORT_RANGE);
> }
> 
> Actually, now that I think about it, the resources should be more uniform:
> 
> 	dev.isa.ed.0.port.type = io
> 	dev.isa.ed.0.port.start = 0x3d0
> 	dev.isa.ed.0.port.size = 0x10
> 	dev.isa.ed.0.mem.type = mem
> 	dev.isa.ed.0.mem.start = 0xd0000
> 	dev.isa.ed.0.mem.size = 0x1000
> 	dev.isa.ed.0.irq.type = irq
> 	dev.isa.ed.0.irq.start = 5
> 	dev.isa.ed.0.irq.size = 1

[ Hmmm, this looks like you meant something else ...
Since you explicitly state the type, you probably want
to remove the ".port" and ".mem" nodes ??? ]

Well, as I said above, this may work for ISA, but in PCI you have
something like this:

	dev.pci.ncr.0.map.0.type = io
	dev.pci.ncr.0.map.0.base = 0xe400
	dev.pci.ncr.0.map.0.size = 0x100
	dev.pci.ncr.0.map.1.type = mem
	dev.pci.ncr.0.map.1.base = 0xfafef000
	dev.pci.ncr.0.map.1.size = 0x100
	dev.pci.ncr.0.map.2.type = mem
	dev.pci.ncr.0.map.2.base = 0xfafed000
	dev.pci.ncr.0.map.2.size = 0x1000

If I want to include PCI to PCI brdiges (for which nothing similar
exists on ISA :), I need completely different values, again, and I
really don't think we should even try to find a great unified 
representation. We should just have a generic procedure to create
the desired results ;-)

> Then one could write code like:
> 
> 	ports = sysctl_get_handle(resources, "port");
> 	sc->sc_port = sysctl_read_integer(ports, "start");
> 	sysctl_write_integer(ports, "size", real_port_range);
> 	if (error = resource_check(ports))
> 		giveup;

I'd rather stay with something like the isa_device structure for
ISA, and do it like this:

	int data[10];
	values = config_query ("isa", "ed", 0, 10, data);
	dev->iobase = data[CONF_ISA_IOBASE];
	dev->iosize = data[CONF_ISA_IOSIZE];
	...

In fact, I'd loop over all isa_device structures, and just request
those values for each (i.e. with dev->id_drv->name instead of "ed"
and dev->unit instead of the 0 in the example above ...)

> The resource_check function would figure out the type of the resource and
> act appropriately.
> 
> > 
> > 3) Driver specific data structure, values private per
> >    device instance:
> > 
> > 	- xxx_softc
> > 
> > 4) Common data structure, values private per driver but
> >    shared by all instances of this device type:
> > 
> > 	- drvname
> > 	- numunits
> > 	- maxunits
> > 	- probe()
> > 	- attach()
> > 	- shutdown()
> > 
> > Of course, the struct components are not meant to be a
> > complete list, just a few typical examples ...
> 
> I would add detach() to that list.  I want all drivers to be loadable
> *and* unloadable.  The detach() routine would be called when the driver is
> unloaded.

Sure, the list is far from complete. I just wanted to give examples 
of what goes into which category. A shutdown of the last device could
imply a detach, and I don't want to specify the contents of these
data structures, now, but I want to define the STRUCTURE of the data :)

> I just read through NetBSD's extent allocator (sys/kern/subr_extent.c) and
> it looks pretty useful.  You could certainly use it as the implementation
> of your resource functions.

I'll look into this file. Thanks for the pointer ...

Regards, STefan



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?19970514224514.26045>