Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 15 May 1997 10:07:18 +0100 (BST)
From:      Doug Rabson <dfr@nlsystems.com>
To:        Stefan Esser <se@freebsd.org>
Cc:        Michael Smith <msmith@atrad.adelaide.edu.au>, current@freebsd.org
Subject:   Re: Backwards compatibiliy for isa_driver
Message-ID:  <Pine.BSF.3.95q.970515090922.22139I-100000@herring.nlsystems.com>
In-Reply-To: <19970514224514.26045@x14.mi.uni-koeln.de>

next in thread | previous in thread | raw e-mail | index | archive | help
On Wed, 14 May 1997, Stefan Esser wrote:

> 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).

2) would be my preference.  If the data is free-form, then fields can be
added without breaking existing (binary) drivers.

> 
> 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.

Exactly.

> 
> 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).

How about this:

#define ISA_RESOURCE_PORTS	0
#define ISA_RESOURCE_MEMORY	1
#define ISA_RESOURCE_IRQ	2
#define ISA_RESOURCE_DRQ	3

#define PCI_RESOURCE_MAP0	0
#define PCI_RESOURCE_MAP1	1
...


enum resource_type {
	RESOURCE_TYPE_PORT,
	RESOURCE_TYPE_MEMORY,
	RESOURCE_TYPE_IRQ,
	RESOURCE_TYPE_DRQ
};

struct resource {
	enum resource_type	type;
	u_int			start;
	u_int			size;
};

struct bus_ops {
	...
	struct resource *(*get_resource)(struct bus *bus, void *instance,
					 int which);
	int *(*map_resource)(struct bus *bus, void *instance, int which);
	int *(*unmap_resource)(struct bus *bus, void *instance,
			       int which);
	int *(*change_resource(struct bus *bus, void *instance, int which,
			       struct resource *);
}

struct bus {
	...
	struct bus_ops	*ops;
};

int edprobe(struct bus *bus, void *instance)
{
	struct resource *ports;
	struct resource *memory;
	struct resource *irq;

	ports = bus->ops->get_resource(bus, instance, ISA_RESOURCE_PORTS);
	memory  = bus->ops->get_resource(bus, instance, 
					ISA_RESOURCE_MEMORY);
	irq = bus->ops->get_resource(bus, instance, ISA_RESOURCE_IRQ);
	ports->size = 0x10;
	bus->ops->change_resource(bus, instance, ISA_RESOURCE_PORTS);

	bus->ops->map_resource(bus, instance, ISA_RESOURCE_PORTS);
	bus->ops->map_resource(bus, instance, ISA_RESOURCE_MEMORY);
	bus->ops->map_resource(bus, instance, ISA_RESOURCE_IRQ);

	probe the device.

	if (probe failed) {
		bus->ops->unmap_resource(bus, instance,
					ISA_RESOURCE_PORTS);
		bus->ops->unmap_resource(bus, instance,
					ISA_RESOURCE_MEMORY);
		bus->ops->unmap_resource(bus, instance, ISA_RESOURCE_IRQ);
	}
}

> 
> 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 ...

Each bus and each bridge could provide its own ops vector.  Each device
attached to a bus would have a set of resources.  The instance pointer
above is a handle to the device resources.

> 
> > 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:
>
> [examples snipped]
> 
>    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.

Sounds good.  How far have you got, implementation wise?

> 
> 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 ...).
> 

Shouldn't this be:

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

Translating to english:

	Data for sd, unit zero, type is character device
		(major=13,minor=0,...)
	Data for sd, unit zero, type is block device
		(major=4,minor=0,...)

It seems to me that the type here is really 'char' or 'block', not 'sd'.
This way, the knowledge of the data format (major, minor, mode etc) is
encoded by a single piece of code which understands the 'char' and 'block'
type.

If the type is the driver name, then all drivers would need to understand
this data format.  In addition, wouldn't it prevent drivers from having
their own private config data:

	psm:0:isa:0x60:0:12:...
	psm:0:char:21:0:0600:0:0
	psm:0:char:21:1:0600:0:0
	psm:0:psm:acceleration

> 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 ...
> 
> > 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

I think I was drifting in the direction of the bus/instance/resourceid
discussion above.  The idea was that each resource is represented in the
sysctl tree by a triple (type, start, size) and that the same resource
triples could be used by both isa and pci devices.  For probe-able busses,
the resource triples could be generated by the bus probe, otherwise
extracted from config data.

> 
> 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 ...)

That works for me.  I think we should still have compiled in data for isa
devices in ioconf.c for the case when the config file is missing or
doesn't contain an item for the particular device instance required.

	if (values = config_query(...))) {
		dev->iobase = ...
	}

--
Doug Rabson				Mail:  dfr@nlsystems.com
Nonlinear Systems Ltd.			Phone: +44 181 951 1891




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.3.95q.970515090922.22139I-100000>