Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Jan 1997 00:43:08 -0800 (PST)
From:      Julian Elischer <julian@current1.whistle.com>
To:        "Brian J. McGovern" <mcgovern@spoon.beta.com>
Cc:        msmith@atrad.adelaide.edu.au, hackers@freebsd.org
Subject:   Re: The continuting email...
Message-ID:  <Pine.BSF.3.95.970130000413.13849A-100000@current1.whistle.com>
In-Reply-To: <199701300505.AAA09649@spoon.beta.com>

next in thread | previous in thread | raw e-mail | index | archive | help


On Thu, 30 Jan 1997, Brian J. McGovern wrote:

> >I'll restrict myself to ISA drivers, as these are where I'm most familiar.
> >PCI drivers are generally similar, but have an easier time in some
> >regards.
PCI drivers register themselves differently, and the PCI code recognises
the harware and calls the 'attach' routine directly.. the probe() routine
is redunant (yay!)
> 
> >I'll use your 'foo' driver as an example.
> 
>  ====
> 
> >Probe/attach for ISA device drivers is triggered by the presence of a
> >non-static isa_driver structure in the driver; at least the first three
> >fields should be initialised, with the probe and attach routines and the
> >name of the driver :
> 
> Ok. I know the what. Any particular reason it has to be non-static? I assume
> to cause it to blow up if there is another driver with the same name, but, 
> am I correct?
It has to be non static because the presence of the device in teh kernel 
configuration file will cause a reference to this structure to be
made in a generated .c file which will be linked into the kernel.
look in a kernel build directory (e.g. /sys/compile/GENERIC) for
the file ioconf.c . This file is generated from the kernel config file.

> 
> Secondly, what are the fields after the first 3? 
nope, that's all. In other BSD systems there are sometimes more

> 
> Also, I did a grep for "isa_driver" in /usr/include via a find (ie -
> grep "isa_driver" `find .` to no avail. Which header should I include?
in /sys/i386/isa/isa_device.h
> 
> >struct isa_driver foodriver = { fooprobe, fooattach, "foo"};
> 
> >The 'fooprobe' function is called during startup to determine whether
> >the device is present or not.  It should return zero if the probe
> >for the hardware failed, or the size of the I/O space occupied by
> >the device if the probe succeeded.
> 
> Please define "size of the I/O space". To me, this can mean many
> things, probably all of which are wrong. Is it the number of ports a 
> device uses? Amount of memory (shared or otherwise)? And how about
> our simulated pseudo device, which won't control hardware, but might
> have a few K in buffers?
The number of IO ports.
you may fill in the size of the shared memory buffer in the isa_device
structure (and other fields) as well

> 
> >static int
> >fooprobe(struct isa_device *dev)
> 
> >It is legitimate to alter the contents of the fields in the isa_device
> >structure, if new values are determined by probing for the hardware.
> >Note that the id_irq field is a bitmask, not a numeric value.  The
> >probe routine should not emit any text unless it comes up against
> >something particularly alarming.
> 
> Ok. id_irq is a bitmask. I'll have to save my other questions once I can
> find struct isa_device. I am currently assuming that this contains the
> info in the kernel config file when called. Same for a pseudo-device?
> 
> >
> >The probe routine is called once per instance of the driver in the
> >configuration file.
> >
> >Attach is called, again once per instance of the driver in the config,
> >when the device has been successfully probed and does not conflict.
> Does the driver make the call as to the conflict? Or does the system look
> at the struct isa_device, and if a conflict occurs, not call the probe
> and attach routines?
The kernel makes the call after the probe has checked the values it was
given and changed anything.. there is also (I seem to remember) a check
BEFORE that as well
> 
> >
> >static int
> >fooattach(struct isa_device *dev)
> >
> >The attach routine should build any local data structures required for
> >management of the device.  It is traditional for the attach routine to
> >emit a line like :
> >
> >foo0: Snarklewacker 200, rotating Floib, no BoBoBoBoB.
> >
> >The startup code will have already emitted a line like :
> >
> >foo0 at 0x10-20 irq 1 iomem 0x12345 on isa
> >
> Ok. This smells like the init routines I'm used to seeing.
> 
> 
> >Once internal state for the driver has been established, you add an entry
> >to the device switch for the driver.  In the case of a character device, 
> >the following fragment is normally used :
> >
> >    dev = makedev(CDEV_MAJOR,0);
> >    cdevsw_add(&dev,&foo_cdevsw,NULL);
> >
> 
> Ok. Looks clear enough. I'm assuming we're still in attach here...
Well, no..well, maybe..
this must be called exactly ONCE for each driver.
It is possible that the attach code is called more than that 
so the code needs to have "have I been here before" protection..
In many drivers there is no hardware so no attach function os called.
for an example look at mem.c
This driver uses a facility in the kernel called SYSINIT.
the SYSINIT facility allows an arbitrary function to specify where it is
to be called in the boot sequence.
Most drivers have a SYSINIT entry in addition to the attach/probe stuff.
and pseudo-device driver MUST have one.. The SYSINIT entry usually 
specifies an 'init' routine that is run ONCE and ONLY ONCE (unless you 
have two SYSINIT entries). This is the IDEAL place to put the cdevs_add()
calls as it will ensure that it happens once, before it's needed.
Note also that in some ways the 'init' code is exactly what would
need to be called to link in the driver had it been an LKM.
when all drivers use SYSINIT and all cdevsw/bdevsw entries are dynamic
and all interrupts are hooked in dynamically, then in effect
you will be able to load device drivers as LKMs with no extra work.
DEVFS gives you a view into this dynamic world.

> I'm 
> also assuming that block devices would call bdevsw_add (wrong name i think,
> but I think I get the idea).
no, it IS bdevsw_add.
notice that the bdevsw and cdevsw entries in your driver are THE ACTUAL
ENTRIES. In FreeBSD the cdevsw and bdevsw tables are an array of POINTERS,
and not an array of actual structs. The cdevsw_add() can be asked to
CHOOSE a major number for you if you don't have one in mind.
The number used is STORED IN THE cdevsw/bdevsw table entry, so examine
it afterwards to discover what you got. They also have crosspointers,
so given a cdev you  can quickly find the related bdev and visa versa.
(and also the driver 'name' for use in error messages etc. (e.g. 'foo')

> How about STREAMS typesi
BSD doesn't have streams, but if they did they would be initialised
through the same SYSINIT method (see /sys/kernel.h) (SI_SUB_DRIVERS)
as we don't have them I have no idea how you'd access them :)
> or tty type devices that
tty devices are just normal devices..
pty devices are pseudo devices and thus have a SYSINIT entry.

> are linked off through a line protocol? Once I'm done with driver foo,
> I'd like to start work on an ISA multi-modem card thats being prepped
> by Cisco for inclusion in to their routers (the modem modules, not the ISA
> cards). I'll need to run SLIP and PPP across the link. the _documentation I
> have_ says I'll have to make it a little more special than a "normal" character
> device...
why?

> 
> >Where CDEV_MAJOR is the major device number assigned to the driver
> >(normally #defined somewhere obvious).
Well Majors are assigned in /sys/i386/conf/majors (or something)
but that file isn't actually used..it's just for reference.
> >
> >A typical cdevsw initialisation might look like :
> >
> >static d_open_t         fooopen;
> >static d_close_t        fooclose;
> >static d_read_t         fooread;
> >static d_write_t        foowrite;
> >static d_ioctl_t        fooioctl;
> >static d_select_t       fooselect;
> >
> >#define CDEV_MAJOR      20
> >static struct cdevsw foo_cdevsw =
> >{
> >    fooopen,          fooclose,       fooread,        foowrite,
> >    fooioctl,         nullstop,       nullreset,      nodevtotty,
> >    fooselect,        nommap,         NULL,           driver_name,
> >    NULL, -1
> >};
> >
> 
> Ok. Some of it makes sense. Is there a blank generic one that gives the
> appropriate order? For instance, I see nullstop and nullreset. There should
> also be a poll routine in there some where? Is it a NULL? a no? a -1?
Poll is sysV there is no poll in BSD.. the similar entry is the 'select'
entry.

> >Note that some of the placeholders are "no*" and some are "null*" - I
> >think that this is laziness on someone's part 8(
> Again, see the note above. Docs on what they "should be" could help fix this :)
agreed.. it MIGHT be in conf.h
> 
> >
> >To create a devfs device node :
> >
> >	sc->devfs_token = devfs_add_devsw(&foo_cdevsw, unit,
> >					DEV_CHR, UID_ROOT, GID_WHEEL,
> >					0660, "foo%d", unit);
> >
> >This returns a token which is saved (here) in the devfs_token field in
> >the device's softc structure (the per-device state structure).
The reason for keeping the token is in case you want to removethe device.
you do so by supplying the token you were given to the devfs_??? function
(it's in the chapter 9 man page I believe)

> >The
> >cdevsw structure defines the driver's entrypoints, unit is the unit
> >number associated with the device node (you can encode major/minor
> >data here if you wish), DEV_CHR indicates a character device node, the
> >UID_ROOT, GID_WHEEL and 0660 entries set the ownership/permissions on
> >the new device node, and the remaining arguments are printf-style,
> >with a format string and parameters for the format string which yield
> >the name of the device node.
> >
> Ok. Is creating a devfs node mandatory? I know people would like to move to
> it, but when/is it required? What makes the decision if it is optional?
Well If I can get the bugs shaken out we should move to it asap :)

> 
> >You can call this several times to create multiple nodes for a single
> >instance of the driver.
> 
> Ok. I assume this means that it'll generate the same major numbers with
> appropriate minor numbers?
no, YOU supply a differnt minor number and name  each time..
this is effectively making entries in /dev for your driver.
you can also make links for devices that want different names.

how you interpret teh minor number is up to the driver
so only the driver can decide how to allocate them..

> 
> I appreciate the help to date. Again, my goal is not to argue, but to
> assist. Can't assist without knowledge, can't gain knowledge without
> documentation (I despise cultural learning. Too much pure data
> gets damaged in the retelling...)
> 


julian
(let me know more about this device and PPP
I may have some info for you on that too)
> 




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