Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 23 Feb 1997 19:09:25 -0800
From:      Julian Elischer <julian@whistle.com>
To:        "Brian J. McGovern" <mcgovern@spoon.beta.com>
Cc:        hackers@freebsd.org
Subject:   Re: Device driver cookbook.
Message-ID:  <33110665.167EB0E7@whistle.com>
References:  <199702221509.KAA12718@spoon.beta.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Brian J. McGovern wrote:
> 
> I promised I'd eventually get to it, and I'll be working on it heavily
> this weeknd. Below is the first "installment" where I walk through a simple
> pseudo-device driver that you can read from with a fixed buffer. Subsequent
> additions will make it more flexable. I'm releasing it to "hackers" now, so
> that everyone has a chance to comment, and let me know if they like/don't like/
> would like me to change anything. Thanks for the comments ahead of time.

the skeletons I checked in to /usr/share/examples do this as well.
you migh include a pointer..
(they also get the device filesystem parts correct etc.)

a comment.. 
if open checks the dev_t then read doesn't need to..
(one reason for open is to verify all thes sorts of things)
(why I think you shoul dinclude an open() )


> #include <sys/param.h>
> #include <sys/systm.h>
> #include <sys/ioctl.h>
> #include <sys/conf.h>
> #include <sys/uio.h>
> #include <sys/kernel.h>
> 

I'd just have hte driver there at teh bottom and not keep reiincluding
parts..
 just show the bit you are talking about...


> character device driver locally). What we will do now is #define CDEV_MAJOR
> to be equal to the number we just chose. This is one of those "standards" that
> just make sense. You'll find that doing it this way, rather than just
> hard-coding the major number, makes it easier to change later, as well as
> lets other developers know what to look for, in case they ever have to work
> on your driver. It'll also help stop typos, as well.
If I get off my rear end and finish devfs, then major numbers can be 
assigned dynamically
 (you give a major of -1 and it will do that now)


> 
> You'll notice that the string is declared static. This is so that it will
> not conflict with any other variables called "message" elsewhere in the
> kernel. You will see, rather quickly, that nearly all global variables,
> or exported functions, are declared static. 

which means that  they are not exported..
everything is referenced via the well know structs cdevsw and isa_driver

> 
> Now, we only have a few more things to set up for our device driver. Since
> we'll only be concerned about reading from our driver, we'll only concern
> ourselves explicitly with the read routine. We will "fake" the open
> and close routines for the time being, so that they will always succeed. Note,
> that this can cause problems, simply because they'll be able to open
> dozens of "foo" devices. However, we'll add some error checking to our first
> read routine to keep from selecting invalid minor numbers. Note, once we
> have a working open and close routine, we'll be able to move the error
> checking there, and out of the read routine completely.

so why put it there now? It's a bad habit to start..
get them used to having an open()..

> This is because the
> kernel does a certain amount of error checking ahead of time, and won't allow
> reads or writes to be performed on not-open minor numbers (more on this later).
> 
> One note on minor numbers. MINOR numbers are used to keep track of seperate
> entities within the same device driver. For instance, for serial devices
> (the sio driver), the first com port will have a minor number 0, the next,
> a minor number of 1, the next, a minor number of 2, and so on. This is how
> one logical device will be kept descrete from the others, even though their
> major numbers are the same. For our first device driver, we'll support only
> one minor device (minor number 0).

usually the NFOO number can be used to select how many minors will be
acceptable :)

you should also mention that interpretation of the minor numbers is 
TOTALLY up the the driver.


> 

> /* Define our routines in the proper format for a device-driver */
> static d_read_t fooread;

It's defined this wa for teh devsw entry..
this is really part of the block that declares the devsw struct 
and should be kept together with that.

> 

> One note on device drivers I've failed to touch on that suddenly has come
> to me. Device driver "names", in our case, "foo", can be anywhere from 1-4
> characters. 

nonsence.. it can be > 4.. there is no real limit..
it just fits in better if it's kept small as it gets concatinated with
 things all over the place.

>Most systems do not allow numbers (for reasons that will
> become obvious later). Once defined, all references to that device driver
> will use that name.
> 
>
=======

> /* Define our routines in the proper format for a device-driver */
> static d_read_t fooread;
> 
> /* Define the cdewsw structure for our driver */
> static struct cdevsw foo_cdevsw =
>   {
>     nullopen, nullclose, fooread, nxwrite,
>     nxioctl, nxstop, nxreset, nxdevtotty,
>     nxselect, nxmmap, NULL, "foo", NULL, -1
>   };

======
the above is one logical code block..

> 

>

>
> The next routine we'll write is the init routine, which is responsible for
> "starting up" our device driver. It can be as simple as stating the driver
> is loaded, or it can be very complex. In our case, since we're doing a
> pseudo-device driver, and not a regular character device driver, we won't
> have probe and attach routines to do the startup work, so we'll have to do
> it ourselves in the init routine. Our init routine will look something like:
> 
> static void fooinit(void *unused)
>   {
>     dev_t dev;
>     printf("foo0: Foo test character device driver installed and ready.\n");
>     printf("foo0: Configured for 1 device\n");
>     dev = makedev(CDEV_MAJOR,0);
>     cdevsw_add(&dev, &foo_cdevsw, NULL);

DEVFS code goes here too  :)

>   }
> 
> This routine basically will print out two lines of notification on boot up,
> and then get the device set up in the kernel.
> 
> The last thing we need to do is tell the kernel (from inside the driver) what
> routine has to be executed in order to get everything started, as well as give
> it a "priority" for loading. As an example of "priority", you can look
> at the Ethernet cards defined in the LINT kernel config file. You'll note
> it says not to change the order, in order to keep them from
> screwing up each other's probes... This is similar. Unless you have a very
> good reason to change it, I recommend to use the default priority. With this
> in mind, the following default line should work 99% of the time:
> 
> SYSINIT(foodev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR, &fooinit, NULL);
> 
> What does it all do? Even I don't know ;) But, it works, so use it.

the linker gathers all SYSINIT entries in all linked files into an array
at boot time the entries are sorted (a bubble sort in kern_main.c from
memory)
and the functions pointed to are run in the order specified.

this is a specific use of LINKER SETS
there are others in the kernel used for other reasons.
e.g adding protocol table entries etc.

I THINK they are in kernel.h 


> #include "foo.h"
> #if NFOO > 0
^^^^^^^^^^^^^^^
this is not needed becasue if NFOO were 0
the whole foo.c file wouldn't be in the Makefile
it's historical cruft.. (for hysterical raisons?)


> /* Include the header files that we'll need */

> Now, thats it for the driver. The next step is to go in to
> /usr/src/sys/i386/conf. I modified majors.i386 so that character device
> 20 was called "foo". This was not required, but if I had other developers
> working on this system, I'd want them to know I used 20 for my device
> driver, and its no longer available for them to use until I'm done with it,
> and change it back.
> 
> Secondly, you'll need to edit files.i386. Add the line

actually if it's not i386 specific it should be in
/sys/conf/files

> 
> dev/foo/foo.c           optional        foo     device-driver
> 
> someplace in the file. I usually do it about 25-30 lines down,
> where the similar looking lines start. Placement in the file is not critical,
> I just like to keep things looking consistent.
> 
> Almost there. Just a couple of more things. Next, create your kernel config
> file (getting real close now). Add the line:
> 
> pseudo-device foo 1
> 
> someplace to the file. This will let the kernel know you need the foo driver,
> and it will tell the driver it wants one device (more on this later when
> we support multiple devices).
>



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