aboutsummaryrefslogtreecommitdiff
path: root/en_US.ISO_8859-1
diff options
context:
space:
mode:
authorMurray Stokely <murray@FreeBSD.org>2000-11-06 10:22:11 +0000
committerMurray Stokely <murray@FreeBSD.org>2000-11-06 10:22:11 +0000
commit87fddebcea2e98040616614fa335de1320279011 (patch)
tree8f84235a5a7d6f49b7784b123b034aa565460a5a /en_US.ISO_8859-1
parent3c78aa06a4c09ecd8069ab7b6f1933e39fdfb938 (diff)
downloaddoc-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.sgml628
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 &lt;sys/types.h&gt;
+#include &lt;sys/module.h&gt;
+#include &lt;sys/systm.h&gt; /* uprintf */
+#include &lt;sys/errno.h&gt;
+#include &lt;sys/param.h&gt; /* defines used in kernel.h */
+#include &lt;sys/kernel.h&gt; /* 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 &lt;bsd.kmod.mk&gt;
+</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 &lt;sys/types.h&gt;
+#include &lt;sys/module.h&gt;
+#include &lt;sys/systm.h&gt; /* uprintf */
+#include &lt;sys/errno.h&gt;
+#include &lt;sys/param.h&gt; /* defines used in kernel.h */
+#include &lt;sys/kernel.h&gt; /* types used in module initialization */
+#include &lt;sys/conf.h&gt; /* cdevsw struct */
+#include &lt;sys/uio.h&gt; /* uio struct */
+#include &lt;sys/malloc.h&gt;
+
+#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 &lt;sys/types.h&gt;
+#include &lt;sys/module.h&gt;
+#include &lt;sys/systm.h&gt; /* uprintf */
+#include &lt;sys/errno.h&gt;
+#include &lt;sys/param.h&gt; /* defines used in kernel.h */
+#include &lt;sys/kernel.h&gt; /* types used in module initialization */
+#include &lt;sys/conf.h&gt; /* cdevsw struct */
+#include &lt;sys/uio.h&gt; /* uio struct */
+#include &lt;sys/malloc.h&gt;
+#include &lt;sys/bus.h&gt; /* structs, prototypes for pci bus stuff */
+
+#include &lt;pci/pcivar.h&gt; /* 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>