Skip site navigation (1)Skip section navigation (2)
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"

next in thread | previous in thread | raw e-mail | index | archive | help
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  [[



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