Date: Thu, 30 Jan 1997 16:31:38 +1030 (CST) From: Michael Smith <msmith@atrad.adelaide.edu.au> To: mcgovern@spoon.beta.com (Brian J. McGovern) Cc: msmith@atrad.adelaide.edu.au, hackers@freebsd.org Subject: Device Drivers 101 Message-ID: <199701300601.QAA27658@genesis.atrad.adelaide.edu.au> In-Reply-To: <199701300505.AAA09649@spoon.beta.com> from "Brian J. McGovern" at "Jan 30, 97 00:05:43 am"
index | next in thread | previous in thread | raw e-mail
Brian J. McGovern stands accused of saying:
>
> >Driver initialisation is seperated into two parts, known as 'probe' and
> >'attach'. The purpose of the 'probe' routine is to ascertain whether
> >the hardware is present, and optionally determine its configuration.
>
> >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?
The structures are collated at link time into what is known as a 'linker
set'. This results in a statically-initialised array of all of the
isa_device structures which can be processed by the startup code.
The structure has to be non-static so that it is visible to the linker.
> Secondly, what are the fields after the first 3?
There is only one other field at the moment :
struct isa_driver {
int (*probe) __P((struct isa_device *idp));
/* test whether device is present */
int (*attach) __P((struct isa_device *idp));
/* setup driver for a device */
char *name; /* device name */
int sensitive_hw; /* true if other probes confuse us */
}
Drivers with sensitive_hw set to true are probed before others to avoid
being confused by other probes to the same space.
> 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?
#include <i386/isa/isa_device.h>, found in /sys/i386/isa/. As a
kernel-only header, it't not found under /usr/include.
> >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 size of the range of I/O ports that the device occupies. This is, as
has been observed elsewhere, a poor choice, as there are devices that
occupy no I/O ports, and others that occupy several disjoint ranges.
Pseudo-devices are handled differently. The 'vn' driver is a good place
to look for a readable example (/sys/dev/vn/vn.c), but the basic strategy
involves using the SYSINIT macro to register a startup function which
calls the pseudo-device's init function, which in turn uses [cb]devsw_add
and optionally devfs_add* to register the driver's existence.
(For "normal" devices, the bus probe code for the bus to which the
device belongs processes the linker set described above and calls the
probe/attach pairs.)
> >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?
Pseudo-devices don't get any config information. The isa_device structure
is initialised by the config file data, yes.
> >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 bus code is responsible for deciding whether a driver is in conflict;
drivers should ideally know nothing about the bus or other drivers.
> >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... I'm
> also assuming that block devices would call bdevsw_add (wrong name i think,
> but I think I get the idea).
The name is correct too 8)
> How about STREAMS types or tty type devices that are linked off
> through a line protocol?
No STREAMS in the BSD kernel 8). Tty devices are character devices;
there's extra state involved in interacting with the tty stack, which
is logically layered on top of the character driver.
This is a topic on which Bruce is best qualified to comment; I'm just
going to refer to sio.c and translate what I read into English 8)
> >Where CDEV_MAJOR is the major device number assigned to the driver
> >(normally #defined somewhere obvious).
> >
> >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?
I don't believe that there's a blank. The structure and the blanks are
listed in /sys/sys/conf.h. I should have been a little more thoughtful
about the no vs. null comment :
The no* versions return ENODEV, the null* versions always succeed but
do nothing, the nx* versions return ENXIO (but are a bad idea according
to the comment in sys/kern/subr_xxx.c where they are defined).
BSD doesn't have a poll syscall; 'similar' functionality is afforded by
select(2).
> 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?
It's not mandatory, but if you don't put a call in to create it, someone
else will 8)
Normally devfs node creation is conditionalised on DEVFS.
> >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?
Devfs doesn't really 'do' major and minor numbers; there's a logically
direct translation from the named node to your switch entry and the
unit number you supply when you create the node.
--
]] Mike Smith, Software Engineer msmith@gsoft.com.au [[
]] Genesis Software genesis@gsoft.com.au [[
]] High-speed data acquisition and (GSM mobile) 0411-222-496 [[
]] realtime instrument control. (ph) +61-8-8267-3493 [[
]] Unix hardware collector. "Where are your PEZ?" The Tick [[
help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199701300601.QAA27658>
