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.
This commit is contained in:
Murray Stokely 2000-11-06 10:22:11 +00:00
parent 3c78aa06a4
commit 87fddebcea
Notes: svn2git 2020-12-08 03:00:23 +00:00
svn path=/head/; revision=8296
3 changed files with 1866 additions and 18 deletions
en_US.ISO8859-1/books
arch-handbook
developers-handbook
en_US.ISO_8859-1/books/developers-handbook

View file

@ -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>old ways, newbus, character/block devices, etc</para>
<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>
<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>

View file

@ -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>old ways, newbus, character/block devices, etc</para>
<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>
<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>

View file

@ -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>old ways, newbus, character/block devices, etc</para>
<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>
<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>