Last updateed: $Date: 1999/08/11 14:57:29 $Here are examples of what we need.
sys/pci/cy_pci.c
.
diff -u
format)
sys/device.h
which is needed in newconfig mode.
#include <sys/systm.h> #include <sys/kernel.h> +#ifdef NEW_CONFIG +#include <sys/device.h> +#endif #include <vm/vm.h> #include <vm/pmap.h>
foo_probe
to
foo_pci_match
, and
foo_attach
to
foo_pci_attach
.
However, in case of a driver which is only for PCI specific device
and which has no share code with another bus based driver such as ISA,
we can omit a bus specific part of name such as _pci
because there are no ambiguities.
+#ifndef NEW_CONFIG static char *cy_probe __P((pcici_t, pcidi_t)); static void cy_attach __P((pcici_t, int)); +#else +static int cy_pci_match __P((struct device *, struct cfdata *, void *)); +static void cy_pci_attach __P((struct device *, struct device *, void *)); +#endif
cfattach
should be defined.
It works almost like struct pci_device
,
but former struct is common to any bus type.
cy_pci_softc
will be described later.
+#ifdef NEW_CONFIG +struct cy_pci_softc { + struct device cy_pci_dev; +}; + +struct cfattach cy_pci_ca = { + sizeof(struct cy_pci_softc), 1, NULL, cy_pci_match, cy_pci_attach, +}; +#elseA definition of struct
pci_device
and
a macro DATA_SET
which store the definition to the system
are removed by #ifdef
because it is no more required.
cy_pci
part of a name of the object of
struct cfattach
should be a "frontend name"
which is specified in "files" file for config.new
to read in.
"frontend name" will be described later.
In addition to it, names of softc structure, match function and
attach function might be composed from frontend name
to keep up a custom.
void *aux
,
and it casts to bus dependent structrue (in case of PCI, it is
struct pci_attach_args
).
-static char * -cy_probe(config_id, device_id) - pcici_t config_id; - pcidi_t device_id; +static int +cy_match (struct device *parent, struct cfdata *match, void *aux) { + struct pci_attach_args *pa = aux; + pcici_t config_id = pa->pa_tag; + pcidi_t device_id = pa->pa_type;
void *aux
then 1 is returned; otherwise 0 is returned
- return ("Cyclades Cyclom-Y Serial Adapter"); - return NULL; + return (1); + return (0);
void *aux
,
and it casts to bus dependent structrue (in case of PCI, it is
struct pci_attach_args
).
static void -cy_attach(config_id, unit) - pcici_t config_id; - int unit; +cy_attach (struct device *parent, struct device *self, void *aux) { + struct pci_attach_args *pa = aux; + pcici_t config_id = pa->pa_tag; + int unit = self->dv_unit;
+ printf (": Cyclades Cyclom-Y Serial Adapter\n") ioport = (u_int32_t) pci_conf_read(config_id, CY_PCI_BASE_ADDR1) & ~0x3;
sys/i386/isa/psm.c
.
diff -u
format)
sys/device.h
is should be included.
#include <sys/select.h> #include <sys/uio.h> +#ifdef NEW_CONFIG +#include <sys/device.h> +#endif
cfattach
should be defined.
It is also like the PCI example, but slightly differs.
To keep minimum change from old ISA code,
we provide two configuration ways.
One is a "newconfig" mode.
In "newconfig" mode,
match function and attach function are defined
as same as PCI device case.
Onother is a "ISA compatible" mode.
In "ISA compatible" mode,
old probe function and attach function are just used without any change.
In case of change of psm.c
,
we use "ISA compatible" mode.
+#ifdef NEW_CONFIG +struct psm_softc_new { + struct device psm_dev; +}; + +struct cfattach psm_ca = { + sizeof(struct psm_softc_new), 0, &psmdriver, isa_dev_match, NULL, +}; +#endifIn "ISA compatible" mode, a pointer to struct
isa_driver
is
assigned to
ca_aux
member of struct cfattach
.
0 is also assigned to ca_level
member of it.
isa_dev_match
, which is common match function to
all "ISA compatible" mode, is assigned to match function member.
NULL is assigned to attach function member because it is not used.
ISA compatible mode is makeshift;
all ISA driver is expected to rewrite in newconfig mode.
Here, I explain how to rewrite from "ISA compatible mode" to
"newconfig mode" with the case of
sys/i386/isa/psm.c
.
Note that along with this rewriting, softc structure also needed to rewrite to use the one reserved by newconfig. So, most of the parts that access softc structure need rewriting. These rewriting are ommited here. Please see "softc structure" section for details.
diff -u
format)
ioconf.h
which is needed in newconfig mode.
+#ifdef NEW_CONFIG +#include "ioconf.h" +#endif /* NEW_CONFIG */
struct device
member in the begining of definition.
In ISA comapatible mode psm.c
, pointers of softc structures
are prepared statically as an array of length NPSM
,
which is not suitable for newconfig mode.
Please see "softc structure" section for details.
/* driver control block */ -static struct psm_softc { /* Driver status information */ +struct psm_softc { /* Driver status information */ +#ifdef NEW_CONFIG + struct device psm_dev; /* device generic information */ +#endif : -} *psm_softc[NPSM]; +}; +#ifndef NEW_CONFIG +static struct psm_softc *psm_softc[NPSM]; +#endif
/* function prototypes */ +#ifndef NEW_CONFIG static int psmprobe __P((struct isa_device *)); static int psmattach __P((struct isa_device *)); +#endif
isa_driver
which is not needed in newconfig mode.
+#ifndef NEW_CONFIG /* device driver declarateion */ struct isa_driver psmdriver = { psmprobe, psmattach, "psm", FALSE }; +#endif
cf_level In ISA compatible mode 0, in newconfig mode 1 ca_aux In newconfig modeNULL
ca_match Match function in newconfig mode ca_attach Attach function in newconfig mode ca_detach NormallyNULL
ca_activate NormallyNULL
#ifdef NEW_CONFIG -struct psm_softc_new { - struct device psm_dev; -}; +static int psm_match __P((struct device *, struct cfdata *, void *)); +static void psm_attach __P((struct device *, struct device *, void *)); +static int psm_init __P((struct psm_softc *, int, int)); struct cfattach psm_ca = { - sizeof(struct psm_softc_new), 0, &psmdriver, isa_dev_match, NULL, NULL, + sizeof(struct psm_softc), 1, NULL, psm_match, psm_attach, NULL,NULL, }; #endif
/* psm driver entry points */ +#ifdef NEW_CONFIG + +#define endprobe(v) { if (bootverbose) \ + --verbose; \ + kbdc_set_device_mask(sc->kbdc, mask); \ + kbdc_lock(sc->kbdc, FALSE); \ + return (v); \ + } +#else + #define endprobe(v) { if (bootverbose) \ --verbose; \ kbdc_set_device_mask(sc->kbdc, mask); \ @@ -728,17 +761,28 @@ free(sc, M_DEVBUF); \ return (v); \ } +#endif
Rewrite psmprobe() and psmattach() make psm_match() and psm_attach(). Note, previous psmprobe() not only probe the device, but also initializes the device and softc structure, which psmattach() depends on. This not suitable for newconfig style as in the section "precautions". So rewite psmprobe() as initializing function psm_init(). Then call psm_init() from psm_match() and psm_attach() to initialize the device and the softc structure.
The softc structure is allocated by newconfig, so delete and
rewrite lines for malloc().
Accompanying with the deletion of static array of softc structure,
delete lines that check of validation with NPSM
constant.
+#ifdef NEW_CONFIG +static int +psm_init(struct psm_softc *sc, int iobase, int flags) +#else static int psmprobe(struct isa_device *dvp) +#endif { +#ifdef NEW_CONFIG + int unit = sc->psm_dev.dv_unit; /* XXX */ +#else int unit = dvp->id_unit; struct psm_softc *sc; +#endif int stat[3]; int command_byte; int mask; int i; +#ifndef NEW_CONFIG /* validate unit number */ if (unit >= NPSM) return (0); @@ -749,13 +793,20 @@ if (sc == NULL) return (0); bzero(sc, sizeof *sc); +#endif #if 0 kbdc_debug(TRUE); #endif +#ifdef NEW_CONFIG + sc->addr = iobase; + sc->kbdc = kbdc_open(sc->addr); + sc->config = flags & PSM_CONFIG_FLAGS; +#else sc->addr = dvp->id_iobase; sc->kbdc = kbdc_open(sc->addr); sc->config = dvp->id_flags & PSM_CONFIG_FLAGS; +#endif sc->flags = 0; if (bootverbose) ++verbose; @@ -764,7 +815,9 @@ printf("psm%d: unable to lock the controller.\n", unit); if (bootverbose) --verbose; +#ifndef NEW_CONFIG free(sc, M_DEVBUF); +#endif return (0); } @@ -978,20 +1031,56 @@ } /* done */ +#ifdef NEW_CONFIG + kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); + kbdc_lock(sc->kbdc, FALSE); + return (1); +#else psm_softc[unit] = sc; kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS); kbdc_lock(sc->kbdc, FALSE); return (IO_PSMSIZE); +#endif +}
Write psm_match() function that calls psm_init().
The softc structure is not transfered to match function, which leads to the match function cannot access softc structure. That needs some work arounds.
sc_sensitive
field in isa_softc structure of the parent
controls the order of ISA probe. For the sensitive hardware devices,
if (!isa_sc->sc_sensitive) return (0);Otherwise,
if (isa_sc->sc_sensitive) return (0);
+#ifdef NEW_CONFIG +static int +psm_match(struct device *parent, struct cfdata *cf, void *aux) +{ + struct psm_softc dummy; + struct isa_softc *isa_sc = (struct isa_softc *)parent; + struct isa_attach_args *ia = (struct isa_attach_args *)aux; + int v; + + if (isa_sc->sc_sensitive) + return (0); /* psm is not sensitive device */ + bzero(&dummy, sizeof dummy); + ia->ia_compat.id_alive = IO_PSMSIZE; + + v = psm_init(&dummy, ia->ia_iobase, ia->ia_flags); + return (v); }
Rewrite psmattach() as psm_attach().
In newconfig mode, the intterupt is registrated in attach() function.
The registration is done calling isa_register_intr()
function.
isa_register_intr()
is written in
sys/i386/isa/isa.c
.
Arguments of isa_register_intr()
are as follows:
int
irq; Interrupt number (Note: this is not bit vector).int
*mp; Pointer to interupt mask. Following the imask {tty, bio, net, cam} of the device line in old config file, write one of pointers: tty_imask bio_imask net_imask cam_imask If there is no imask in the device line write NULL.u_int
flags; If there is `FAST' interrupt flag, write RI_FAST. This corresponds to the id_ri_flags field of old struct isa_device, which is mostly used RS-232C drivers(ex. sio). Otherwise, write `0'.inthand2_t
handler; Pointer to the interrupt handler function (psm_intr()).void*
arg; Argument that intterrupt handler is called. Write the pointer to the softc structure.
+static void +psm_attach(struct device *parent, struct device *self, void *aux) +#else static int psmattach(struct isa_device *dvp) +#endif { +#ifdef NEW_CONFIG + struct psm_softc *sc = (struct psm_softc *)self; + struct isa_attach_args *ia = (struct isa_attach_args *)aux; + + psm_init(sc, ia->ia_iobase, ia->ia_flags); + isa_register_intr(ia->ia_irq, &tty_imask, 0, psm_intr, (void *)sc); +#else int unit = dvp->id_unit; struct psm_softc *sc = psm_softc[unit]; if (sc == NULL) /* shouldn't happen */ return (0); +#endif /* Setup initial state */ sc->state = PSM_VALID; @@ -1007,7 +1096,7 @@ #endif /* DEVFS */ #ifdef NEW_CONFIG - printf(": "); + printf(": "); #endif /* NEW_CONFIG */ #ifdef PSM_HOOKAPM @@ -1053,7 +1142,9 @@ if (bootverbose) --verbose; +#ifndef NEW_CONFIG return (1); +#endif /* NEW_CONFIG */ }
+#ifdef NEW_CONFIG +void +psm_intr(void *arg) +#else /* NEW_CONFIG */ void psmintr(int unit) +#endif /* NEW_CONFIG */ { /* * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN) @@ -1714,7 +1826,11 @@ MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN, MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN }; +#ifdef NEW_CONFIG + struct psm_softc *sc = (struct psm_softc *)arg; +#else /* NEW_CONFIG */ register struct psm_softc *sc = psm_softc[unit]; +#endif /* NEW_CONFIG */ mousestatus_t ms; int x, y, z; int c;
When new driver added,
file names of the driver of source codes should be added to
"files" file.
Currently, there are two "files" file.
One is a /sys/conf/files.newconf
and another is a /sys/i386/conf/files.i386.newconf
.
The former is for a machine independent driver,
and the later is for a machine dependent driver.
Device drivers which souce code locates under
/sys/i386
might be registered later,
and others might be registerd former.
How to register source codes of the device driver is related to following syntax. (I will skip detail explanations. Please refer to a paper of Mr. Chiris Torek)
device
syntax.
Device driver to register should be described.
In case of sharing same driver for many buses,
only one driver should be described.
The syntax is following:
device device [{locators}] [: attrs]
Here, device is a device name.
locators is a list of locators,
which relates a bus that the device controls.
It is used in bus type device such as isa
and it is normally unused in many other devices.
attrs is a attirubtes of the device.
attach
syntax.
It should describe a bus which the device driver is attached to.
In case of sharing same driver for many buses,
all buses should be described.
The syntax is following:
attach device at parent [with name] [: attrs]
Here,device is a device name.
parent is a parent device name.
name is a frontend name of the device.
Normally, frontend name is used to distinguish each other
in case of sharing same driver for may buses.
A name of struct cfattach
of the device driver
is composed from the frontend name.
On the other hand, a name of struct cfdriver
is composed from the device name.
If the device is used for only one device,
then one can omit
[with name]
part.
In this case, a frontend name is a device name.
attrs is a attributes of the device.
file
syntax.
It should describe file names of source codes of the device driver.
The syntax is following:
file path fopts [needs-flag | needs-count]
Here, path is a relative path name of the source file.
fopts is a compile condition.
If the condition is satisfied then the source file is compiled.
A compile condition is a device name or a option.
One can apply logical operation to the conpile conditions.
For example, if pci/hoge.c
is a common module
used by foo device and bar device,
then we can write;
file pci/hoge.c foo | bar
needs-count
is described if the driver includes
a device number header file
(which #define
s a constant NED
that is the number of driver instance of the driver ed
)
If one uses needs-count
then one
cannot describe such as
ed* at pci? ...
In this description, the number of the driver instance
is not clear in compile time, and it is not fixed
until the OS is booted.
needs-flag
is like needs-count
but differs in a point that it #define
s a
constant 1 or 0 according to whether the driver exists
in a config file or not rather than the number of the
driver instance.
Even if one uses needs-flag
then
one can specifiy "*" driver in config file.
Generally speaking, a device driver needs internal work variables for each driver units. These variables are gathered up to a structure, and are initialized when the device driver is attached. The structure is called "softc structure".
In old FreeBSD config scheme, the device driver is responsible for initialization and allocation of a softc structure. In newconfig scheme, these are performed by the configuration framework rather than the device deriver. In order to let the framework know a size of a softc structure to be allocated, the size should be described in cfattach structure.
In newconfig scheme,
head part of a softc structure is used by the framework internally.
Because of this, head part of any device driver
should be a element of device
structure.
Of cause, the size of the softc structure to let the framework know
is equal or larger than the size of the softc structure itself.
Newconfig is designed as the framework allocates a softc structure.
Because each driver allocates a softc structure
in the traditional FreeBSD framework,
we may use old softc structure in first step of the porting,
after it is changed to use a softc structure which is allocated by
the framework.
In this case, there should be device
structure
at least for the newconfig framework.
We can access to a softc structure allocated by newconfig by following way:
foo_cd.cd_devs[unit]
.
Here, foo_cd
is a variable of
cfdriver
structure and
generated config.new
automatically.
So as to acess foo_cd
,
you need to #include
"ioconf.h
"
in which it is declared.
self
of the function to a pointer to a softc structure
of each driver.
First member of a softc structure is must be device
structure which is used by the framework,
so a pointer to it is just a pointer to the softc structure
(if casted properly).
In newconfig framework, you should be care about following points:
Generally speaking, an attach function is not always called just after a match function is called. In a bus code, after every soft of match function are called, the best attach function is called depending on the return value of all match functions. So you should avoid side effect operation in a match function. An attach function should not depend on a status caused by a match function. If you need a kind of initialization of a device, you should also initialize in an attach function.
In a device which is a controller of another bus, an attach function should call a initialization code of the bus. Newconfig framework defines how to call a initialization code of the bus, so you need to change an attach function. It includes SCSI bus, IDE bus(wdc), parallel port bus(nlpt) and so. In device drivers of these bus controller devices, you need to modify a code which initialize bus. Until the bus controller code is implemented, we can not use a driver under the bus.