diff options
author | Murray Stokely <murray@FreeBSD.org> | 2000-11-06 10:22:11 +0000 |
---|---|---|
committer | Murray Stokely <murray@FreeBSD.org> | 2000-11-06 10:22:11 +0000 |
commit | 87fddebcea2e98040616614fa335de1320279011 (patch) | |
tree | 8f84235a5a7d6f49b7784b123b034aa565460a5a /en_US.ISO_8859-1 | |
parent | 3c78aa06a4c09ecd8069ab7b6f1933e39fdfb938 (diff) | |
download | doc-87fddebcea2e98040616614fa335de1320279011.tar.gz doc-87fddebcea2e98040616614fa335de1320279011.zip |
First significant batch of content for the Developer's Handbook.
* Added introductory content for device driver chapter.
* Included some example pseudo device drivers with (not-enough) commentary.
* Added new chapters to the framework
* Included pointers to more information in some of the unwritten chapters.
Notes
Notes:
svn path=/head/; revision=8296
Diffstat (limited to 'en_US.ISO_8859-1')
-rw-r--r-- | en_US.ISO_8859-1/books/developers-handbook/book.sgml | 628 |
1 files changed, 622 insertions, 6 deletions
diff --git a/en_US.ISO_8859-1/books/developers-handbook/book.sgml b/en_US.ISO_8859-1/books/developers-handbook/book.sgml index f490578976..bf8ae4a56a 100644 --- a/en_US.ISO_8859-1/books/developers-handbook/book.sgml +++ b/en_US.ISO_8859-1/books/developers-handbook/book.sgml @@ -1,7 +1,7 @@ <!-- The FreeBSD Documentation Project - $FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.2 2000/10/03 07:34:34 murray Exp $ + $FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/book.sgml,v 1.3 2000/10/06 15:36:45 phantom Exp $ --> <!DOCTYPE BOOK PUBLIC "-//FreeBSD//DTD DocBook V3.1-Based Extension//EN" [ @@ -317,12 +317,609 @@ <part id="devicedrivers"> <title>Device Drivers</title> - <chapter id="oldbsddriver"> - <title>4.4BSD Driver Writing</title> + <chapter id="driverbasics"> + <title>Writing FreeBSD Device Drivers</title> + + <para>This chapter was written by Murray Stokely with selections from + a variety of sources including the intro(4) man page by Joerg + Wunsch.</para> + + <sect1> + <title>Introduction</title> + <para> + This chapter provides a brief introduction to writing device + drivers for FreeBSD. A device in this context is a term used + mostly for hardware-related stuff that belongs to the system, + like disks, printers, or a graphics display with its keyboard. + A device driver is the software component of the operating + system that controls a specific device. There are also + so-called pseudo-devices where a device driver emulates the + behaviour of a device in software without any particular + underlying hardware. Device drivers can be compiled into the + system statically or loaded on demand through the dynamic + kernel linker facility `kld'.</para> + + <para>Most devices in a Unix-like operating system are + accessed through device-nodes, sometimes also called special + files. These files are usually located under the directory + <filename>/dev</filename> in the file system hierarchy. Until + devfs is fully integrated into FreeBSD, each device node must + be created statically and independent of the existence of the + associated device driver. Most device nodes on the system are + created by running <command>MAKEDEV</command>.</para> + + <para>Device drivers can roughly be broken down into three + categories; character (unbuffered), block (buffered), and + network drivers.</para> + </sect1> + + <sect1> + <title>Dynamic Kernel Linker Facility - KLD</title> + <para>The kld interface allows system administrators to + dynamically add and remove functionality from a running + system. This allows device driver writers to load their new + changes into a running kernel without constantly rebooting to + test changes.</para> + + <para>The kld interface is used through the following + administrator commands : + <itemizedlist> + <listitem><simpara><command>kldload</command> - loads a new kernel + module</simpara></listitem> + <listitem><simpara><command>kldunload</command> - unloads a kernel + module</simpara></listitem> + <listitem><simpara><command>kldstat</command> - lists the currently loadded + modules</simpara></listitem> + </itemizedlist> + </para> + + <para>Skeleton Layout of a kernel module</para> +<programlisting> +/* + * KLD Skeleton + * Inspired by Andrew Reiter's Daemonnews article + */ + +#include <sys/types.h> +#include <sys/module.h> +#include <sys/systm.h> /* uprintf */ +#include <sys/errno.h> +#include <sys/param.h> /* defines used in kernel.h */ +#include <sys/kernel.h> /* types used in module initialization */ + +/* + * Load handler that deals with the loading and unloading of a KLD. + */ + +static int +skel_loader(struct module *m, int what, void *arg) +{ + int err = 0; + + switch (what) { + case MOD_LOAD: /* kldload */ + uprintf("Skeleton KLD loaded.\n"); + break; + case MOD_UNLOAD: + uprintf("Skeleton KLD unloaded.\n"); + break; + default: + err = EINVAL; + break; + } + return(err); +} + +/* Declare this module to the rest of the kernel */ + +DECLARE_MODULE(skeleton, skel_loader, SI_SUB_KLD, SI_ORDER_ANY); +</programlisting> + + + <sect2> + <title>Makefile</title> + <para>FreeBSD provides a makefile include that you can use + to quickly compile your kernel addition.</para> + <programlisting> +SRCS=skeleton.c +KMOD=skeleton + +.include <bsd.kmod.mk> +</programlisting> + + + <para>Simply running <command>make</command> with + this makefile will create a file + <filename>skeleton.ko</filename> that can be loaded into + your system by typing : +<screen> +&prompt.root kldload -v ./skeleton.ko +</screen> + </para> + </sect2> + </sect1> + + <sect1> + <title>Accessing a device driver</title> + <para>Unix provides a common set of system calls for user + applications to use. The upper layers of the kernel dispatch + these calls to the corresponding device driver when a user + accesses a device node. The <command>/dev/MAKEDEV</command> + script makes most of the device nodes for your system but if + you are doing your own driver development it may be necessary + to create your own device nodes with <command>mknod</command> + </para> + + <sect2> + <title>Creating static device nodes</title> + <para>The <command>mknod</command> command requires four + arguments to create a device node. You must specify the + name of this device node, the type of device, the major number + of the device, and the minor number of the device.</para> + </sect2> + + <sect2> + <title>Dynamic device nodes</title> + <para>The device filesystem, or devfs, provides access to the + kernel's device namespace in the global filesystem namespace. + This eliminates the problems of potentially having a device + driver without a static device node, or a device node without + an installed device driver. Unfortunately, devfs is still a + work in progress.</para> + </sect2> + + </sect1> + + <sect1> + <title>Character Devices</title> + <para>A character device driver is one that transfers data + directly to and from a user process. This is the most common + type of device driver and there are plenty of simple examples + in the source tree.</para> + <para>This simple example pseudo-device remembers whatever values you write + to it and can then supply them back to you when you read from + it.</para> +<programlisting> +/* + * Simple `echo' pseudo-device KLD + * + * Murray Stokely + */ + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#include <sys/types.h> +#include <sys/module.h> +#include <sys/systm.h> /* uprintf */ +#include <sys/errno.h> +#include <sys/param.h> /* defines used in kernel.h */ +#include <sys/kernel.h> /* types used in module initialization */ +#include <sys/conf.h> /* cdevsw struct */ +#include <sys/uio.h> /* uio struct */ +#include <sys/malloc.h> + +#define BUFFERSIZE 256 + +/* Function prototypes */ +d_open_t echo_open; +d_close_t echo_close; +d_read_t echo_read; +d_write_t echo_write; + +/* Character device entry points */ +static struct cdevsw echo_cdevsw = { + echo_open, + echo_close, + echo_read, + echo_write, + noioctl, + nopoll, + nommap, + nostrategy, + "echo", + 33, /* reserved for lkms - /usr/src/sys/conf/majors */ + nodump, + nopsize, + D_TTY, + -1 +}; + +typedef struct s_echo { + char msg[BUFFERSIZE]; + int len; +} t_echo; + +/* vars */ +static dev_t sdev; +static int len; +static int count; +static t_echo *echomsg; + +MALLOC_DECLARE(M_ECHOBUF); +MALLOC_DEFINE(M_ECHOBUF, "echobuffer", "buffer for echo module"); + +/* + * This function acts is called by the kld[un]load(2) system calls to + * determine what actions to take when a module is loaded or unloaded. + */ + +static int +echo_loader(struct module *m, int what, void *arg) +{ + int err = 0; + + switch (what) { + case MOD_LOAD: /* kldload */ + sdev = make_dev(<literal>&</literal>echo_cdevsw, + 0, + UID_ROOT, + GID_WHEEL, + 0600, + "echo"); + /* kmalloc memory for use by this driver */ + /* malloc(256,M_ECHOBUF,M_WAITOK); */ + MALLOC(echomsg, t_echo *, sizeof(t_echo), M_ECHOBUF, M_WAITOK); + printf("Echo device loaded.\n"); + break; + case MOD_UNLOAD: + destroy_dev(sdev); + FREE(echomsg,M_ECHOBUF); + printf("Echo device unloaded.\n"); + break; + default: + err = EINVAL; + break; + } + return(err); +} + +int +echo_open(dev_t dev, int oflags, int devtype, struct proc *p) +{ + int err = 0; + + uprintf("Opened device \"echo\" successfully.\n"); + return(err); +} + +int +echo_close(dev_t dev, int fflag, int devtype, struct proc *p) +{ + uprintf("Closing device \"echo.\"\n"); + return(0); +} + +/* + * The read function just takes the buf that was saved via + * echo_write() and returns it to userland for accessing. + * uio(9) + */ + +int +echo_read(dev_t dev, struct uio *uio, int ioflag) +{ + int err = 0; + int amt; + + /* How big is this read operation? Either as big as the user wants, + or as big as the remaining data */ + amt = MIN(uio->uio_resid, (echomsg->len - uio->uio_offset > 0) ? echomsg->len - uio->uio_offset : 0); + if ((err = uiomove(echomsg->msg + uio->uio_offset,amt,uio)) != 0) { + uprintf("uiomove failed!\n"); + } + + return err; +} + +/* + * echo_write takes in a character string and saves it + * to buf for later accessing. + */ + +int +echo_write(dev_t dev, struct uio *uio, int ioflag) +{ + int err = 0; + + /* Copy the string in from user memory to kernel memory */ + err = copyin(uio->uio_iov->iov_base, echomsg->msg, MIN(uio->uio_iov->iov_len,BUFFERSIZE)); + + /* Now we need to null terminate */ + *(echomsg->msg + MIN(uio->uio_iov->iov_len,BUFFERSIZE)) = 0; + /* Record the length */ + echomsg->len = MIN(uio->uio_iov->iov_len,BUFFERSIZE); + + if (err != 0) { + uprintf("Write failed: bad address!\n"); + } + + count++; + return(err); +} + +DEV_MODULE(echo,echo_loader,NULL); +</programlisting> + +<para>To install this driver you will first need to make a node on + your filesystem with a command such as : </para> +<screen> + &prompt.root mknod /dev/echo c 33 0 +</screen> +<para>With this driver loaded you should now be able to type something + like :</para> +<screen> + &prompt.root echo -n "Test Data" > /dev/echo + &prompt.root cat /dev/echo + Test Data +</screen> + <para>Real hardware devices in the next chapter..</para> + + <para>Additional Resources + <itemizedlist> + <listitem><simpara><ulink + url="http://www.daemonnews.org/200010/blueprints.html">Dynamic + Kernel Linker (KLD) Facility Programming Tutorial</ulink> - + <ulink url="http://www.daemonnews.org">Daemonnews</ulink> October 2000</simpara></listitem> + <listitem><simpara><ulink + url="http://www.daemonnews.org/200007/newbus-intro.html">How + to Write Kernel Drivers with NEWBUS</ulink> - <ulink + url="http://www.daemonnews.org">Daemonnews</ulink> July + 2000</simpara></listitem> + </itemizedlist> + </para> + </sect1> + + <sect1> + <title>Block Devices</title> + <para>A block device driver transfers data to and from the + operating system's buffer cache. They are solely intended to + layer a file system on top of them. For this reason they are + normally implemented for disks and disk-like devices only.</para> + + <para>Example test data generator ... </para> + + <para>Example ramdisk device ... </para> + + <para>Real hardware devices in the next chapter..</para> + </sect1> + + <sect1> + <title>Network Drivers</title> + <para>Drivers for network devices do not use device nodes in + ord to be accessed. Their selection is based on other + decisions made inside the kernel and instead of calling + open(), use of a network device is generally introduced by + using the system call socket(2).</para> + <para>man ifnet(), loopback device, Bill Pauls drivers, etc..</para> + </sect1> + + </chapter> + + <chapter id="pci"> + <title>PCI Devices</title> + + <para>This chapter will talk about the FreeBSD mechanisms for + writing a device driver for a device on a PCI bus.</para> + + <sect1><title>Probe and Attach</title> + + <para>Information here about how the PCI bus code iterates + through the unattached devices and see if a newly loaded kld + will attach to any of them.</para> +<programlisting> +/* + * Simple KLD to play with the PCI functions. + * + * Murray Stokely + */ + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#include <sys/types.h> +#include <sys/module.h> +#include <sys/systm.h> /* uprintf */ +#include <sys/errno.h> +#include <sys/param.h> /* defines used in kernel.h */ +#include <sys/kernel.h> /* types used in module initialization */ +#include <sys/conf.h> /* cdevsw struct */ +#include <sys/uio.h> /* uio struct */ +#include <sys/malloc.h> +#include <sys/bus.h> /* structs, prototypes for pci bus stuff */ + +#include <pci/pcivar.h> /* For get_pci macros! */ + +/* Function prototypes */ +d_open_t mypci_open; +d_close_t mypci_close; +d_read_t mypci_read; +d_write_t mypci_write; + +/* Character device entry points */ + +static struct cdevsw mypci_cdevsw = { + mypci_open, + mypci_close, + mypci_read, + mypci_write, + noioctl, + nopoll, + nommap, + nostrategy, + "mypci", + 36, /* reserved for lkms - /usr/src/sys/conf/majors */ + nodump, + nopsize, + D_TTY, + -1 +}; + +/* vars */ +static dev_t sdev; + +/* We're more interested in probe/attach than with + open/close/read/write at this point */ + +int +mypci_open(dev_t dev, int oflags, int devtype, struct proc *p) +{ + int err = 0; + + uprintf("Opened device \"mypci\" successfully.\n"); + return(err); +} + +int +mypci_close(dev_t dev, int fflag, int devtype, struct proc *p) +{ + int err=0; + + uprintf("Closing device \"mypci.\"\n"); + return(err); +} + +int +mypci_read(dev_t dev, struct uio *uio, int ioflag) +{ + int err = 0; + + uprintf("mypci read!\n"); + return err; +} + +int +mypci_write(dev_t dev, struct uio *uio, int ioflag) +{ + int err = 0; + + uprintf("mypci write!\n"); + return(err); +} + +/* PCI Support Functions */ + +/* + * Return identification string if this is device is ours. + */ +static int +mypci_probe(device_t dev) +{ + uprintf("MyPCI Probe\n" + "Vendor ID : 0x%x\n" + "Device ID : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev)); + + if (pci_get_vendor(dev) == 0x11c1) { + uprintf("We've got the Winmodem, probe successful!\n"); + return 0; + } + + return ENXIO; +} + +/* Attach function is only called if the probe is successful */ + +static int +mypci_attach(device_t dev) +{ + uprintf("MyPCI Attach for : deviceID : 0x%x\n",pci_get_vendor(dev)); + sdev = make_dev(<literal>&</literal>mypci_cdevsw, + 0, + UID_ROOT, + GID_WHEEL, + 0600, + "mypci"); + uprintf("Mypci device loaded.\n"); + return ENXIO; +} + +/* Detach device. */ + +static int +mypci_detach(device_t dev) +{ + uprintf("Mypci detach!\n"); + return 0; +} + +/* Called during system shutdown after sync. */ + +static int +mypci_shutdown(device_t dev) +{ + uprintf("Mypci shutdown!\n"); + return 0; +} + +/* + * Device suspend routine. + */ +static int +mypci_suspend(device_t dev) +{ + uprintf("Mypci suspend!\n"); + return 0; +} + +/* + * Device resume routine. + */ + +static int +mypci_resume(device_t dev) +{ + uprintf("Mypci resume!\n"); + return 0; +} + +static device_method_t mypci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mypci_probe), + DEVMETHOD(device_attach, mypci_attach), + DEVMETHOD(device_detach, mypci_detach), + DEVMETHOD(device_shutdown, mypci_shutdown), + DEVMETHOD(device_suspend, mypci_suspend), + DEVMETHOD(device_resume, mypci_resume), + + { 0, 0 } +}; + +static driver_t mypci_driver = { + "mypci", + mypci_methods, + 0, + /* sizeof(struct mypci_softc), */ +}; + +static devclass_t mypci_devclass; + +DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0); +</programlisting> + + <para>Additional Resources + <itemizedlist> + <listitem><simpara><ulink + url="http://www.pcisig.org">PCI Special Interest + Group</ulink></simpara></listitem> + <listitem><simpara>PCI System Architecture, Fourth Edition by + Tom Shanley, et al.</simpara></listitem> + </itemizedlist> + </para> + </sect1> + </chapter> + + <chapter id="usb"> + <title>USB Devices</title> + + <para>This chapter will talk about the FreeBSD mechanisms for + writing a device driver for a device on a USB bus.</para> + </chapter> - <para>old ways, newbus, character/block devices, etc</para> + <chapter id="newbus"> + <title>NewBus</title> + <para>This chapter will talk about the FreeBSD NewBus + architecture.</para> </chapter> + </part> <part id="architectures"> @@ -331,8 +928,27 @@ <chapter id="ia32"> <title>IA-32</title> - <para>Detail the (major) differences between IA-32, IA-64, PPC, - ARM, Sparc, Alpha, etc</para> + <para>Talk about the architectural specifics of FreeBSD/x86.</para> + + </chapter> + + <chapter id="alpha"> + <title>Alpha</title> + + <para>Talk about the architectural specifics of + FreeBSD/alpha.</para> + + <para>Explanation of allignment errors, how to fix, how to + ignore.</para> + + <para>Example assembly language code for FreeBSD/alpha.</para> + </chapter> + + <chapter id="ia64"> + <title>IA-64</title> + + <para>Talk about the architectural specifics of + FreeBSD/ia64.</para> </chapter> </part> |