aboutsummaryrefslogtreecommitdiff
path: root/en_US.ISO8859-1/books
diff options
context:
space:
mode:
authorDoc Manager <doceng@FreeBSD.org>2001-09-18 22:27:35 +0000
committerDoc Manager <doceng@FreeBSD.org>2001-09-18 22:27:35 +0000
commita147bf20a5cfdc48fe43a79be5dbc8d59f00fdaa (patch)
tree15ba978bb881cc49043022c504be4729072219ad /en_US.ISO8859-1/books
parent7636daf2655749322bf4e0418f8eb2e4437c888a (diff)
downloaddoc-release/4.4.0.tar.gz
doc-release/4.4.0.zip
Create tag '4.4.0'.release/4.4.0
Diffstat (limited to 'en_US.ISO8859-1/books')
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/Makefile46
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/book.sgml311
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/chapters.ent65
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/driverbasics/chapter.sgml391
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/isa/chapter.sgml2479
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/kobj/chapter.sgml298
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/locking/chapter.sgml333
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml372
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/scsi/chapter.sgml1983
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/sysinit/chapter.sgml161
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/usb/chapter.sgml623
-rw-r--r--en_US.ISO8859-1/books/arch-handbook/vm/chapter.sgml255
-rw-r--r--en_US.ISO8859-1/books/handbook/basics/disk-layout.kilbin1450 -> 0 bytes
-rw-r--r--en_US.ISO8859-1/books/handbook/book.sgml9
-rw-r--r--en_US.ISO8859-1/books/handbook/introduction/chapter.sgml11
-rw-r--r--en_US.ISO8859-1/books/handbook/mirrors/chapter.sgml53
-rw-r--r--en_US.ISO8859-1/books/handbook/multimedia/Makefile15
-rw-r--r--en_US.ISO8859-1/books/handbook/multimedia/chapter.sgml368
18 files changed, 58 insertions, 7715 deletions
diff --git a/en_US.ISO8859-1/books/arch-handbook/Makefile b/en_US.ISO8859-1/books/arch-handbook/Makefile
deleted file mode 100644
index a0e84f24d3..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/Makefile
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# $FreeBSD: doc/en_US.ISO8859-1/books/developers-handbook/Makefile,v 1.6 2001/06/23 04:57:31 murray Exp $
-#
-# Build the FreeBSD Developers' Handbook.
-#
-
-MAINTAINER=asmodai@FreeBSD.org
-
-DOC?= book
-
-FORMATS?= html-split
-
-INSTALL_COMPRESSED?= gz
-INSTALL_ONLY_COMPRESSED?=
-
-GEN_INDEX?= yes
-
-# Images
-IMAGES= sockets/layers.eps sockets/sain.eps sockets/sainfill.eps sockets/sainlsb.eps sockets/sainmsb.eps sockets/sainserv.eps sockets/serv.eps sockets/serv2.eps sockets/slayers.eps
-
-#
-# SRCS lists the individual SGML files that make up the document. Changes
-# to any of these files will force a rebuild
-#
-
-# SGML content
-SRCS= book.sgml
-SRCS+= introduction/chapter.sgml
-SRCS+= tools/chapter.sgml
-SRCS+= secure/chapter.sgml
-SRCS+= locking/chapter.sgml
-SRCS+= sockets/chapter.sgml
-SRCS+= ipv6/chapter.sgml
-SRCS+= isa/chapter.sgml
-SRCS+= pci/chapter.sgml
-SRCS+= usb/chapter.sgml
-SRCS+= scsi/chapter.sgml
-SRCS+= x86/chapter.sgml
-SRCS+= vm/chapter.sgml
-SRCS+= dma/chapter.sgml
-SRCS+= kerneldebug/chapter.sgml
-
-# Entities
-
-DOC_PREFIX?= ${.CURDIR}/../../..
-.include "${DOC_PREFIX}/share/mk/doc.project.mk"
diff --git a/en_US.ISO8859-1/books/arch-handbook/book.sgml b/en_US.ISO8859-1/books/arch-handbook/book.sgml
deleted file mode 100644
index 6f625557bd..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/book.sgml
+++ /dev/null
@@ -1,311 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: doc/en_US.ISO8859-1/books/developers-handbook/book.sgml,v 1.26 2001/09/02 03:11:08 murray Exp $
--->
-
-<!DOCTYPE BOOK PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
-<!ENTITY % bookinfo PUBLIC "-//FreeBSD//ENTITIES DocBook BookInfo Entities//EN">
-%bookinfo;
-<!ENTITY % man PUBLIC "-//FreeBSD//ENTITIES DocBook Manual Page Entities//EN">
-%man;
-<!ENTITY % chapters SYSTEM "chapters.ent"> %chapters;
-<!ENTITY % authors PUBLIC "-//FreeBSD//ENTITIES DocBook Author Entities//EN"> %authors
-<!ENTITY % mailing-lists PUBLIC "-//FreeBSD//ENTITIES DocBook Mailing List Entities//EN"> %mailing-lists;
-]>
-
-<book>
- <bookinfo>
- <title>FreeBSD Developers' Handbook</title>
-
- <corpauthor>The FreeBSD Documentation Project</corpauthor>
-
- <pubdate>August 2000</pubdate>
-
- <copyright>
- <year>2000</year>
- <year>2001</year>
- <holder>The FreeBSD Documentation Project</holder>
- </copyright>
-
- &bookinfo.legalnotice;
-
- <abstract>
- <para>Welcome to the Developers' Handbook. This manual is a
- <emphasis>work in progress</emphasis> and is the work of many
- individuals. Many sections do not yet exist and some of those
- that do exist need to be updated. If you are interested in
- helping with this project, send email to the &a.doc;.</para>
-
- <para>The latest version of this document is always available
- from the <ulink URL="http://www.FreeBSD.org/">FreeBSD World
- Wide Web server</ulink>. It may also be downloaded in a
- variety of formats and compression options from the <ulink
- url="ftp://ftp.FreeBSD.org/pub/FreeBSD/doc">FreeBSD FTP
- server</ulink> or one of the numerous <ulink
- url="http://www.freebsd.org/handbook/mirrors-ftp.html">mirror
- sites</ulink>.</para>
- </abstract>
- </bookinfo>
-
- <part id="Basics">
- <title>Basics</title>
-
- &chap.introduction;
- &chap.tools;
- &chap.secure;
-
- </part>
-
- <part id="ipc">
- <title>Interprocess Communication</title>
-
- <chapter id="signals">
- <title>* Signals</title>
-
- <para>Signals, pipes, semaphores, message queues, shared memory,
- ports, sockets, doors</para>
-
- </chapter>
-
- &chap.sockets;
- &chap.ipv6;
-
- </part>
-
- <part id="kernel">
- <title>Kernel</title>
-
- <chapter id="kernelhistory">
- <title>* History of the Unix Kernel</title>
-
- <para>Some history of the Unix/BSD kernel, system calls, how do
- processes work, blocking, scheduling, threads (kernel),
- context switching, signals, interrupts, modules, etc.</para>
-
- <para></para>
- </chapter>
-
- &chap.locking;
- &chap.kobj;
- &chap.sysinit;
- &chap.vm;
- &chap.dma;
- &chap.kerneldebug;
-
- <chapter id="ufs">
- <title>* UFS</title>
-
- <para>UFS, FFS, Ext2FS, JFS, inodes, buffer cache, labeling,
- locking, metadata, soft-updates, LFS, portalfs, procfs,
- vnodes, memory sharing, memory objects, TLBs, caching</para>
-
- </chapter>
-
- <chapter id="afs">
- <title>* AFS</title>
-
- <para>AFS, NFS, SANs etc]</para>
-
- </chapter>
-
- <chapter id="syscons">
- <title>* Syscons</title>
-
- <para>Syscons, tty, PCVT, serial console, screen savers,
- etc</para>
-
- </chapter>
-
- <chapter id="compatibility">
- <title>* Compatibility Layers</title>
-
- <sect1 id="linux">
- <title>* Linux</title>
-
- <para>Linux, SVR4, etc</para>
- </sect1>
-
- </chapter>
- </part>
-
- <part id="devicedrivers">
- <title>Device Drivers</title>
-
- &chap.driverbasics;
- &chap.isa;
- &chap.pci;
- &chap.scsi;
- &chap.usb;
-
- <chapter id="newbus">
- <title>* NewBus</title>
-
- <para>This chapter will talk about the FreeBSD NewBus
- architecture.</para>
- </chapter>
-
- <chapter id="oss">
- <title>* Sound subsystem</title>
-
- <para>OSS, waveforms, etc</para>
-
- </chapter>
-
- </part>
-
- <part id="architectures">
- <title>Architectures</title>
-
- &chap.x86;
-
- <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>
-
- <part id="appendices">
- <title>Appendices</title>
-
- <bibliography>
-
- <biblioentry id="COD" xreflabel="1">
- <authorgroup>
- <author>
- <firstname>Dave</firstname>
- <othername role="MI">A</othername>
- <surname>Patterson</surname>
- </author>
- <author>
- <firstname>John</firstname>
- <othername role="MI">L</othername>
- <surname>Hennessy</surname>
- </author>
- </authorgroup>
- <copyright><year>1998</year><holder>Morgan Kaufmann Publishers,
- Inc.</holder></copyright>
- <isbn>1-55860-428-6</isbn>
- <publisher>
- <publishername>Morgan Kaufmann Publishers, Inc.</publishername>
- </publisher>
- <title>Computer Organization and Design</title>
- <subtitle>The Hardware / Software Interface</subtitle>
- <pagenums>1-2</pagenums>
- </biblioentry>
-
- <biblioentry xreflabel="2">
- <authorgroup>
- <author>
- <firstname>W.</firstname>
- <othername role="Middle">Richard</othername>
- <surname>Stevens</surname>
- </author>
- </authorgroup>
- <copyright><year>1993</year><holder>Addison Wesley Longman,
- Inc.</holder></copyright>
- <isbn>0-201-56317-7</isbn>
- <publisher>
- <publishername>Addison Wesley Longman, Inc.</publishername>
- </publisher>
- <title>Advanced Programming in the Unix Environment</title>
- <pagenums>1-2</pagenums>
- </biblioentry>
-
- <biblioentry xreflabel="3">
- <authorgroup>
- <author>
- <firstname>Marshall</firstname>
- <othername role="Middle">Kirk</othername>
- <surname>McKusick</surname>
- </author>
- <author>
- <firstname>Keith</firstname>
- <surname>Bostic</surname>
- </author>
- <author>
- <firstname>Michael</firstname>
- <othername role="MI">J</othername>
- <surname>Karels</surname>
- </author>
- <author>
- <firstname>John</firstname>
- <othername role="MI">S</othername>
- <surname>Quarterman</surname>
- </author>
- </authorgroup>
- <copyright><year>1996</year><holder>Addison-Wesley Publishing Company,
- Inc.</holder></copyright>
- <isbn>0-201-54979-4</isbn>
- <publisher>
- <publishername>Addison-Wesley Publishing Company, Inc.</publishername>
- </publisher>
- <title>The Design and Implementation of the 4.4 BSD Operating System</title>
- <pagenums>1-2</pagenums>
- </biblioentry>
-
- <biblioentry id="Phrack" xreflabel="4">
- <authorgroup>
- <author>
- <firstname>Aleph</firstname>
- <surname>One</surname>
- </author>
- </authorgroup>
- <title>Phrack 49; "Smashing the Stack for Fun and Profit"</title>
- </biblioentry>
-
- <biblioentry id="StackGuard" xreflabel="5">
- <authorgroup>
- <author>
- <firstname>Chrispin</firstname>
- <surname>Cowan</surname>
- </author>
- <author>
- <firstname>Calton</firstname>
- <surname>Pu</surname>
- </author>
- <author>
- <firstname>Dave</firstname>
- <surname>Maier</surname>
- </author>
- </authorgroup>
- <title>StackGuard; Automatic Adaptive Detection and Prevention of
- Buffer-Overflow Attacks</title>
- </biblioentry>
-
- <biblioentry id="OpenBSD" xreflabel="6">
- <authorgroup>
- <author>
- <firstname>Todd</firstname>
- <surname>Miller</surname>
- </author>
- <author>
- <firstname>Theo</firstname>
- <surname>de Raadt</surname>
- </author>
- </authorgroup>
- <title>strlcpy and strlcat -- consistent, safe string copy and
- concatenation.</title>
- </biblioentry>
-
- </bibliography>
-
- &chap.index;
- </part>
-
-</book>
diff --git a/en_US.ISO8859-1/books/arch-handbook/chapters.ent b/en_US.ISO8859-1/books/arch-handbook/chapters.ent
deleted file mode 100644
index 1ef9ebe938..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/chapters.ent
+++ /dev/null
@@ -1,65 +0,0 @@
-<!--
- Creates entities for each chapter in the FreeBSD Developer's
- Handbook. Each entity is named chap.foo, where foo is the value
- of the id attribute on that chapter, and corresponds to the name of
- the directory in which that chapter's .sgml file is stored.
-
- Chapters should be listed in the order in which they are referenced.
-
- $FreeBSD: doc/en_US.ISO8859-1/books/developers-handbook/chapters.ent,v 1.11 2001/09/02 03:11:08 murray Exp $
--->
-
-<!-- Part one -->
-<!ENTITY chap.introduction SYSTEM "introduction/chapter.sgml">
-<!ENTITY chap.tools SYSTEM "tools/chapter.sgml">
-<!ENTITY chap.secure SYSTEM "secure/chapter.sgml">
-
-<!-- Part two -->
-<!ENTITY chap.kobj SYSTEM "kobj/chapter.sgml">
-<!ENTITY chap.sysinit SYSTEM "sysinit/chapter.sgml">
-<!ENTITY chap.locking SYSTEM "locking/chapter.sgml">
-
-<!-- Part three -->
-<!ENTITY chap.vm SYSTEM "vm/chapter.sgml">
-<!ENTITY chap.dma SYSTEM "dma/chapter.sgml">
-
-<!-- Part four -->
-<!-- No significant material yet, still in book.sgml -->
-
-<!-- Part five -->
-<!-- No significant material yet, still in book.sgml -->
-
-<!-- Part six -->
-<!ENTITY chap.sockets SYSTEM "sockets/chapter.sgml">
-<!ENTITY chap.ipv6 SYSTEM "ipv6/chapter.sgml">
-
-<!-- Part seven -->
-<!-- No significant material yet, still in book.sgml -->
-
-<!-- Part eight -->
-<!-- No significant material yet, still in book.sgml -->
-
-<!-- Part nine -->
-<!-- No significant material yet, still in book.sgml -->
-
-<!ENTITY chap.kerneldebug SYSTEM "kerneldebug/chapter.sgml">
-
-<!-- Part ten -->
-<!ENTITY chap.driverbasics SYSTEM "driverbasics/chapter.sgml">
-<!ENTITY chap.isa SYSTEM "isa/chapter.sgml">
-<!ENTITY chap.pci SYSTEM "pci/chapter.sgml">
-<!ENTITY chap.scsi SYSTEM "scsi/chapter.sgml">
-<!ENTITY chap.usb SYSTEM "usb/chapter.sgml">
-
-<!-- Part eleven -->
-<!ENTITY chap.x86 SYSTEM "x86/chapter.sgml">
-
-<!-- Part twelve -->
-<!-- No significant material yet, still in book.sgml -->
-
-<!-- Part thirteen -->
-<!-- No significant material yet, still in book.sgml -->
-
-<!-- Part fourteen (appendices) -->
-<!ENTITY chap.bibliography SYSTEM "bibliography/chapter.sgml">
-<!ENTITY chap.index SYSTEM "index.sgml">
diff --git a/en_US.ISO8859-1/books/arch-handbook/driverbasics/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/driverbasics/chapter.sgml
deleted file mode 100644
index a65ec54238..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/driverbasics/chapter.sgml
+++ /dev/null
@@ -1,391 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: doc/en_US.ISO8859-1/books/developers-handbook/driverbasics/chapter.sgml,v 1.8 2001/07/19 10:37:35 dd Exp $
--->
-
-<chapter id="driverbasics">
- <title>Writing FreeBSD Device Drivers</title>
-
- <para>This chapter was written by &a.murray; with selections from a
- variety of sources including the intro(4) man page by
- &a.joerg;.</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 two
- categories; character and network device 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 */
-
-static moduledata_t skel_mod = {
- "skel",
- skel_loader,
- NULL
-};
-
-DECLARE_MODULE(skeleton, skel_mod, 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. Devfs is still a work in
- progress, but it is already working quite nice.</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>Network Drivers</title>
-
- <para>Drivers for network devices do not use device nodes in order
- 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 Paul's drivers,
- etc..</para>
-
- </sect1>
-
-</chapter>
-
-<!--
- Local Variables:
- mode: sgml
- sgml-declaration: "../chapter.decl"
- sgml-indent-data: t
- sgml-omittag: nil
- sgml-always-quote-attributes: t
- sgml-parent-document: ("../book.sgml" "part" "chapter")
- End:
--->
diff --git a/en_US.ISO8859-1/books/arch-handbook/isa/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/isa/chapter.sgml
deleted file mode 100644
index 96f68a88c5..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/isa/chapter.sgml
+++ /dev/null
@@ -1,2479 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: doc/en_US.ISO8859-1/books/developers-handbook/isa/chapter.sgml,v 1.1 2001/05/02 01:56:01 murray Exp $
--->
-
-<chapter id="isa-driver">
- <title>ISA device drivers</title>
-
- <para>
- <emphasis>
- This chapter was written by &a.babkin; Modifications for the
- handbook made by &a.murray;, &a.wylie;, and &a.logo;.
- </emphasis>
- </para>
-
- <sect1>
- <title>Synopsis</title>
-
- <para>This chapter introduces the issues relevant to writing a
- driver for an ISA device. The pseudo-code presented here is
- rather detailed and reminiscent of the real code but is still
- only pseudo-code. It avoids the details irrelevant to the
- subject of the discussion. The real-life examples can be found
- in the source code of real drivers. In particular the drivers
- "ep" and "aha" are good sources of information.</para>
- </sect1>
-
- <sect1>
- <title>Basic information</title>
-
- <para>A typical ISA driver would need the following include
- files:</para>
-
-<programlisting>#include &lt;sys/module.h&gt;
-#include &lt;sys/bus.h&gt;
-#include &lt;machine/bus.h&gt;
-#include &lt;machine/resource.h&gt;
-#include &lt;sys/rman.h&gt;
-
-#include &lt;isa/isavar.h&gt;
-#include &lt;isa/pnpvar.h&gt;</programlisting>
-
- <para>They describe the things specific to the ISA and generic
- bus subsystem.</para>
-
- <para>The bus subsystem is implemented in an object-oriented
- fashion, its main structures are accessed by associated method
- functions.</para>
-
- <para>The list of bus methods implemented by an ISA driver is like
- one for any other bus. For a hypothetical driver named "xxx"
- they would be:</para>
-
- <itemizedlist>
- <listitem>
- <para><function>static void xxx_isa_identify (driver_t *,
- device_t);</function> Normally used for bus drivers, not
- device drivers. But for ISA devices this method may have
- special use: if the device provides some device-specific
- (non-PnP) way to auto-detect devices this routine may
- implement it.</para>
- </listitem>
-
- <listitem>
- <para><function>static int xxx_isa_probe (device_t
- dev);</function> Probe for a device at a known (or PnP)
- location. This routine can also accommodate device-specific
- auto-detection of parameters for partially configured
- devices.</para>
- </listitem>
-
- <listitem>
- <para><function>static int xxx_isa_attach (device_t
- dev);</function> Attach and initialize device.</para>
- </listitem>
-
- <listitem>
- <para><function>static int xxx_isa_detach (device_t
- dev);</function> Detach device before unloading the driver
- module.</para>
- </listitem>
-
- <listitem>
- <para><function>static int xxx_isa_shutdown (device_t
- dev);</function> Execute shutdown of the device before
- system shutdown.</para>
- </listitem>
-
- <listitem>
- <para><function>static int xxx_isa_suspend (device_t
- dev);</function> Suspend the device before the system goes
- to the power-save state. May also abort transition to the
- power-save state.</para>
- </listitem>
-
- <listitem>
- <para><function>static int xxx_isa_resume (device_t
- dev);</function> Resume the device activity after return
- from power-save state.</para>
- </listitem>
-
- </itemizedlist>
-
- <para><function>xxx_isa_probe()</function> and
- <function>xxx_isa_attach()</function> are mandatory, the rest of
- the routines are optional, depending on the device's
- needs.</para>
-
- <para>The driver is linked to the system with the following set of
- descriptions.</para>
-
-<programlisting> /* table of supported bus methods */
- static device_method_t xxx_isa_methods[] = {
- /* list all the bus method functions supported by the driver */
- /* omit the unsupported methods */
- DEVMETHOD(device_identify, xxx_isa_identify),
- DEVMETHOD(device_probe, xxx_isa_probe),
- DEVMETHOD(device_attach, xxx_isa_attach),
- DEVMETHOD(device_detach, xxx_isa_detach),
- DEVMETHOD(device_shutdown, xxx_isa_shutdown),
- DEVMETHOD(device_suspend, xxx_isa_suspend),
- DEVMETHOD(device_resume, xxx_isa_resume),
-
- { 0, 0 }
- };
-
- static driver_t xxx_isa_driver = {
- "xxx",
- xxx_isa_methods,
- sizeof(struct xxx_softc),
- };
-
-
- static devclass_t xxx_devclass;
-
- DRIVER_MODULE(xxx, isa, xxx_isa_driver, xxx_devclass,
- load_function, load_argument);</programlisting>
-
- <para>Here struct <structname>xxx_softc</structname> is a
- device-specific structure that contains private driver data
- and descriptors for the driver's resources. The bus code
- automatically allocates one softc descriptor per device as
- needed.</para>
-
- <para>If the driver is implemented as a loadable module then
- <function>load_function()</function> is called to do
- driver-specific initialization or clean-up when the driver is
- loaded or unloaded and load_argument is passed as one of its
- arguments. If the driver does not support dynamic loading (in
- other words it must always be linked into kernel) then these
- values should be set to 0 and the last definition would look
- like:</para>
-
- <programlisting> DRIVER_MODULE(xxx, isa, xxx_isa_driver,
- xxx_devclass, 0, 0);</programlisting>
-
- <para>If the driver is for a device which supports PnP then a
- table of supported PnP IDs must be defined. The table
- consists of a list of PnP IDs supported by this driver and
- human-readable descriptions of the hardware types and models
- having these IDs. It looks like:</para>
-
-<programlisting> static struct isa_pnp_id xxx_pnp_ids[] = {
- /* a line for each supported PnP ID */
- { 0x12345678, "Our device model 1234A" },
- { 0x12345679, "Our device model 1234B" },
- { 0, NULL }, /* end of table */
- };</programlisting>
-
- <para>If the driver does not support PnP devices it still needs
- an empty PnP ID table, like:</para>
-
-<programlisting> static struct isa_pnp_id xxx_pnp_ids[] = {
- { 0, NULL }, /* end of table */
- };</programlisting>
-
- </sect1>
-
- <sect1>
- <title>Device_t pointer</title>
-
- <para><structname>Device_t</structname> is the pointer type for
- the device structure. Here we consider only the methods
- interesting from the device driver writer's standpoint. The
- methods to manipulate values in the device structure
- are:</para>
-
- <itemizedlist>
-
- <listitem><para><function>device_t
- device_get_parent(dev)</function> Get the parent bus of a
- device.</para></listitem>
-
- <listitem><para><function>driver_t
- device_get_driver(dev)</function> Get pointer to its driver
- structure.</para></listitem>
-
- <listitem><para><function>char
- *device_get_name(dev)</function> Get the driver name, such
- as "xxx" for our example.</para></listitem>
-
- <listitem><para><function>int device_get_unit(dev)</function>
- Get the unit number (units are numbered from 0 for the
- devices associated with each driver).</para></listitem>
-
- <listitem><para><function>char
- *device_get_nameunit(dev)</function> Get the device name
- including the unit number, such as "xxx0" , "xxx1" and so
- on.</para></listitem>
-
- <listitem><para><function>char
- *device_get_desc(dev)</function> Get the device
- description. Normally it describes the exact model of device
- in human-readable form.</para></listitem>
-
- <listitem><para><function>device_set_desc(dev,
- desc)</function> Set the description. This makes the device
- description point to the string desc which may not be
- deallocated or changed after that.</para></listitem>
-
- <listitem><para><function>device_set_desc_copy(dev,
- desc)</function> Set the description. The description is
- copied into an internal dynamically allocated buffer, so the
- string desc may be changed afterwards without adverse
- effects.</para></listitem>
-
- <listitem><para><function>void
- *device_get_softc(dev)</function> Get pointer to the device
- descriptor (struct <structname>xxx_softc</structname>)
- associated with this device.</para></listitem>
-
- <listitem><para><function>u_int32_t
- device_get_flags(dev)</function> Get the flags specified for
- the device in the configuration file.</para></listitem>
-
- </itemizedlist>
-
- <para>A convenience function <function>device_printf(dev, fmt,
- ...)</function> may be used to print the messages from the
- device driver. It automatically prepends the unitname and
- colon to the message.</para>
-
- <para>The device_t methods are implemented in the file
- kern/bus_subr.c.</para>
-
- </sect1>
-
- <sect1>
- <title>Config file and the order of identifying and probing
- during auto-configuration</title>
-
- <para>The ISA devices are described in the kernel config file
- like:</para>
-
- <programlisting>device xxx0 at isa? port 0x300 irq 10 drq 5
- iomem 0xd0000 flags 0x1 sensitive</programlisting>
-
- <para>The values of port, IRQ and so on are converted to the
- resource values associated with the device. They are optional,
- depending on the device needs and abilities for
- auto-configuration. For example, some devices don't need DRQ
- at all and some allow the driver to read the IRQ setting from
- the device configuration ports. If a machine has multiple ISA
- buses the exact bus may be specified in the configuration
- line, like "isa0" or "isa1", otherwise the device would be
- searched for on all the ISA buses.</para>
-
- <para>"sensitive" is a resource requesting that this device must
- be probed before all non-sensitive devices. It is supported
- but does not seem to be used in any current driver.</para>
-
- <para>For legacy ISA devices in many cases the drivers are still
- able to detect the configuration parameters. But each device
- to be configured in the system must have a config line. If two
- devices of some type are installed in the system but there is
- only one configuration line for the corresponding driver, ie:
- <programlisting>device xxx0 at isa?</programlisting> then only
- one device will be configured.</para>
-
- <para>But for the devices supporting automatic identification by
- the means of Plug-n-Play or some proprietary protocol one
- configuration line is enough to configure all the devices in
- the system, like the one above or just simply:</para>
-
- <programlisting>device xxx at isa?</programlisting>
-
- <para>If a driver supports both auto-identified and legacy
- devices and both kinds are installed at once in one machine
- then it's enough to describe in the config file the legacy
- devices only. The auto-identified devices will be added
- automatically.</para>
-
- <para>When an ISA bus is auto-configured the events happen as
- follows:</para>
-
- <para>All the drivers' identify routines (including the PnP
- identify routine which identifies all the PnP devices) are
- called in random order. As they identify the devices they add
- them to the list on the ISA bus. Normally the drivers'
- identify routines associate their drivers with the new
- devices. The PnP identify routine does not know about the
- other drivers yet so it does not associate any with the new
- devices it adds.</para>
-
- <para>The PnP devices are put to sleep using the PnP protocol to
- prevent them from being probed as legacy devices.</para>
-
- <para>The probe routines of non-PnP devices marked as
- "sensitive" are called. If probe for a device went
- successfully, the attach routine is called for it.</para>
-
- <para>The probe and attach routines of all non-PNP devices are
- called likewise.</para>
-
- <para>The PnP devices are brought back from the sleep state and
- assigned the resources they request: I/O and memory address
- ranges, IRQs and DRQs, all of them not conflicting with the
- attached legacy devices.</para>
-
- <para>Then for each PnP device the probe routines of all the
- present ISA drivers are called. The first one that claims the
- device gets attached. It is possible that multiple drivers
- would claim the device with different priority, the
- highest-priority driver wins. The probe routines must call
- <function>ISA_PNP_PROBE()</function> to compare the actual PnP
- ID with the list of the IDs supported by the driver and if the
- ID is not in the table return failure. That means that
- absolutely every driver, even the ones not supporting any PnP
- devices must call <function>ISA_PNP_PROBE()</function>, at
- least with an empty PnP ID table to return failure on unknown
- PnP devices.</para>
-
- <para>The probe routine returns a positive value (the error
- code) on error, zero or negative value on success.</para>
-
- <para>The negative return values are used when a PnP device
- supports multiple interfaces. For example, an older
- compatibility interface and a newer advanced interface which
- are supported by different drivers. Then both drivers would
- detect the device. The driver which returns a higher value in
- the probe routine takes precedence (in other words, the driver
- returning 0 has highest precedence, returning -1 is next,
- returning -2 is after it and so on). In result the devices
- which support only the old interface will be handled by the
- old driver (which should return -1 from the probe routine)
- while the devices supporting the new interface as well will be
- handled by the new driver (which should return 0 from the
- probe routine). If multiple drivers return the same value then
- the one called first wins. So if a driver returns value 0 it
- may be sure that it won the priority arbitration.</para>
-
- <para>The device-specific identify routines can also assign not
- a driver but a class of drivers to the device. Then all the
- drivers in the class are probed for this device, like the case
- with PnP. This feature is not implemented in any existing
- driver and is not considered further in this document.</para>
-
- <para>Because the PnP devices are disabled when probing the
- legacy devices they will not be attached twice (once as legacy
- and once as PnP). But in case of device-dependent identify
- routines it's the responsibility of the driver to make sure
- that the same device won't be attached by the driver twice:
- once as legacy user-configured and once as
- auto-identified.</para>
-
- <para>Another practical consequence for the auto-identified
- devices (both PnP and device-specific) is that the flags can
- not be passed to them from the kernel configuration file. So
- they must either not use the flags at all or use the flags
- from the device unit 0 for all the auto-identified devices or
- use the sysctl interface instead of flags.</para>
-
- <para>Other unusual configurations may be accommodated by
- accessing the configuration resources directly with functions
- of families <function>resource_query_*()</function> and
- <function>resource_*_value()</function>. Their implementations
- are located in kern/subr_bus.h. The old IDE disk driver
- i386/isa/wd.c contains examples of such use. But the standard
- means of configuration must always be preferred. Leave parsing
- the configuration resources to the bus configuration
- code.</para>
-
- </sect1>
-
- <sect1>
- <title>Resources</title>
-
- <para>The information that a user enters into the kernel
- configuration file is processed and passed to the kernel as
- configuration resources. This information is parsed by the bus
- configuration code and transformed into a value of structure
- device_t and the bus resources associated with it. The drivers
- may access the configuration resources directly using
- functions resource_* for more complex cases of
- configuration. But generally it's not needed nor recommended,
- so this issue is not discussed further.</para>
-
- <para>The bus resources are associated with each device. They
- are identified by type and number within the type. For the ISA
- bus the following types are defined:</para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis>SYS_RES_IRQ</emphasis> - interrupt
- number</para>
- </listitem>
-
- <listitem>
- <para><emphasis>SYS_RES_DRQ</emphasis> - ISA DMA channel
- number</para>
- </listitem>
-
- <listitem>
- <para><emphasis>SYS_RES_MEMORY</emphasis> - range of
- device memory mapped into the system memory space
- </para>
- </listitem>
-
- <listitem>
- <para><emphasis>SYS_RES_IOPORT</emphasis> - range of
- device I/O registers</para>
- </listitem>
- </itemizedlist>
-
- <para>The enumeration within types starts from 0, so if a device
- has two memory regions if would have resources of type
- SYS_RES_MEMORY numbered 0 and 1. The resource type has
- nothing to do with the C language type, all the resource
- values have the C language type "unsigned long" and must be
- cast as necessary. The resource numbers don't have to be
- contiguous although for ISA they normally would be. The
- permitted resource numbers for ISA devices are:</para>
-
- <programlisting> IRQ: 0-1
- DRQ: 0-1
- MEMORY: 0-3
- IOPORT: 0-7</programlisting>
-
- <para>All the resources are represented as ranges, with a start
- value and count. For IRQ and DRQ resources the count would be
- normally equal to 1. The values for memory refer to the
- physical addresses.</para>
-
- <para>Three types of activities can be performed on
- resources:</para>
-
- <itemizedlist>
- <listitem><para>set/get</para></listitem>
- <listitem><para>allocate/release</para></listitem>
- <listitem><para>activate/deactivate</para></listitem>
- </itemizedlist>
-
- <para>Setting sets the range used by the resource. Allocation
- reserves the requested range that no other driver would be
- able to reserve it (and checking that no other driver reserved
- this range already). Activation makes the resource accessible
- to the driver doing whatever is necessary for that (for
- example, for memory it would be mapping into the kernel
- virtual address space).</para>
-
- <para>The functions to manipulate resources are:</para>
-
- <itemizedlist>
- <listitem>
- <para><function>int bus_set_resource(device_t dev, int type,
- int rid, u_long start, u_long count)</function></para>
-
- <para>Set a range for a resource. Returns 0 if successful,
- error code otherwise. Normally the only reason this
- function would return an error is value of type, rid,
- start or count out of permitted range.</para>
-
- <itemizedlist>
- <listitem>
- <para> dev - driver's device</para>
- </listitem>
- <listitem>
- <para> type - type of resource, SYS_RES_* </para>
- </listitem>
- <listitem>
- <para> rid - resource number (ID) within type </para>
- </listitem>
- <listitem>
- <para> start, count - resource range </para>
- </listitem>
- </itemizedlist>
- </listitem>
-
- <listitem>
- <para><function>int bus_get_resource(device_t dev, int type,
- int rid, u_long *startp, u_long *countp)</function></para>
-
- <para>Get the range of resource. Returns 0 if successful,
- error code if the resource is not defined yet.</para>
- </listitem>
-
- <listitem>
- <para><function>u_long bus_get_resource_start(device_t dev,
- int type, int rid) u_long bus_get_resource_count (device_t
- dev, int type, int rid)</function></para>
-
- <para>Convenience functions to get only the start or
- count. Return 0 in case of error, so if the resource start
- has 0 among the legitimate values it would be impossible
- to tell if the value is 0 or an error occurred. Luckily,
- no ISA resources for add-on drivers may have a start value
- equal 0.</para>
- </listitem>
-
- <listitem>
- <para><function>void bus_delete_resource(device_t dev, int
- type, int rid)</function></para>
- <para> Delete a resource, make it undefined.</para>
- </listitem>
-
- <listitem>
- <para><function>struct resource *
- bus_alloc_resource(device_t dev, int type, int *rid,
- u_long start, u_long end, u_long count, u_int
- flags)</function></para>
-
- <para>Allocate a resource as a range of count values not
- allocated by anyone else, somewhere between start and
- end. Alas, alignment is not supported. If the resource
- was not set yet it's automatically created. The special
- values of start 0 and end ~0 (all ones) means that the
- fixed values previously set by
- <function>bus_set_resource()</function> must be used
- instead: start and count as themselves and
- end=(start+count), in this case if the resource was not
- defined before then an error is returned. Although rid is
- passed by reference it's not set anywhere by the resource
- allocation code of the ISA bus. (The other buses may use a
- different approach and modify it).</para>
- </listitem>
- </itemizedlist>
-
- <para>Flags are a bitmap, the flags interesting for the caller
- are:</para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis>RF_ACTIVE</emphasis> - causes the resource
- to be automatically activated after allocation.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>RF_SHAREABLE</emphasis> - resource may be
- shared at the same time by multiple drivers.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>RF_TIMESHARE</emphasis> - resource may be
- time-shared by multiple drivers, i.e. allocated at the
- same time by many but activated only by one at any given
- moment of time.</para>
- </listitem>
-<!-- XXXDONT KNOW IT THESE SHOULD BE TWO SEPERATE LISTS OR NOT -->
- <listitem>
- <para>Returns 0 on error. The allocated values may be
- obtained from the returned handle using methods
- <function>rhand_*()</function>.</para>
- </listitem>
- <listitem>
- <para><function>int bus_release_resource(device_t dev, int
- type, int rid, struct resource *r)</function></para>
- </listitem>
-
- <listitem>
- <para>Release the resource, r is the handle returned by
- <function>bus_alloc_resource()</function>. Returns 0 on
- success, error code otherwise.</para>
- </listitem>
-
- <listitem>
- <para><function>int bus_activate_resource(device_t dev, int
- type, int rid, struct resource *r)</function>
- <function>int bus_deactivate_resource(device_t dev, int
- type, int rid, struct resource *r)</function></para>
- </listitem>
-
- <listitem>
- <para>Activate or deactivate resource. Return 0 on success,
- error code otherwise. If the resource is time-shared and
- currently activated by another driver then EBUSY is
- returned.</para>
- </listitem>
-
- <listitem>
- <para><function>int bus_setup_intr(device_t dev, struct
- resource *r, int flags, driver_intr_t *handler, void *arg,
- void **cookiep)</function> <function>int
- bus_teardown_intr(device_t dev, struct resource *r, void
- *cookie)</function></para>
- </listitem>
-
- <listitem>
- <para>Associate or de-associate the interrupt handler with a
- device. Return 0 on success, error code otherwise.</para>
- </listitem>
-
- <listitem>
- <para>r - the activated resource handler describing the
- IRQ</para>
- <para>flags - the interrupt priority level, one of:</para>
-
- <itemizedlist>
- <listitem>
- <para><function>INTR_TYPE_TTY</function> - terminals and
- other likewise character-type devices. To mask them
- use <function>spltty()</function>.</para>
- </listitem>
- <listitem>
- <para><function>(INTR_TYPE_TTY |
- INTR_TYPE_FAST)</function> - terminal type devices
- with small input buffer, critical to the data loss on
- input (such as the old-fashioned serial ports). To
- mask them use <function>spltty()</function>.</para>
- </listitem>
- <listitem>
- <para><function>INTR_TYPE_BIO</function> - block-type
- devices, except those on the CAM controllers. To mask
- them use <function>splbio()</function>.</para>
- </listitem>
- <listitem>
- <para><function>INTR_TYPE_CAM</function> - CAM (Common
- Access Method) bus controllers. To mask them use
- <function>splcam()</function>.</para>
- </listitem>
- <listitem>
- <para><function>INTR_TYPE_NET</function> - network
- interface controllers. To mask them use
- <function>splimp()</function>.</para>
- </listitem>
- <listitem>
- <para><function>INTR_TYPE_MISC</function> -
- miscellaneous devices. There is no other way to mask
- them than by <function>splhigh()</function> which
- masks all interrupts.</para>
- </listitem>
- </itemizedlist>
- </listitem>
- </itemizedlist>
-
- <para>When an interrupt handler executes all the other
- interrupts matching its priority level will be masked. The
- only exception is the MISC level for which no other interrupts
- are masked and which is not masked by any other
- interrupt.</para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis>handler</emphasis> - pointer to the handler
- function, the type driver_intr_t is defined as "void
- driver_intr_t(void *)"</para>
- </listitem>
- <listitem>
- <para><emphasis>arg</emphasis> - the argument passed to the
- handler to identify this particular device. It is cast
- from void* to any real type by the handler. The old
- convention for the ISA interrupt handlers was to use the
- unit number as argument, the new (recommended) convention
- is using a pointer to the device softc structure.</para>
- </listitem>
- <listitem>
- <para><emphasis>cookie[p]</emphasis> - the value received
- from <function>setup()</function> is used to identify the
- handler when passed to
- <function>teardown()</function></para>
- </listitem>
- </itemizedlist>
-
- <para>A number of methods is defined to operate on the resource
- handlers (struct resource *). Those of interest to the device
- driver writers are:</para>
-
- <itemizedlist>
- <listitem>
- <para><function>u_long rman_get_start(r) u_long
- rman_get_end(r)</function> Get the start and end of
- allocated resource range.</para>
- </listitem>
- <listitem>
- <para><function>void *rman_get_virtual(r)</function> Get
- the virtual address of activated memory resource.</para>
- </listitem>
- </itemizedlist>
-
- </sect1>
-
- <sect1>
- <title>Bus memory mapping</title>
-
- <para>In many cases data is exchanged between the driver and the
- device through the memory. Two variants are possible:</para>
-
- <para>(a) memory is located on the device card</para>
- <para>(b) memory is the main memory of computer</para>
-
- <para>In the case (a) the driver always copies the data back and
- forth between the on-card memory and the main memory as
- necessary. To map the on-card memory into the kernel virtual
- address space the physical address and length of the on-card
- memory must be defined as a SYS_RES_MEMORY resource. That
- resource can then be allocated and activated, and its virtual
- address obtained using
- <function>rman_get_virtual()</function>. The older drivers
- used the function <function>pmap_mapdev()</function> for this
- purpose, which should not be used directly any more. Now it's
- one of the internal steps of resource activation.</para>
-
- <para>Most of the ISA cards will have their memory configured
- for physical location somewhere in range 640KB-1MB. Some of
- the ISA cards require larger memory ranges which should be
- placed somewhere under 16MB (because of the 24-bit address
- limitation on the ISA bus). In that case if the machine has
- more memory than the start address of the device memory (in
- other words, they overlap) a memory hole must be configured at
- the address range used by devices. Many BIOSes allow to
- configure a memory hole of 1MB starting at 14MB or
- 15MB. FreeBSD can handle the memory holes properly if the BIOS
- reports them properly (old BIOSes may have this feature
- broken).</para>
-
- <para>In the case (b) just the address of the data is sent to
- the device, and the device uses DMA to actually access the
- data in the main memory. Two limitations are present: First,
- ISA cards can only access memory below 16MB. Second, the
- contiguous pages in virtual address space may not be
- contiguous in physical address space, so the device may have
- to do scatter/gather operations. The bus subsystem provides
- ready solutions for some of these problems, the rest has to be
- done by the drivers themselves.</para>
-
- <para>Two structures are used for DMA memory allocation,
- bus_dma_tag_t and bus_dmamap_t. Tag describes the properties
- required for the DMA memory. Map represents a memory block
- allocated according to these properties. Multiple maps may be
- associated with the same tag.</para>
-
- <para>Tags are organized into a tree-like hierarchy with
- inheritance of the properties. A child tag inherits all the
- requirements of its parent tag or may make them more strict
- but never more loose.</para>
-
- <para>Normally one top-level tag (with no parent) is created for
- each device unit. If multiple memory areas with different
- requirements are needed for each device then a tag for each of
- them may be created as a child of the parent tag.</para>
-
- <para>The tags can be used to create a map in two ways.</para>
-
- <para>First, a chunk of contiguous memory conformant with the
- tag requirements may be allocated (and later may be
- freed). This is normally used to allocate relatively
- long-living areas of memory for communication with the
- device. Loading of such memory into a map is trivial: it's
- always considered as one chunk in the appropriate physical
- memory range.</para>
-
- <para>Second, an arbitrary area of virtual memory may be loaded
- into a map. Each page of this memory will be checked for
- conformance to the map requirement. If it conforms then it's
- left at it's original location. If it is not then a fresh
- conformant "bounce page" is allocated and used as intermediate
- storage. When writing the data from the non-conformant
- original pages they will be copied to their bounce pages first
- and then transferred from the bounce pages to the device. When
- reading the data would go from the device to the bounce pages
- and then copied to their non-conformant original pages. The
- process of copying between the original and bounce pages is
- called synchronization. This is normally used on per-transfer
- basis: buffer for each transfer would be loaded, transfer done
- and buffer unloaded.</para>
-
- <para>The functions working on the DMA memory are:</para>
-
- <itemizedlist>
- <listitem>
- <para><function>int bus_dma_tag_create(bus_dma_tag_t parent,
- bus_size_t alignment, bus_size_t boundary, bus_addr_t
- lowaddr, bus_addr_t highaddr, bus_dma_filter_t *filter, void
- *filterarg, bus_size_t maxsize, int nsegments, bus_size_t
- maxsegsz, int flags, bus_dma_tag_t *dmat)</function></para>
-
- <para>Create a new tag. Returns 0 on success, the error code
- otherwise.</para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis>parent</emphasis> - parent tag, or NULL to
- create a top-level tag <emphasis>alignment</emphasis> -
- required physical alignment of the memory area to be
- allocated for this tag. Use value 1 for "no specific
- alignment". Applies only to the future
- <function>bus_dmamem_alloc()</function> but not
- <function>bus_dmamap_create()</function> calls.
- <emphasis>boundary</emphasis> - physical address
- boundary that must not be crossed when allocating the
- memory. Use value 0 for "no boundary". Applies only to
- the future <function>bus_dmamem_alloc()</function> but
- not <function>bus_dmamap_create()</function> calls.
- Must be power of 2. If the memory is planned to be used
- in non-cascaded DMA mode (i.e. the DMA addresses will be
- supplied not by the device itself but by the ISA DMA
- controller) then the boundary must be no larger than
- 64KB (64*1024) due to the limitations of the DMA
- hardware.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>lowaddr, highaddr</emphasis> - the names
- are slighlty misleading; these values are used to limit
- the permitted range of physical addresses used to
- allocate the memory. The exact meaning varies depending
- on the planned future use:</para>
-
- <itemizedlist>
- <listitem>
- <para>For <function>bus_dmamem_alloc()</function> all
- the addresses from 0 to lowaddr-1 are considered
- permitted, the higher ones are forbidden.</para>
- </listitem>
-
- <listitem>
- <para>For <function>bus_dmamap_create()</function> all
- the addresses outside the inclusive range [lowaddr;
- highaddr] are considered accessible. The addresses
- of pages inside the range are passed to the filter
- function which decides if they are accessible. If no
- filter function is supplied then all the range is
- considered unaccessible.</para>
- </listitem>
-
- <listitem>
- <para>For the ISA devices the normal values (with no
- filter function) are:</para>
- <para>lowaddr = BUS_SPACE_MAXADDR_24BIT</para>
- <para>highaddr = BUS_SPACE_MAXADDR</para>
- </listitem>
- </itemizedlist>
-
- </listitem>
-
- <listitem>
- <para><emphasis>filter, filterarg</emphasis> - the filter
- function and its argument. If NULL is passed for filter
- then the whole range [lowaddr, highaddr] is considered
- unaccessible when doing
- <function>bus_dmamap_create()</function>. Otherwise the
- physical address of each attempted page in range
- [lowaddr; highaddr] is passed to the filter function
- which decides if it is accessible. The prototype of the
- filter function is: <function>int filterfunc(void *arg,
- bus_addr_t paddr)</function> It must return 0 if the
- page is accessible, non-zero otherwise.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>maxsize</emphasis> - the maximal size of
- memory (in bytes) that may be allocated through this
- tag. In case it's difficult to estimate or could be
- arbitrarily big, the value for ISA devices would be
- BUS_SPACE_MAXSIZE_24BIT.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>nsegments</emphasis> - maximal number of
- scatter-gather segments supported by the device. If
- unrestricted then the value BUS_SPACE_UNRESTRICTED
- should be used. This value is recommended for the parent
- tags, the actual restrictions would then be specified
- for the descendant tags. Tags with nsegments equal to
- BUS_SPACE_UNRESTRICTED may not be used to actually load
- maps, they may be used only as parent tags. The
- practical limit for nsegments seems to be about 250-300,
- higher values will cause kernel stack overflow. But
- anyway the hardware normally can't support that many
- scatter-gather buffers.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>maxsegsz</emphasis> - maximal size of a
- scatter-gather segment supported by the device. The
- maximal value for ISA device would be
- BUS_SPACE_MAXSIZE_24BIT.</para>
- </listitem>
-
- <listitem>
- <para><emphasis>flags</emphasis> - a bitmap of flags. The
- only interesting flags are:</para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis>BUS_DMA_ALLOCNOW</emphasis> - requests
- to allocate all the potentially needed bounce pages
- when creating the tag</para>
- </listitem>
-
- <listitem>
- <para><emphasis>BUS_DMA_ISA</emphasis> - mysterious
- flag used only on Alpha machines. It is not defined
- for the i386 machines. Probably it should be used
- by all the ISA drivers for Alpha machines but it
- looks like there are no such drivers yet.</para>
- </listitem>
- </itemizedlist>
- </listitem>
-
- <listitem>
- <para><emphasis>dmat</emphasis> - pointer to the storage
- for the new tag to be returned</para>
- </listitem>
-
- </itemizedlist>
-
- </listitem>
-
- <listitem> <!-- Second entry in list alpha -->
- <para><function>int bus_dma_tag_destroy(bus_dma_tag_t
- dmat)</function></para>
-
- <para>Destroy a tag. Returns 0 on success, the error code
- otherwise.</para>
-
- <para>dmat - the tag to be destroyed</para>
-
- </listitem>
-
- <listitem> <!-- Third entry in list alpha -->
- <para><function>int bus_dmamem_alloc(bus_dma_tag_t dmat,
- void** vaddr, int flags, bus_dmamap_t
- *mapp)</function></para>
-
- <para>Allocate an area of contiguous memory described by the
- tag. The size of memory to be allocated is tag's maxsize.
- Returns 0 on success, the error code otherwise. The result
- still has to be loaded by
- <function>bus_dmamap_load()</function> before used to get
- the physical address of the memory.</para>
-
-<!-- XXX What it is Wylie, I got to here -->
-
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>dmat</emphasis> - the tag
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>vaddr</emphasis> - pointer to the storage
- for the kernel virtual address of the allocated area
- to be returned.
- </para>
- </listitem>
- <listitem>
- <para>
- flags - a bitmap of flags. The only interesting flag is:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>BUS_DMA_NOWAIT</emphasis> - if the
- memory is not immediately available return the
- error. If this flag is not set then the routine
- is allowed to sleep waiting until the memory
- will become available.
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
- <listitem>
- <para>
- <emphasis>mapp</emphasis> - pointer to the storage
- for the new map to be returned
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
-
- <listitem> <!-- Fourth entry in list alpha -->
- <para>
- <function>void bus_dmamem_free(bus_dma_tag_t dmat, void
- *vaddr, bus_dmamap_t map)</function>
- </para>
- <para>
- Free the memory allocated by
- <function>bus_dmamem_alloc()</function>. As of now
- freeing of the memory allocated with ISA restrictions is
- not implemented. Because of this the recommended model
- of use is to keep and re-use the allocated areas for as
- long as possible. Do not lightly free some area and then
- shortly allocate it again. That does not mean that
- <function>bus_dmamem_free()</function> should not be
- used at all: hopefully it will be properly implemented
- soon.
- </para>
-
- <itemizedlist>
- <listitem>
- <para><emphasis>dmat</emphasis> - the tag
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>vaddr</emphasis> - the kernel virtual
- address of the memory
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>map</emphasis> - the map of the memory (as
- returned from
- <function>bus_dmamem_alloc()</function>)
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
-
- <listitem> <!-- The fifth entry in list alpha -->
- <para>
- <function>int bus_dmamap_create(bus_dma_tag_t dmat, int
- flags, bus_dmamap_t *mapp)</function>
- </para>
- <para>
- Create a map for the tag, to be used in
- <function>bus_dmamap_load()</function> later. Returns 0
- on success, the error code otherwise.
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>dmat</emphasis> - the tag
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>flags</emphasis> - theoretically, a bit map
- of flags. But no flags are defined yet, so as of now
- it will be always 0.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>mapp</emphasis> - pointer to the storage
- for the new map to be returned
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
-
- <listitem> <!-- Sixth entry in the alpha list -->
- <para>
- <function>int bus_dmamap_destroy(bus_dma_tag_t dmat,
- bus_dmamap_t map)</function>
- </para>
- <para>
- Destroy a map. Returns 0 on success, the error code otherwise.
- </para>
-
- <itemizedlist>
- <listitem>
- <para>
- dmat - the tag to which the map is associated
- </para>
- </listitem>
- <listitem>
- <para>
- map - the map to be destroyed
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
-
- <listitem> <!-- Seventh entry in list alpha -->
- <para>
- <function>int bus_dmamap_load(bus_dma_tag_t dmat,
- bus_dmamap_t map, void *buf, bus_size_t buflen,
- bus_dmamap_callback_t *callback, void *callback_arg, int
- flags)</function>
- </para>
- <para>
- Load a buffer into the map (the map must be previously
- created by <function>bus_dmamap_create()</function> or
- <function>bus_dmamem_alloc()</function>). All the pages
- of the buffer are checked for conformance to the tag
- requirements and for those not conformant the bounce
- pages are allocated. An array of physical segment
- descriptors is built and passed to the callback
- routine. This callback routine is then expected to
- handle it in some way. The number of bounce buffers in
- the system is limited, so if the bounce buffers are
- needed but not immediately available the request will be
- queued and the callback will be called when the bounce
- buffers will become available. Returns 0 if the callback
- was executed immediately or EINPROGRESS if the request
- was queued for future execution. In the latter case the
- synchronization with queued callback routine is the
- responsibility of the driver.
- </para>
- <!--<blockquote>-->
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>dmat</emphasis> - the tag
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>map</emphasis> - the map
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>buf</emphasis> - kernel virtual address of
- the buffer
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>buflen</emphasis> - length of the buffer
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>callback</emphasis>,<function>
- callback_arg</function> - the callback function and
- its argument
- </para>
- </listitem>
- </itemizedlist>
- <!--</blockquote>-->
- <para>
- The prototype of callback function is:
- </para>
- <para>
- <function>void callback(void *arg, bus_dma_segment_t
- *seg, int nseg, int error)</function>
- </para>
- <!-- <blockquote> -->
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>arg</emphasis> - the same as callback_arg
- passed to <function>bus_dmamap_load()</function>
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>seg</emphasis> - array of the segment
- descriptors
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>nseg</emphasis> - number of descriptors in
- array
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>error</emphasis> - indication of the
- segment number overflow: if it's set to EFBIG then
- the buffer did not fit into the maximal number of
- segments permitted by the tag. In this case only the
- permitted number of descriptors will be in the
- array. Handling of this situation is up to the
- driver: depending on the desired semantics it can
- either consider this an error or split the buffer in
- two and handle the second part separately
- </para>
- </listitem>
- </itemizedlist>
- <!-- </blockquote> -->
- <para>
- Each entry in the segments array contains the fields:
- </para>
-
- <!-- <blockquote> -->
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>ds_addr</emphasis> - physical bus address
- of the segment
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>ds_len</emphasis> - length of the segment
- </para>
- </listitem>
- </itemizedlist>
- <!-- </blockquote>-->
- </listitem>
-
- <listitem> <!-- Eighth entry in alpha list -->
- <para>
- <function>void bus_dmamap_unload(bus_dma_tag_t dmat,
- bus_dmamap_t map)</function>
- </para>
- <para>unload the map.
- </para>
- <!-- <blockquote> -->
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>dmat</emphasis> - tag
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>map</emphasis> - loaded map
- </para>
- </listitem>
- </itemizedlist>
- <!-- </blockquote> -->
- </listitem>
-
- <listitem> <!-- Ninth entry list alpha -->
- <para>
- <function>void bus_dmamap_sync (bus_dma_tag_t dmat,
- bus_dmamap_t map, bus_dmasync_op_t op)</function>
- </para>
- <para>
- Synchronise a loaded buffer with its bounce pages before
- and after physical transfer to or from device. This is
- the function that does all the necessary copying of data
- between the original buffer and its mapped version. The
- buffers must be synchronized both before and after doing
- the transfer.
- </para>
- <!-- <blockquote> -->
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>dmat</emphasis> - tag
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>map</emphasis> - loaded map
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>op</emphasis> - type of synchronization
- operation to perform:
- </para>
- </listitem>
- </itemizedlist>
- <!-- <blockquote> -->
- <itemizedlist>
- <listitem>
- <para>
- <function>BUS_DMASYNC_PREREAD</function> - before
- reading from device into buffer
- </para>
- </listitem>
- <listitem>
- <para>
- <function>BUS_DMASYNC_POSTREAD</function> - after
- reading from device into buffer
- </para>
- </listitem>
- <listitem>
- <para>
- <function>BUS_DMASYNC_PREWRITE</function> - before
- writing the buffer to device
- </para>
- </listitem>
- <listitem>
- <para>
- <function>BUS_DMASYNC_POSTWRITE</function> - after
- writing the buffer to device
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
- </itemizedlist> <!-- End of list alpha -->
-<!-- </blockquote>
-</blockquote> -->
-
- <para>
- As of now PREREAD and POSTWRITE are null operations but that
- may change in the future, so they must not be ignored in the
- driver. Synchronization is not needed for the memory
- obtained from <function>bus_dmamem_alloc()</function>.
- </para>
- <para>
- Before calling the callback function from
- <function>bus_dmamap_load()</function> the segment array is
- stored in the stack. And it gets pre-allocated for the
- maximal number of segments allowed by the tag. Because of
- this the practical limit for the number of segments on i386
- architecture is about 250-300 (the kernel stack is 4KB minus
- the size of the user structure, size of a segment array
- entry is 8 bytes, and some space must be left). Because the
- array is allocated based on the maximal number this value
- must not be set higher than really needed. Fortunately, for
- most of hardware the maximal supported number of segments is
- much lower. But if the driver wants to handle buffers with a
- very large number of scatter-gather segments it should do
- that in portions: load part of the buffer, transfer it to
- the device, load next part of the buffer, and so on.
- </para>
- <para>
- Another practical consequence is that the number of segments
- may limit the size of the buffer. If all the pages in the
- buffer happen to be physically non-contiguous then the
- maximal supported buffer size for that fragmented case would
- be (nsegments * page_size). For example, if a maximal number
- of 10 segments is supported then on i386 maximal guaranteed
- supported buffer size would be 40K. If a higher size is
- desired then special tricks should be used in the driver.
- </para>
- <para>
- If the hardware does not support scatter-gather at all or
- the driver wants to support some buffer size even if it's
- heavily fragmented then the solution is to allocate a
- contiguous buffer in the driver and use it as intermediate
- storage if the original buffer does not fit.
- </para>
- <para>
- Below are the typical call sequences when using a map depend
- on the use of the map. The characters -> are used to show
- the flow of time.
- </para>
- <para>
- For a buffer which stays practically fixed during all the
- time between attachment and detachment of a device:</para>
- <para>
- bus_dmamem_alloc -> bus_dmamap_load -> ...use buffer... ->
- -> bus_dmamap_unload -> bus_dmamem_free
- </para>
-
- <para>For a buffer that changes frequently and is passed from
- outside the driver:
-
- <!-- XXX is this correct? -->
- <programlisting> bus_dmamap_create ->
- -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->
- -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->
- ...
- -> bus_dmamap_load -> bus_dmamap_sync(PRE...) -> do transfer ->
- -> bus_dmamap_sync(POST...) -> bus_dmamap_unload ->
- -> bus_dmamap_destroy </programlisting>
-
- </para>
- <para>
- When loading a map created by
- <function>bus_dmamem_alloc()</function> the passed address
- and size of the buffer must be the same as used in
- <function>bus_dmamem_alloc()</function>. In this case it is
- guaranteed that the whole buffer will be mapped as one
- segment (so the callback may be based on this assumption)
- and the request will be executed immediately (EINPROGRESS
- will never be returned). All the callback needs to do in
- this case is to save the physical address.
- </para>
- <para>
- A typical example would be:
- </para>
-
- <programlisting> static void
- alloc_callback(void *arg, bus_dma_segment_t *seg, int nseg, int error)
- {
- *(bus_addr_t *)arg = seg[0].ds_addr;
- }
-
- ...
- int error;
- struct somedata {
- ....
- };
- struct somedata *vsomedata; /* virtual address */
- bus_addr_t psomedata; /* physical bus-relative address */
- bus_dma_tag_t tag_somedata;
- bus_dmamap_t map_somedata;
- ...
-
- error=bus_dma_tag_create(parent_tag, alignment,
- boundary, lowaddr, highaddr, /*filter*/ NULL, /*filterarg*/ NULL,
- /*maxsize*/ sizeof(struct somedata), /*nsegments*/ 1,
- /*maxsegsz*/ sizeof(struct somedata), /*flags*/ 0,
- &#38;tag_somedata);
- if(error)
- return error;
-
- error = bus_dmamem_alloc(tag_somedata, &#38;vsomedata, /* flags*/ 0,
- &#38;map_somedata);
- if(error)
- return error;
-
- bus_dmamap_load(tag_somedata, map_somedata, (void *)vsomedata,
- sizeof (struct somedata), alloc_callback,
- (void *) &#38;psomedata, /*flags*/0); </programlisting>
-
- <para>
- Looks a bit long and complicated but that's the way to do
- it. The practical consequence is: if multiple memory areas
- are allocated always together it would be a really good idea
- to combine them all into one structure and allocate as one
- (if the alignment and boundary limitations permit).
- </para>
- <para>
- When loading an arbitrary buffer into the map created by
- <function>bus_dmamap_create()</function> special measures
- must be taken to synchronize with the callback in case it
- would be delayed. The code would look like:
- </para>
-
- <programlisting> {
- int s;
- int error;
-
- s = splsoftvm();
- error = bus_dmamap_load(
- dmat,
- dmamap,
- buffer_ptr,
- buffer_len,
- callback,
- /*callback_arg*/ buffer_descriptor,
- /*flags*/0);
- if (error == EINPROGRESS) {
- /*
- * Do whatever is needed to ensure synchronization
- * with callback. Callback is guaranteed not to be started
- * until we do splx() or tsleep().
- */
- }
- splx(s);
- } </programlisting>
-
- <para>
- Two possible approaches for the processing of requests are:
- </para>
- <para>
- 1. If requests are completed by marking them explicitly as
- done (such as the CAM requests) then it would be simpler to
- put all the further processing into the callback driver
- which would mark the request when it's done. Then not much
- extra synchronization is needed. For the flow control
- reasons it may be a good idea to freeze the request queue
- until this request gets completed.
- </para>
- <para>
- 2. If requests are completed when the function returns (such
- as classic read or write requests on character devices) then
- a synchronization flag should be set in the buffer
- descriptor and <function>tsleep()</function> called. Later
- when the callback gets called it will do it's processing and
- check this synchronization flag. If it's set then the
- callback should issue a wakeup. In this approach the
- callback function could either do all the needed processing
- (just like the previous case) or simply save the segments
- array in the buffer descriptor. Then after callback
- completes the calling function could use this saved segments
- array and do all the processing.
-
- </para>
- </sect1>
-<!--_________________________________________________________________________-->
-<!--~~~~~~~~~~~~~~~~~~~~END OF SECTION~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
-
- <sect1>
- <title>DMA</title>
- <!-- Section Marked up by Wylie -->
- <para>
- The Direct Memory Access (DMA) is implemented in the ISA bus
- through the DMA controller (actually, two of them but that's
- an irrelevant detail). To make the early ISA devices simple
- and cheap the logic of the bus control and address
- generation was concentrated in the DMA controller.
- Fortunately, FreeBSD provides a set of functions that mostly
- hide the annoying details of the DMA controller from the
- device drivers.
- </para>
-
- <para>
- The simplest case is for the fairly intelligent
- devices. Like the bus master devices on PCI they can
- generate the bus cycles and memory addresses all by
- themselves. The only thing they really need from the DMA
- controller is bus arbitration. So for this purpose they
- pretend to be cascaded slave DMA controllers. And the only
- thing needed from the system DMA controller is to enable the
- cascaded mode on a DMA channel by calling the following
- function when attaching the driver:
- </para>
-
- <para>
- <function>void isa_dmacascade(int channel_number)</function>
- </para>
-
- <para>
- All the further activity is done by programming the
- device. When detaching the driver no DMA-related functions
- need to be called.
- </para>
-
- <para>
- For the simpler devices things get more complicated. The
- functions used are:
- </para>
-
- <itemizedlist>
-
- <listitem>
- <para>
- <function>int isa_dma_acquire(int chanel_number)</function>
- </para>
- <para>
- Reserve a DMA channel. Returns 0 on success or EBUSY
- if the channel was already reserved by this or a
- different driver. Most of the ISA devices are not able
- to share DMA channels anyway, so normally this
- function is called when attaching a device. This
- reservation was made redundant by the modern interface
- of bus resources but still must be used in addition to
- the latter. If not used then later, other DMA routines
- will panic.
- </para>
- </listitem>
-
- <listitem>
- <para>
- <function>int isa_dma_release(int chanel_number)</function>
- </para>
- <para>
- Release a previously reserved DMA channel. No
- transfers must be in progress when the channel is
- released (as well as the device must not try to
- initiate transfer after the channel is released).
- </para>
- </listitem>
-
- <listitem>
- <para>
- <function>void isa_dmainit(int chan, u_int
- bouncebufsize)</function>
- </para>
- <para>
- Allocate a bounce buffer for use with the specified
- channel. The requested size of the buffer can't exceed
- 64KB. This bounce buffer will be automatically used
- later if a transfer buffer happens to be not
- physically contiguous or outside of the memory
- accessible by the ISA bus or crossing the 64KB
- boundary. If the transfers will be always done from
- buffers which conform to these conditions (such as
- those allocated by
- <function>bus_dmamem_alloc()</function> with proper
- limitations) then <function>isa_dmainit()</function>
- does not have to be called. But it's quite convenient
- to transfer arbitrary data using the DMA controller.
- The bounce buffer will automatically care of the
- scatter-gather issues.
- </para>
- <!-- <blockquote> -->
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>chan</emphasis> - channel number
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>bouncebufsize</emphasis> - size of the
- bounce buffer in bytes
- </para>
- </listitem>
- </itemizedlist>
-<!-- </blockquote> -->
-<!--</para> -->
- </listitem>
-
- <listitem>
- <para>
- <function>void isa_dmastart(int flags, caddr_t addr, u_int
- nbytes, int chan)</function>
- </para>
- <para>
- Prepare to start a DMA transfer. This function must be
- called to set up the DMA controller before actually
- starting transfer on the device. It checks that the
- buffer is contiguous and falls into the ISA memory
- range, if not then the bounce buffer is automatically
- used. If bounce buffer is required but not set up by
- <function>isa_dmainit()</function> or too small for
- the requested transfer size then the system will
- panic. In case of a write request with bounce buffer
- the data will be automatically copied to the bounce
- buffer.
- </para>
- </listitem>
- <listitem>
- <para>flags - a bitmask determining the type of operation to
- be done. The direction bits B_READ and B_WRITE are mutually
- exclusive.
- </para>
- <!-- <blockquote> -->
- <itemizedlist>
- <listitem>
- <para>
- B_READ - read from the ISA bus into memory
- </para>
- </listitem>
- <listitem>
- <para>
- B_WRITE - write from the memory to the ISA bus
- </para>
- </listitem>
- <listitem>
- <para>
- B_RAW - if set then the DMA controller will remember
- the buffer and after the end of transfer will
- automatically re-initialize itself to repeat transfer
- of the same buffer again (of course, the driver may
- change the data in the buffer before initiating
- another transfer in the device). If not set then the
- parameters will work only for one transfer, and
- <function>isa_dmastart()</function> will have to be
- called again before initiating the next
- transfer. Using B_RAW makes sense only if the bounce
- buffer is not used.
- </para>
- </listitem>
- </itemizedlist>
-<!-- </blockquote> -->
- </listitem>
- <listitem>
- <para>
- addr - virtual address of the buffer
- </para>
- </listitem>
- <listitem>
- <para>
- nbytes - length of the buffer. Must be less or equal to
- 64KB. Length of 0 is not allowed: the DMA controller will
- understand it as 64KB while the kernel code will
- understand it as 0 and that would cause unpredictable
- effects. For channels number 4 and higher the length must
- be even because these channels transfer 2 bytes at a
- time. In case of an odd length the last byte will not be
- transferred.
- </para>
- </listitem>
- <listitem>
- <para>
- chan - channel number
- </para>
- </listitem>
-
- <listitem>
- <para>
- <function>void isa_dmadone(int flags, caddr_t addr, int
- nbytes, int chan)</function>
- </para>
- <para>
- Synchronize the memory after device reports that transfer
- is done. If that was a read operation with a bounce buffer
- then the data will be copied from the bounce buffer to the
- original buffer. Arguments are the same as for
- <function>isa_dmastart()</function>. Flag B_RAW is
- permitted but it does not affect
- <function>isa_dmadone()</function> in any way.
- </para>
- </listitem>
-
- <listitem>
- <para>
- <function>int isa_dmastatus(int channel_number)</function>
- </para>
- <para>
- Returns the number of bytes left in the current transfer
- to be transferred. In case the flag B_READ was set in
- <function>isa_dmastart()</function> the number returned
- will never be equal to zero. At the end of transfer it
- will be automatically reset back to the length of
- buffer. The normal use is to check the number of bytes
- left after the device signals that the transfer is
- completed. If the number of bytes is not 0 then probably
- something went wrong with that transfer.
- </para>
- </listitem>
-
- <listitem>
- <para>
- <function>int isa_dmastop(int channel_number)</function>
- </para>
- <para>
- Aborts the current transfer and returns the number of
- bytes left untransferred.
- </para>
- </listitem>
- </itemizedlist>
- </sect1>
-
- <sect1>
- <title>xxx_isa_probe</title>
- <!-- Section marked up by Wylie -->
-
- <para>
- This function probes if a device is present. If the driver
- supports auto-detection of some part of device configuration
- (such as interrupt vector or memory address) this
- auto-detection must be done in this routine.
- </para>
-
- <para>
- As for any other bus, if the device cannot be detected or
- is detected but failed the self-test or some other problem
- happened then it returns a positive value of error. The
- value ENXIO must be returned if the device is not
- present. Other error values may mean other conditions. Zero
- or negative values mean success. Most of the drivers return
- zero as success.
- </para>
-
- <para>
- The negative return values are used when a PnP device
- supports multiple interfaces. For example, an older
- compatibility interface and a newer advanced interface which
- are supported by different drivers. Then both drivers would
- detect the device. The driver which returns a higher value
- in the probe routine takes precedence (in other words, the
- driver returning 0 has highest precedence, one returning -1
- is next, one returning -2 is after it and so on). In result
- the devices which support only the old interface will be
- handled by the old driver (which should return -1 from the
- probe routine) while the devices supporting the new
- interface as well will be handled by the new driver (which
- should return 0 from the probe routine).
- </para>
-
- <para>
- The device descriptor struct xxx_softc is allocated by the
- system before calling the probe routine. If the probe
- routine returns an error the descriptor will be
- automatically deallocated by the system. So if a probing
- error occurs the driver must make sure that all the
- resources it used during probe are deallocated and that
- nothing keeps the descriptor from being safely
- deallocated. If the probe completes successfully the
- descriptor will be preserved by the system and later passed
- to the routine <function>xxx_isa_attach()</function>. If a
- driver returns a negative value it can't be sure that it
- will have the highest priority and its attach routine will
- be called. So in this case it also must release all the
- resources before returning and if necessary allocate them
- again in the attach routine. When
- <function>xxx_isa_probe()</function> returns 0 releasing the
- resources before returning is also a good idea, a
- well-behaved driver should do so. But in case if there is
- some problem with releasing the resources the driver is
- allowed to keep resources between returning 0 from the probe
- routine and execution of the attach routine.
- </para>
-
- <para>
- A typical probe routine starts with getting the device
- descriptor and unit:
- </para>
-
- <programlisting> struct xxx_softc *sc = device_get_softc(dev);
- int unit = device_get_unit(dev);
- int pnperror;
- int error = 0;
-
- sc->dev = dev; /* link it back */
- sc->unit = unit; </programlisting>
-
- <para>
- Then check for the PnP devices. The check is carried out by
- a table containing the list of PnP IDs supported by this
- driver and human-readable descriptions of the device models
- corresponding to these IDs.
- </para>
-
- <programlisting>
- pnperror=ISA_PNP_PROBE(device_get_parent(dev), dev,
- xxx_pnp_ids); if(pnperror == ENXIO) return ENXIO;
- </programlisting>
-
- <para>
- The logic of ISA_PNP_PROBE is the following: If this card
- (device unit) was not detected as PnP then ENOENT will be
- returned. If it was detected as PnP but its detected ID does
- not match any of the IDs in the table then ENXIO is
- returned. Finally, if it has PnP support and it matches on
- of the IDs in the table, 0 is returned and the appropriate
- description from the table is set by
- <function>device_set_desc()</function>.
- </para>
-
- <para>
- If a driver supports only PnP devices then the condition
- would look like:
- </para>
-
- <programlisting> if(pnperror != 0)
- return pnperror; </programlisting>
-
- <para>
- No special treatment is required for the drivers which don't
- support PnP because they pass an empty PnP ID table and will
- always get ENXIO if called on a PnP card.
- </para>
-
- <para>
- The probe routine normally needs at least some minimal set
- of resources, such as I/O port number to find the card and
- probe it. Depending on the hardware the driver may be able
- to discover the other necessary resources automatically. The
- PnP devices have all the resources pre-set by the PnP
- subsystem, so the driver does not need to discover them by
- itself.
- </para>
-
- <para>
- Typically the minimal information required to get access to
- the device is the I/O port number. Then some devices allow
- to get the rest of information from the device configuration
- registers (though not all devices do that). So first we try
- to get the port start value:
- </para>
-
- <programlisting> sc->port0 = bus_get_resource_start(dev,
- SYS_RES_IOPORT, 0 /*rid*/); if(sc->port0 == 0) return ENXIO;
- </programlisting>
-
- <para>
- The base port address is saved in the structure softc for
- future use. If it will be used very often then calling the
- resource function each time would be prohibitively slow. If
- we don't get a port we just return an error. Some device
- drivers can instead be clever and try to probe all the
- possible ports, like this:
- </para>
-
- <programlisting>
- /* table of all possible base I/O port addresses for this device */
- static struct xxx_allports {
- u_short port; /* port address */
- short used; /* flag: if this port is already used by some unit */
- } xxx_allports = {
- { 0x300, 0 },
- { 0x320, 0 },
- { 0x340, 0 },
- { 0, 0 } /* end of table */
- };
-
- ...
- int port, i;
- ...
-
- port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
- if(port !=0 ) {
- for(i=0; xxx_allports[i].port!=0; i++) {
- if(xxx_allports[i].used || xxx_allports[i].port != port)
- continue;
-
- /* found it */
- xxx_allports[i].used = 1;
- /* do probe on a known port */
- return xxx_really_probe(dev, port);
- }
- return ENXIO; /* port is unknown or already used */
- }
-
- /* we get here only if we need to guess the port */
- for(i=0; xxx_allports[i].port!=0; i++) {
- if(xxx_allports[i].used)
- continue;
-
- /* mark as used - even if we find nothing at this port
- * at least we won't probe it in future
- */
- xxx_allports[i].used = 1;
-
- error = xxx_really_probe(dev, xxx_allports[i].port);
- if(error == 0) /* found a device at that port */
- return 0;
- }
- /* probed all possible addresses, none worked */
- return ENXIO;</programlisting>
-
- <para>
- Of course, normally the driver's
- <function>identify()</function> routine should be used for
- such things. But there may be one valid reason why it may be
- better to be done in <function>probe()</function>: if this
- probe would drive some other sensitive device crazy. The
- probe routines are ordered with consideration of the
- "sensitive" flag: the sensitive devices get probed first and
- the rest of devices later. But the
- <function>identify()</function> routines are called before
- any probes, so they show no respect to the sensitive devices
- and may upset them.
- </para>
-
- <para>
- Now, after we got the starting port we need to set the port
- count (except for PnP devices) because the kernel does not
- have this information in the configuration file.
- </para>
-
- <programlisting>
- if(pnperror /* only for non-PnP devices */
- &#38;&#38; bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port0,
- XXX_PORT_COUNT)&lt;0)
- return ENXIO;</programlisting>
-
- <para>
- Finally allocate and activate a piece of port address space
- (special values of start and end mean "use those we set by
- <function>bus_set_resource()</function>"):
- </para>
-
- <programlisting>
- sc->port0_rid = 0;
- sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT,
- &#38;sc->port0_rid,
- /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
-
- if(sc->port0_r == NULL)
- return ENXIO;</programlisting>
-
- <para>
- Now having access to the port-mapped registers we can poke
- the device in some way and check if it reacts like it is
- expected to. If it does not then there is probably some
- other device or no device at all at this address.
- </para>
-
- <para>
- Normally drivers don't set up the interrupt handlers until
- the attach routine. Instead they do probes in the polling
- mode using the <function>DELAY()</function> function for
- timeout. The probe routine must never hang forever, all the
- waits for the device must be done with timeouts. If the
- device does not respond within the time it's probably broken
- or misconfigured and the driver must return error. When
- determining the timeout interval give the device some extra
- time to be on the safe side: although
- <function>DELAY()</function> is supposed to delay for the
- same amount of time on any machine it has some margin of
- error, depending on the exact CPU.
- </para>
-
- <para>
- If the probe routine really wants to check that the
- interrupts really work it may configure and probe the
- interrupts too. But that's not recommended.
- </para>
-
- <programlisting>
- /* implemented in some very device-specific way */
- if(error = xxx_probe_ports(sc))
- goto bad; /* will deallocate the resources before returning */
- </programlisting>
-
- <para>
- The fucntion <function>xxx_probe_ports()</function> may also
- set the device description depending on the exact model of
- device it discovers. But if there is only one supported
- device model this can be as well done in a hardcoded way.
- Of course, for the PnP devices the PnP support sets the
- description from the table automatically.
- </para>
-
-
- <programlisting> if(pnperror)
- device_set_desc(dev, "Our device model 1234");
- </programlisting>
-
- <para>
- Then the probe routine should either discover the ranges of
- all the resources by reading the device configuration
- registers or make sure that they were set explicitly by the
- user. We will consider it with an example of on-board
- memory. The probe routine should be as non-intrusive as
- possible, so allocation and check of functionality of the
- rest of resources (besides the ports) would be better left
- to the attach routine.
- </para>
-
- <para>
- The memory address may be specified in the kernel
- configuration file or on some devices it may be
- pre-configured in non-volatile configuration registers. If
- both sources are available and different, which one should
- be used? Probably if the user bothered to set the address
- explicitly in the kernel configuration file they know what
- they're doing and this one should take precedence. An
- example of implementation could be:
- </para>
- <programlisting>
- /* try to find out the config address first */
- sc->mem0_p = bus_get_resource_start(dev, SYS_RES_MEMORY, 0 /*rid*/);
- if(sc->mem0_p == 0) { /* nope, not specified by user */
- sc->mem0_p = xxx_read_mem0_from_device_config(sc);
-
-
- if(sc->mem0_p == 0)
- /* can't get it from device config registers either */
- goto bad;
- } else {
- if(xxx_set_mem0_address_on_device(sc) &lt; 0)
- goto bad; /* device does not support that address */
- }
-
- /* just like the port, set the memory size,
- * for some devices the memory size would not be constant
- * but should be read from the device configuration registers instead
- * to accommodate different models of devices. Another option would
- * be to let the user set the memory size as "msize" configuration
- * resource which will be automatically handled by the ISA bus.
- */
- if(pnperror) { /* only for non-PnP devices */
- sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);
- if(sc->mem0_size == 0) /* not specified by user */
- sc->mem0_size = xxx_read_mem0_size_from_device_config(sc);
-
- if(sc->mem0_size == 0) {
- /* suppose this is a very old model of device without
- * auto-configuration features and the user gave no preference,
- * so assume the minimalistic case
- * (of course, the real value will vary with the driver)
- */
- sc->mem0_size = 8*1024;
- }
-
- if(xxx_set_mem0_size_on_device(sc) &lt; 0)
- goto bad; /* device does not support that size */
-
- if(bus_set_resource(dev, SYS_RES_MEMORY, /*rid*/0,
- sc->mem0_p, sc->mem0_size)&lt;0)
- goto bad;
- } else {
- sc->mem0_size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0 /*rid*/);
- } </programlisting>
-
- <para>
- Resources for IRQ and DRQ are easy to check by analogy.
- </para>
-
- <para>
- If all went well then release all the resources and return success.
- </para>
-
- <programlisting> xxx_free_resources(sc);
- return 0;</programlisting>
-
- <para>
- Finally, handle the troublesome situations. All the
- resources should be deallocated before returning. We make
- use of the fact that before the structure softc is passed to
- us it gets zeroed out, so we can find out if some resource
- was allocated: then its descriptor is non-zero.
- </para>
-
- <programlisting> bad:
-
- xxx_free_resources(sc);
- if(error)
- return error;
- else /* exact error is unknown */
- return ENXIO;</programlisting>
-
- <para>
- That would be all for the probe routine. Freeing of
- resources is done from multiple places, so it's moved to a
- function which may look like:
- </para>
-
-<programlisting>static void
- xxx_free_resources(sc)
- struct xxx_softc *sc;
- {
- /* check every resource and free if not zero */
-
- /* interrupt handler */
- if(sc->intr_r) {
- bus_teardown_intr(sc->dev, sc->intr_r, sc->intr_cookie);
- bus_release_resource(sc->dev, SYS_RES_IRQ, sc->intr_rid,
- sc->intr_r);
- sc->intr_r = 0;
- }
-
- /* all kinds of memory maps we could have allocated */
- if(sc->data_p) {
- bus_dmamap_unload(sc->data_tag, sc->data_map);
- sc->data_p = 0;
- }
- if(sc->data) { /* sc->data_map may be legitimately equal to 0 */
- /* the map will also be freed */
- bus_dmamem_free(sc->data_tag, sc->data, sc->data_map);
- sc->data = 0;
- }
- if(sc->data_tag) {
- bus_dma_tag_destroy(sc->data_tag);
- sc->data_tag = 0;
- }
-
- ... free other maps and tags if we have them ...
-
- if(sc->parent_tag) {
- bus_dma_tag_destroy(sc->parent_tag);
- sc->parent_tag = 0;
- }
-
- /* release all the bus resources */
- if(sc->mem0_r) {
- bus_release_resource(sc->dev, SYS_RES_MEMORY, sc->mem0_rid,
- sc->mem0_r);
- sc->mem0_r = 0;
- }
- ...
- if(sc->port0_r) {
- bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->port0_rid,
- sc->port0_r);
- sc->port0_r = 0;
- }
- }</programlisting>
-
- </sect1>
-
- <sect1>
- <title>xxx_isa_attach</title>
- <!-- Section Marked up by Wylie -->
-
- <para>The attach routine actually connects the driver to the
- system if the probe routine returned success and the system
- had chosen to attach that driver. If the probe routine
- returned 0 then the attach routine may expect to receive the
- device structure softc intact, as it was set by the probe
- routine. Also if the probe routine returns 0 it may expect
- that the attach routine for this device shall be called at
- some point in the future. If the probe routine returns a
- negative value then the driver may make none of these
- assumptions.
- </para>
-
- <para>The attach routine returns 0 if it completed successfully or
- error code otherwise.
- </para>
-
- <para>The attach routine starts just like the probe routine,
- with getting some frequently used data into more accessible
- variables.
- </para>
-
- <programlisting> struct xxx_softc *sc = device_get_softc(dev);
- int unit = device_get_unit(dev);
- int error = 0;</programlisting>
-
- <para>Then allocate and activate all the necessary
- resources. Because normally the port range will be released
- before returning from probe, it has to be allocated
- again. We expect that the probe routine had properly set all
- the resource ranges, as well as saved them in the structure
- softc. If the probe routine had left some resource allocated
- then it does not need to be allocated again (which would be
- considered an error).
- </para>
-
- <programlisting> sc->port0_rid = 0;
- sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &#38;sc->port0_rid,
- /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
-
- if(sc->port0_r == NULL)
- return ENXIO;
-
- /* on-board memory */
- sc->mem0_rid = 0;
- sc->mem0_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &#38;sc->mem0_rid,
- /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
-
- if(sc->mem0_r == NULL)
- goto bad;
-
- /* get its virtual address */
- sc->mem0_v = rman_get_virtual(sc->mem0_r);</programlisting>
-
- <para>The DMA request channel (DRQ) is allocated likewise. To
- initialize it use functions of the
- <function>isa_dma*()</function> family. For example:
- </para>
-
- <para><function>isa_dmacascade(sc->drq0);</function></para>
-
- <para>The interrupt request line (IRQ) is a bit
- special. Besides allocation the driver's interrupt handler
- should be associated with it. Historically in the old ISA
- drivers the argument passed by the system to the interrupt
- handler was the device unit number. But in modern drivers
- the convention suggests passing the pointer to structure
- softc. The important reason is that when the structures
- softc are allocated dynamically then getting the unit number
- from softc is easy while getting softc from unit number is
- difficult. Also this convention makes the drivers for
- different buses look more uniform and allows them to share
- the code: each bus gets its own probe, attach, detach and
- other bus-specific routines while the bulk of the driver
- code may be shared among them.
- </para>
-
- <programlisting>
- sc->intr_rid = 0;
- sc->intr_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &#38;sc->intr_rid,
- /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE);
-
- if(sc->intr_r == NULL)
- goto bad;
-
- /*
- * XXX_INTR_TYPE is supposed to be defined depending on the type of
- * the driver, for example as INTR_TYPE_CAM for a CAM driver
- */
- error = bus_setup_intr(dev, sc->intr_r, XXX_INTR_TYPE,
- (driver_intr_t *) xxx_intr, (void *) sc, &#38;sc->intr_cookie);
- if(error)
- goto bad;
-
- </programlisting>
-
-
- <para>If the device needs to make DMA to the main memory then
- this memory should be allocated like described before:
- </para>
-
- <programlisting> error=bus_dma_tag_create(NULL, /*alignment*/ 4,
- /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_24BIT,
- /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL,
- /*maxsize*/ BUS_SPACE_MAXSIZE_24BIT,
- /*nsegments*/ BUS_SPACE_UNRESTRICTED,
- /*maxsegsz*/ BUS_SPACE_MAXSIZE_24BIT, /*flags*/ 0,
- &#38;sc->parent_tag);
- if(error)
- goto bad;
-
- /* many things get inherited from the parent tag
- * sc->data is supposed to point to the structure with the shared data,
- * for example for a ring buffer it could be:
- * struct {
- * u_short rd_pos;
- * u_short wr_pos;
- * char bf[XXX_RING_BUFFER_SIZE]
- * } *data;
- */
- error=bus_dma_tag_create(sc->parent_tag, 1,
- 0, BUS_SPACE_MAXADDR, 0, /*filter*/ NULL, /*filterarg*/ NULL,
- /*maxsize*/ sizeof(* sc->data), /*nsegments*/ 1,
- /*maxsegsz*/ sizeof(* sc->data), /*flags*/ 0,
- &#38;sc->data_tag);
- if(error)
- goto bad;
-
- error = bus_dmamem_alloc(sc->data_tag, &#38;sc->data, /* flags*/ 0,
- &#38;sc->data_map);
- if(error)
- goto bad;
-
- /* xxx_alloc_callback() just saves the physical address at
- * the pointer passed as its argument, in this case &#38;sc->data_p.
- * See details in the section on bus memory mapping.
- * It can be implemented like:
- *
- * static void
- * xxx_alloc_callback(void *arg, bus_dma_segment_t *seg,
- * int nseg, int error)
- * {
- * *(bus_addr_t *)arg = seg[0].ds_addr;
- * }
- */
- bus_dmamap_load(sc->data_tag, sc->data_map, (void *)sc->data,
- sizeof (* sc->data), xxx_alloc_callback, (void *) &#38;sc->data_p,
- /*flags*/0);</programlisting>
-
-
- <para>After all the necessary resources are allocated the
- device should be initialized. The initialization may include
- testing that all the expected features are functional.</para>
-
- <programlisting> if(xxx_initialize(sc) &lt; 0)
- goto bad; </programlisting>
-
-
- <para>The bus subsystem will automatically print on the
- console the device description set by probe. But if the
- driver wants to print some extra information about the
- device it may do so, for example:</para>
-
- <programlisting>
- device_printf(dev, "has on-card FIFO buffer of %d bytes\n", sc->fifosize);
- </programlisting>
-
- <para>If the initialization routine experiences any problems
- then printing messages about them before returning error is
- also recommended.</para>
-
- <para>The final step of the attach routine is attaching the
- device to its functional subsystem in the kernel. The exact
- way to do it depends on the type of the driver: a character
- device, a block device, a network device, a CAM SCSI bus
- device and so on.</para>
-
- <para>If all went well then return success.</para>
-
- <programlisting> error = xxx_attach_subsystem(sc);
- if(error)
- goto bad;
-
- return 0; </programlisting>
-
- <para>Finally, handle the troublesome situations. All the
- resources should be deallocated before returning an
- error. We make use of the fact that before the structure
- softc is passed to us it gets zeroed out, so we can find out
- if some resource was allocated: then its descriptor is
- non-zero.</para>
-
- <programlisting> bad:
-
- xxx_free_resources(sc);
- if(error)
- return error;
- else /* exact error is unknown */
- return ENXIO;</programlisting>
-
- <para>That would be all for the attach routine.</para>
-
- </sect1>
-
-
- <sect1>
- <title>xxx_isa_detach</title>
-
- <para>
- If this function is present in the driver and the driver is
- compiled as a loadable module then the driver gets the
- ability to be unloaded. This is an important feature if the
- hardware supports hot plug. But the ISA bus does not support
- hot plug, so this feature is not particularly important for
- the ISA devices. The ability to unload a driver may be
- useful when debugging it, but in many cases installation of
- the new version of the driver would be required only after
- the old version somehow wedges the system and reboot will be
- needed anyway, so the efforts spent on writing the detach
- routine may not be worth it. Another argument is that
- unloading would allow upgrading the drivers on a production
- machine seems to be mostly theoretical. Installing a new
- version of a driver is a dangerous operation which should
- never be performed on a production machine (and which is not
- permitted when the system is running in secure mode). Still
- the detach routine may be provided for the sake of
- completeness.
- </para>
-
- <para>
- The detach routine returns 0 if the driver was successfully
- detached or the error code otherwise.
- </para>
-
- <para>
- The logic of detach is a mirror of the attach. The first
- thing to do is to detach the driver from its kernel
- subsystem. If the device is currently open then the driver
- has two choices: refuse to be detached or forcibly close and
- proceed with detach. The choice used depends on the ability
- of the particular kernel subsystem to do a forced close and
- on the preferences of the driver's author. Generally the
- forced close seems to be the preferred alternative.
- <programlisting> struct xxx_softc *sc = device_get_softc(dev);
- int error;
-
- error = xxx_detach_subsystem(sc);
- if(error)
- return error;</programlisting>
- </para>
- <para>
- Next the driver may want to reset the hardware to some
- consistent state. That includes stopping any ongoing
- transfers, disabling the DMA channels and interrupts to
- avoid memory corruption by the device. For most of the
- drivers this is exactly what the shutdown routine does, so
- if it is included in the driver we can as well just call it.
- </para>
- <para><function>xxx_isa_shutdown(dev);</function></para>
-
- <para>
- And finally release all the resources and return success.
- <programlisting> xxx_free_resources(sc);
- return 0;</programlisting>
-
- </para>
- </sect1>
-
- <sect1>
- <title>xxx_isa_shutdown</title>
-
- <para>
- This routine is called when the system is about to be shut
- down. It is expected to bring the hardware to some
- consistent state. For most of the ISA devices no special
- action is required, so the function is not really necessary
- because the device will be re-initialized on reboot
- anyway. But some devices have to be shut down with a special
- procedure, to make sure that they will be properly detected
- after soft reboot (this is especially true for many devices
- with proprietary identification protocols). In any case
- disabling DMA and interrupts in the device registers and
- stopping any ongoing transfers is a good idea. The exact
- action depends on the hardware, so we don't consider it here
- in any details.
- </para>
-
- <para>
- xxx_intr
- </para>
-
- <para>
- The interrupt handler is called when an interrupt is
- received which may be from this particular device. The ISA
- bus does not support interrupt sharing (except some special
- cases) so in practice if the interrupt handler is called
- then the interrupt almost for sure came from its
- device. Still the interrupt handler must poll the device
- registers and make sure that the interrupt was generated by
- its device. If not it should just return.
- </para>
-
- <para>
- The old convention for the ISA drivers was getting the
- device unit number as an argument. It is obsolete, and the
- new drivers receive whatever argument was specified for them
- in the attach routine when calling
- <function>bus_setup_intr()</function>. By the new convention
- it should be the pointer to the structure softc. So the
- interrupt handler commonly starts as:
- </para>
-
- <programlisting>
- static void
- xxx_intr(struct xxx_softc *sc)
- {
-
- </programlisting>
-
- <para>
- It runs at the interrupt priority level specified by the
- interrupt type parameter of
- <function>bus_setup_intr()</function>. That means that all
- the other interrupts of the same type as well as all the
- software interrupts are disabled.
- </para>
-
- <para>
- To avoid races it is commonly written as a loop:
- </para>
-
- <programlisting>
- while(xxx_interrupt_pending(sc)) {
- xxx_process_interrupt(sc);
- xxx_acknowledge_interrupt(sc);
- } </programlisting>
-
- <para>
- The interrupt handler has to acknowledge interrupt to the
- device only but not to the interrupt controller, the system
- takes care of the latter.
- </para>
-
- </sect1>
-</chapter>
diff --git a/en_US.ISO8859-1/books/arch-handbook/kobj/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/kobj/chapter.sgml
deleted file mode 100644
index a9ea688b9a..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/kobj/chapter.sgml
+++ /dev/null
@@ -1,298 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD$
--->
-
-<chapter id="kernel-objects">
- <title>Kernel Objects</title>
-
- <para>Kernel Objects, or <firstterm>Kobj</firstterm> provides an
- object-oriented C programming system for the kernel. As such the
- data being operated on carries the description of how to operate
- on it. This allows operations to be added and removed from an
- interface at run time and without breaking binary
- compatibility.</para>
-
- <sect1>
- <title>Terminology</title>
-
- <variablelist>
- <varlistentry>
- <term>Object</term>
- <listitem><para>A set of data - data structure - data
- allocation.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>Method</term>
- <listitem>
- <para>An operation - function.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>Class</term>
- <listitem>
- <para>One or more methods.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>Interface</term>
- <listitem>
- <para>A standard set of one or more methods.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect1>
-
- <sect1>
- <title>Kobj Operation</title>
-
- <para>Kobj works by generating descriptions of methods. Each
- description holds a unique id as well as a default function. The
- description's address is used to uniquely identify the method
- within a class' method table.</para>
-
- <para>A class is built by creating a method table associating one
- or more functions with method descriptions. Before use the class
- is compiled. The compilation allocates a cache and associates it
- with the class. A unique id is assigned to each method
- description within the method table of the class if not already
- done so by another referencing class compilation. For every
- method to be used a function is generated by script to qualify
- arguments and automatically reference the method description for
- a lookup. The generated function looks up the method by using
- the unique id associated with the method description as a hash
- into the cache associated with the object's class. If the method
- is not cached the generated function proceeds to use the class'
- table to find the method. If the method is found then the
- associated function within the class is used; otherwise, the
- default function associated with the method description is
- used.</para>
-
- <para>These indirections can be visualized as the
- following:</para>
-
- <programlisting>object->cache<->class</programlisting>
-
- </sect1>
-
- <sect1>
- <title>Using Kobj</title>
-
- <sect2>
- <title>Structures</title>
-
- <programlisting>struct kobj_method</programlisting>
- </sect2>
-
- <sect2>
- <title>Functions</title>
-
- <programlisting>void kobj_class_compile(kobj_class_t cls);
-void kobj_class_compile_static(kobj_class_t cls, kobj_ops_t ops);
-void kobj_class_free(kobj_class_t cls);
-kobj_t kobj_create(kobj_class_t cls, struct malloc_type *mtype, int mflags);
-void kobj_init(kobj_t obj, kobj_class_t cls);
-void kobj_delete(kobj_t obj, struct malloc_type *mtype);</programlisting>
- </sect2>
-
- <sect2>
- <title>Macros</title>
-
- <programlisting>KOBJ_CLASS_FIELDS
-KOBJ_FIELDS
-DEFINE_CLASS(name, methods, size)
-KOBJMETHOD(NAME, FUNC)</programlisting>
- </sect2>
-
- <sect2>
- <title>Headers</title>
-
- <programlisting>&lt;sys/param.h>
-&lt;sys/kobj.h></programlisting>
- </sect2>
-
- <sect2>
- <title>Creating an interface template</title>
-
- <para>The first step in using Kobj is to create an
- Interface. Creating the interface involves creating a template
- that the script
- <filename>src/sys/kern/makeobjops.pl</filename> can use to
- generate the header and code for the method declarations and
- method lookup functions.</para>
-
- <para>Within this template the following keywords are used:
- <literal>#include</literal>, <literal>INTERFACE</literal>,
- <literal>CODE</literal>, <literal>METHOD</literal>,
- <literal>STATICMETHOD</literal>, and
- <literal>DEFAULT</literal>.</para>
-
- <para>The <literal>#include</literal> statement and what follows
- it is copied verbatim to the head of the generated code
- file.</para>
-
- <para>For example:</para>
-
- <programlisting>#include &lt;sys/foo.h></programlisting>
-
- <para>The <literal>INTERFACE</literal> keyword is used to define
- the interface name. This name is concatenated with each method
- name as [interface name]_[method name]. It's syntax is
- INTERFACE [interface name];.</para>
-
- <para>For example:</para>
-
- <programlisting>INTERFACE foo;</programlisting>
-
- <para>The <literal>CODE</literal> keyword copies its arguments
- verbatim into the code file. It's syntax is
- <literal>CODE { [whatever] };</literal></para>
-
- <para>For example:</para>
-
- <programlisting>CODE {
- struct foo * foo_alloc_null(struct bar *)
- {
- return NULL;
-}
-};</programlisting>
-
- <para>The <literal>METHOD</literal> keyword describes a method. It's syntax is
- <literal>METHOD [return type] [method name] { [object [,
- arguments]] };</literal></para>
-
- <para>For example:</para>
-
- <programlisting>METHOD int bar {
- struct object *;
- struct foo *;
- struct bar;
-};</programlisting>
-
- <para>The <literal>DEFAULT</literal> keyword may follow the
- <literal>METHOD</literal> keyword. It extends the
- <literal>METHOD</literal> key word to include the default
- function for method. The extended syntax is
- <literal>METHOD [return type] [method name] {
- [object; [other arguments]] }DEFAULT [default
- function];</literal></para>
-
- <para>For example:</para>
-
- <programlisting>METHOD int bar {
- struct object *;
- struct foo *;
- int bar;
-} DEFAULT foo_hack;</programlisting>
-
- <para>The <literal>STATICMETHOD</literal> keyword is used like
- the <literal>METHOD</literal> keyword except the kobj data isn't
- at the head of the object structure so casting to kobj_t would
- be incorrect. Instead <literal>STATICMETHOD</literal> relies on the Kobj data being
- referenced as 'ops'. This is also useful for calling
- methods directly out of a class's method table.</para>
-
- <para>Other complete examples:</para>
-
- <programlisting>src/sys/kern/bus_if.m
-src/sys/kern/device_if.m</programlisting>
-
- </sect2>
-
- <sect2>
- <title>Creating a Class</title>
-
- <para>The second step in using Kobj is to create a class. A
- class consists of a name, a table of methods, and the size of
- objects if Kobj's object handling facilities are used. To
- create the class use the macro
- <function>DEFINE_CLASS()</function>. To create the method
- table create an array of kobj_method_t terminated by a NULL
- entry. Each non-NULL entry may be created using the macro
- <function>KOBJMETHOD()</function>.</para>
-
- <para>For example:</para>
-
- <programlisting>DEFINE_CLASS(fooclass, foomethods, sizeof(struct foodata));
-
-kobj_method_t foomethods[] = {
- KOBJMETHOD(bar_doo, foo_doo),
- KOBJMETHOD(bar_foo, foo_foo),
- { NULL, NULL}
-};</programlisting>
-
- <para>The class must be <quote>compiled</quote>. Depending on
- the state of the system at the time that the class is to be
- initialized a statically allocated cache, <quote>ops
- table</quote> have to be used. This can be accomplished by
- declaring a <structname>struct kobj_ops</structname> and using
- <function>kobj_class_compile_static();</function> otherwise,
- <function>kobj_class_compile()</function> should be used.</para>
- </sect2>
-
- <sect2>
- <title>Creating an Object</title>
-
- <para>The third step in using Kobj involves how to define the
- object. Kobj object creation routines assume that Kobj data is
- at the head of an object. If this in not appropriate you will
- have to allocate the object yourself and then use
- <function>kobj_init()</function> on the Kobj portion of it;
- otherwise, you may use <function>kobj_create()</function> to
- allocate and initialize the Kobj portion of the object
- automatically. <function>kobj_init()</function> may also be
- used to change the class that an object uses.</para>
-
- <para>To integrate Kobj into the object you should use the macro
- KOBJ_FIELDS.</para>
-
- <para>For example</para>
-
- <programlisting>struct foo_data {
- KOBJ_FIELDS;
- foo_foo;
- foo_bar;
-};</programlisting>
- </sect2>
-
- <sect2>
- <title>Calling Methods</title>
-
- <para>The last step in using Kobj is to simply use the generated
- functions to use the desired method within the object's
- class. This is as simple as using the interface name and the
- method name with a few modifications. The interface name
- should be concatenated with the method name using a '_'
- between them, all in upper case.</para>
-
- <para>For example, if the interface name was foo and the method
- was bar then the call would be:</para>
-
- <programlisting>[return value = ] FOO_BAR(object [, other parameters]);</programlisting>
-
- </sect2>
-
- <sect2>
- <title>Cleaning Up</title>
-
- <para>When an object allocated through
- <function>kobj_create()</function> is no longer needed
- <function>kobj_delete()</function> may be called on it, and
- when a class is no longer being used
- <function>kobj_class_free()</function> may be called on it.</para>
- </sect2>
- </sect1>
-</chapter>
-
-<!--
- Local Variables:
- mode: sgml
- sgml-declaration: "../chapter.decl"
- sgml-indent-data: t
- sgml-omittag: nil
- sgml-always-quote-attributes: t
- sgml-parent-document: ("../book.sgml" "part" "chapter")
- End:
--->
diff --git a/en_US.ISO8859-1/books/arch-handbook/locking/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/locking/chapter.sgml
deleted file mode 100644
index 993d649ffc..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/locking/chapter.sgml
+++ /dev/null
@@ -1,333 +0,0 @@
-<!--
- The FreeBSD Documentation Project
- The FreeBSD SMP Next Generation Project
-
- $FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/locking/chapter.sgml,v 1.1 2000/11/29 04:15:17 jhb Exp $
--->
-
-<chapter id="locking">
- <title>Locking Notes</title>
-
- <para><emphasis>This chapter is maintained by the FreeBSD SMP Next
- Generation Project
- <email>freebsd-smp@FreeBSD.org</email>.</emphasis></para>
-
-
- <para>This document outlines the locking used in the FreeBSD kernel
- to permit effective multi-processing within the kernel. Locking
- can be achieved via several means. Data structures can be
- protected by mutexes or &man.lockmgr.9; locks. A few variables
- are protected simply by always using atomic operations to access
- them.</para>
-
- <sect1>
- <title>Mutexes</title>
-
- <para>A mutex is simply a lock used to guarantee mutual exclusion.
- Specifically, a mutex may only be owned by one entity at a time.
- If another entity wishes to obtain a mutex that is already
- owned, it must wait until the mutex is released. In the FreeBSD
- kernel, mutexes are owned by processes.</para>
-
- <para>Mutexes may be recursively acquired, but they are intended
- to be held for a short period of time. Specifically, one may
- not sleep while holding a mutex. If you need to hold a lock
- across a sleep, use a &man.lockmgr.9; lock.</para>
-
- <para>Each mutex has several properties of interest:</para>
-
- <variablelist>
- <varlistentry>
- <term>Variable Name</term>
- <listitem>
- <para>The name of the <type>struct mtx</type> variable in
- the kernel source.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Logical Name</term>
- <listitem>
- <para>The name of the mutex assigned to it by
- <function>mtx_init</function>. This name is displayed in
- KTR trace messages and witness errors and warnings and is
- used to distinguish mutexes in the witness code.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Type</term>
- <listitem>
- <para>The type of the mutex in terms of the
- <constant>MTX_*</constant> flags. The meaning for each
- flag is related to its meaning as documented in
- &man.mutex.9;.</para>
-
- <variablelist>
- <varlistentry>
- <term><constant>MTX_DEF</constant></term>
- <listitem>
- <para>A sleep mutex</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>MTX_SPIN</constant></term>
- <listitem>
- <para>A spin mutex</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>MTX_COLD</constant></term>
- <listitem>
- <para>This mutex is initialized very early. Thus, it
- must be declared via
- <function>MUTEX_DECLARE</function>, and the
- <constant>MTX_COLD</constant> flag must be passed to
- <function>mtx_init</function>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>MTX_TOPHALF</constant></term>
- <listitem>
- <para>This spin mutex does not disable
- interrupts.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>MTX_NORECURSE</constant></term>
- <listitem>
- <para>This mutex is not allowed to recurse.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Protectees</term>
- <listitem>
- <para>A list of data structures or data structure members
- that this entry protects. For data structure members, the
- name will be in the form of
- <structname/structure name/.<structfield/member name/.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Dependent Functions</term>
- <listitem>
- <para>Functions that can only be called if this mutex is
- held.</para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- <table frame="all" colsep="1" rowsep="1" pgwide="1">
- <title>Mutex List</title>
-
- <tgroup cols="5">
- <thead>
- <row>
- <entry>Variable Name</entry>
- <entry>Logical Name</entry>
- <entry>Type</entry>
- <entry>Protectees</entry>
- <entry>Dependent Functions</entry>
- </row>
- </thead>
-
- <!-- The scheduler lock -->
- <tbody>
- <row>
- <entry>sched_lock</entry>
- <entry><quote>sched lock</quote></entry>
- <entry>
- <constant>MTX_SPIN</constant> |
- <constant>MTX_COLD</constant>
- </entry>
- <entry>
- <varname>_gmonparam</varname>,
- <varname>cnt.v_swtch</varname>,
- <varname>cp_time</varname>,
- <varname>curpriority</varname>,
- <structname/mtx/.<structfield/mtx_blocked/,
- <structname/mtx/.<structfield/mtx_contested/,
- <structname/proc/.<structfield/p_contested/,
- <structname/proc/.<structfield/p_blocked/,
- <structname/proc/.<structfield/p_flag/
- (<constant>P_PROFIL</constant> XXX,
- <constant>P_INMEM</constant>,
- <constant>P_SINTR</constant>,
- <constant>P_TIMEOUT</constant>,
- <constant>P_SWAPINREQ</constant> XXX,
- <constant>P_INMEN</constant> XXX),
- <structname/proc/.<structfield/p_nice/,
- <structname/proc/.<structfield/p_procq/,
- <structname/proc/.<structfield/p_blocked/,
- <structname/proc/.<structfield/p_estcpu/,
- <structname/proc/.<structfield/p_nativepri/,
- <structname/proc/.<structfield/p_priority/,
- <structname/proc/.<structfield/p_usrpri/,
- <structname/proc/.<structfield/p_rtprio/,
- <structname/proc/.<structfield/p_rqindex/,
- <structname/proc/.<structfield/p_stats->p_prof/,
- <structname/proc/.<structfield/p_stats->p_ru/,
- <structname/proc/.<structfield/p_stat/,
- <structname/proc/.<structfield/p_cpticks/
- <structname/proc/.<structfield/p_iticks/,
- <structname/proc/.<structfield/p_uticks/,
- <structname/proc/.<structfield/p_sticks/,
- <structname/proc/.<structfield/p_swtime/,
- <structname/proc/.<structfield/p_slptime/,
- <structname/proc/.<structfield/p_runtime/,
- <structname/proc/.<structfield/p_pctcpu/,
- <structname/proc/.<structfield/p_oncpu/,
- <structname/proc/.<structfield/p_asleep/,
- <structname/proc/.<structfield/p_wchan/,
- <structname/proc/.<structfield/p_wmesg/,
- <structname/proc/.<structfield/p_slpq/,
- <structname/proc/.<structfield/p_vmspace/
- (XXX - in <function>statclock</function>),
- <varname>pscnt</varname>,
- <varname>slpque</varname>,
- <varname>itqueuebits</varname>,
- <varname>itqueues</varname>,
- <varname>rtqueuebits</varname>,
- <varname>rtqueues</varname>,
- <varname>queuebits</varname>,
- <varname>queues</varname>,
- <varname>idqueuebits</varname>,
- <varname>idqueues</varname>,
- <varname>switchtime</varname>,
- </entry>
- <entry>
- <function>setrunqueue</function>,
- <function>remrunqueue</function>,
- <function>mi_switch</function>,
- <function>chooseproc</function>,
- <function>schedclock</function>,
- <function>resetpriority</function>,
- <function>updatepri</function>,
- <function>maybe_resched</function>,
- <function>cpu_switch</function>,
- <function>cpu_throw</function>
- </entry>
- </row>
-
- <!-- The vm86 pcb lock -->
- <row>
- <entry>vm86pcb_lock</entry>
- <entry><quote>vm86pcb lock</quote></entry>
- <entry>
- <constant>MTX_DEF</constant> |
- <constant>MTX_COLD</constant>
- </entry>
- <entry>
- <varname>vm86pcb</varname>
- </entry>
- <entry>
- <function>vm86_bioscall</function>
- </entry>
- </row>
-
- <!-- Giant -->
- <row>
- <entry>Giant</entry>
- <entry><quote>Giant</quote></entry>
- <entry>
- <constant>MTX_DEF</constant> |
- <constant>MTX_COLD</constant>
- </entry>
- <entry>nearly everything</entry>
- <entry>lots</entry>
- </row>
-
- <!-- The callout lock -->
- <row>
- <entry>callout_lock</entry>
- <entry><quote>callout lock</quote></entry>
- <entry>
- <constant>MTX_SPIN</constant>
- </entry>
- <entry>
- <varname>callfree</varname>,
- <varname>callwheel</varname>,
- <varname>nextsoftcheck</varname>,
- <structname/proc/.<structfield/p_itcallout/,
- <structname/proc/.<structfield/p_slpcallout/,
- <varname>softticks</varname>,
- <varname>ticks</varname>
- </entry>
- <entry>
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect1>
-
- <sect1>
- <title>Lock Manager Locks</title>
-
- <para>Locks that are provided via the &man.lockmgr.9; interface
- are lock manager locks. These locks are reader-writer locks and
- may be held by a sleeping process.</para>
-
- <table>
- <title>&man.lockmgr.9; Lock List</title>
-
- <tgroup cols="2">
- <thead>
- <row>
- <entry>Variable Name</entry>
- <entry>Protectees</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><varname>allproc_lock</varname></entry>
- <entry>
- <varname>allproc</varname>
- <varname>zombproc</varname>
- <varname>pidhashtbl</varname>
- <structname/proc/.<structfield/p_list/
- <structname/proc/.<structfield/p_hash/
- <varname>nextpid</varname>
- </entry>
- <entry><varname>proctree_lock</varname></entry>
- <entry>
- <structname/proc/.<structfield/p_children/
- <structname/proc/.<structfield/p_sibling/
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect1>
-
- <sect1>
- <title>Atomically Protected Variables</title>
-
- <para>An atomically protected variable is a special variable that
- is not protected by an explicit lock. Instead, all data
- accesses to the variables use special atomic operations as
- described in &man.atomic.9;. Very few variables are treated
- this way, although other synchronization primitives such as
- mutexes are implemented with atomically protected
- variables.</para>
-
- <itemizedlist>
- <listitem>
- <para><varname>astpending</varname></para>
- </listitem>
-
- <listitem>
- <para><structname/mtx/.<structfield/mtx_lock/</para>
- </listitem>
- </itemizedlist>
- </sect1>
-</chapter>
diff --git a/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml
deleted file mode 100644
index ca94063864..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/pci/chapter.sgml
+++ /dev/null
@@ -1,372 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/pci/chapter.sgml,v 1.2 2001/04/09 00:33:42 dd Exp $
--->
-
-<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>
-
- <sect1>
- <title>Bus Resources</title>
-
- <para>FreeBSD provides an object-oriented mechanism for requesting
- resources from a parent bus. Almost all devices will be a child
- member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these
- devices need to acquire resources from their parent bus (such as
- memory segments, interrupt lines, or DMA channels).</para>
-
- <sect2>
- <title>Base Address Registers</title>
-
- <para>To do anything particularly useful with a PCI device you
- will need to obtain the <emphasis>Base Address
- Registers</emphasis> (BARs) from the PCI Configuration space.
- The PCI-specific details of obtaining the BAR is abstracted in
- the <function>bus_alloc_resource()</function> function.</para>
-
- <para>For example, a typical driver might have something similar
- to this in the <function>attach()</function> function. : </para>
-
-<programlisting> sc->bar0id = 0x10;
- sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar0id),
- 0, ~0, 1, RF_ACTIVE);
- if (sc->bar0res == NULL) {
- uprintf("Memory allocation of PCI base register 0 failed!\n");
- error = ENXIO;
- goto fail1;
- }
-
- sc->bar1id = 0x14;
- sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar1id),
- 0, ~0, 1, RF_ACTIVE);
- if (sc->bar1res == NULL) {
- uprintf("Memory allocation of PCI base register 1 failed!\n");
- error = ENXIO;
- goto fail2;
- }
- sc->bar0_bt = rman_get_bustag(sc->bar0res);
- sc->bar0_bh = rman_get_bushandle(sc->bar0res);
- sc->bar1_bt = rman_get_bustag(sc->bar1res);
- sc->bar1_bh = rman_get_bushandle(sc->bar1res);
-
-</programlisting>
-
- <para>Handles for each base address register are kept in the
- <structname>softc</structname> structure so that they can be
- used to write to the device later.</para>
-
- <para>These handles can then be used to read or write from the
- device registers with the <function>bus_space_*</function>
- functions. For example, a driver might contain a shorthand
- function to read from a board specific register like this :
- </para>
-
-<programlisting>uint16_t
-board_read(struct ni_softc *sc, uint16_t address) {
- return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
-}
-</programlisting>
-
- <para>Similarly, one could write to the registers with : </para>
-
-<programlisting>void
-board_write(struct ni_softc *sc, uint16_t address, uint16_t value) {
- bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);
-}
-</programlisting>
-
- <para>These functions exist in 8bit, 16bit, and 32bit versions
- and you should use
- <function>bus_space_{read|write}_{1|2|4}</function>
- accordingly.</para>
-
- </sect2>
- <sect2>
- <title>Interrupts</title>
-
- <para>Interrupts are allocated from the object-oriented bus code
- in a way similar to the memory resources. First an IRQ
- resource must be allocated from the parent bus, and then the
- interrupt handler must be setup to deal with this IRQ.</para>
-
- <para>Again, a sample from a device
- <function>attach()</function> function says more than
- words.</para>
-
-<programlisting>/* Get the IRQ resource */
-
- sc->irqid = 0x0;
- sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &amp;(sc->irqid),
- 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
- if (sc->irqres == NULL) {
- uprintf("IRQ allocation failed!\n");
- error = ENXIO;
- goto fail3;
- }
-
- /* Now we should setup the interrupt handler */
-
- error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,
- my_handler, sc, &amp;(sc->handler));
- if (error) {
- printf("Couldn't set up irq\n");
- goto fail4;
- }
-
- sc->irq_bt = rman_get_bustag(sc->irqres);
- sc->irq_bh = rman_get_bushandle(sc->irqres);
-</programlisting>
-
- </sect2>
-
- <sect2>
- <title>DMA</title>
- <para>On the PC, peripherals that want to do bus-mastering DMA
- must deal with physical addresses. This is a problem since
- FreeBSD uses virtual memory and deals almost exclusively with
- virtual addresses. Fortunately, there is a function,
- <function>vtophys()</function> to help.</para>
-
-<programlisting>#include &lt;vm/vm.h&gt;
-#include &lt;vm/pmap.h&gt;
-
-#define vtophys(virtual_address) (...)
-</programlisting>
-
- <para>The solution is a bit different on the alpha however, and
- what we really want is a function called
- <function>vtobus()</function>.</para>
-
-<programlisting>#if defined(__alpha__)
-#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
-#else
-#define vtobus(va) vtophys(va)
-#endif
-</programlisting>
-
- </sect2>
-
- <sect2>
- <title>Deallocating Resources</title>
-
- <para>It's very important to deallocate all of the resources
- that were allocated during <function>attach()</function>.
- Care must be taken to deallocate the correct stuff even on a
- failure condition so that the system will remain useable while
- your driver dies.</para>
-
- </sect2>
- </sect1>
-
-</chapter>
-
-
diff --git a/en_US.ISO8859-1/books/arch-handbook/scsi/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/scsi/chapter.sgml
deleted file mode 100644
index 072aea5499..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/scsi/chapter.sgml
+++ /dev/null
@@ -1,1983 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: doc/en_US.ISO8859-1/books/developers-handbook/scsi/chapter.sgml,v 1.4 2001/06/13 11:35:56 tom Exp $
--->
-
-<chapter id="scsi">
- <title>Common Access Method SCSI Controllers</title>
-
- <para><emphasis>This chapter was written by &a.babkin;
- Modifications for the handbook made by
- &a.murray;.</emphasis></para>
-
- <sect1>
- <title>Synopsis</title>
-
- <para>This document assumes that the reader has a general
- understanding of device drivers in FreeBSD and of the SCSI
- protocol. Much of the information in this document was
- extracted from the drivers :</para>
-
- <itemizedlist>
-
- <listitem><para>ncr (<filename>/sys/pci/ncr.c</filename>) by
- Wolfgang Stanglmeier and Stefan Esser</para></listitem>
-
- <listitem><para>sym (<filename>/sys/pci/sym.c</filename>) by
- Gerard Roudier</para></listitem>
-
- <listitem><para>aic7xxx
- (<filename>/sys/dev/aic7xxx/aic7xxx.c</filename>) by Justin
- T. Gibbs</para></listitem>
-
- </itemizedlist>
-
- <para>and from the CAM code itself (by Justing T. Gibbs, see
- <filename>/sys/cam/*</filename>). When some solution looked the
- most logical and was essentially verbatim extracted from the code
- by Justin Gibbs, I marked it as "recommended".</para>
-
- <para>The document is illustrated with examples in
- pseudo-code. Although sometimes the examples have many details
- and look like real code, it's still pseudo-code. It was written
- to demonstrate the concepts in an understandable way. For a real
- driver other approaches may be more modular and efficient. It
- also abstracts from the hardware details, as well as issues that
- would cloud the demonstrated concepts or that are supposed to be
- described in the other chapters of the developers handbook. Such
- details are commonly shown as calls to functions with descriptive
- names, comments or pseudo-statements. Fortunately real life
- full-size examples with all the details can be found in the real
- drivers.</para>
-
- </sect1>
-
- <sect1>
- <title>General architecture</title>
-
- <para>CAM stands for Common Access Method. It's a generic way to
- address the I/O buses in a SCSI-like way. This allows a
- separation of the generic device drivers from the drivers
- controlling the I/O bus: for example the disk driver becomes able
- to control disks on both SCSI, IDE, and/or any other bus so the
- disk driver portion does not have to be rewritten (or copied and
- modified) for every new I/O bus. Thus the two most important
- active entities are:</para>
-
- <itemizedlist>
- <listitem><para><emphasis>Peripheral Modules</emphasis> - a
- driver for peripheral devices (disk, tape, CDROM,
- etc.)</para></listitem>
- <listitem><para><emphasis>SCSI Interface Modules </emphasis>(SIM)
- - a Host Bus Adapter drivers for connecting to an I/O bus such
- as SCSI or IDE.</para></listitem>
- </itemizedlist>
-
- <para>A peripheral driver receives requests from the OS, converts
- them to a sequence of SCSI commands and passes these SCSI
- commands to a SCSI Interface Module. The SCSI Interface Module
- is responsible for passing these commands to the actual hardware
- (or if the actual hardware is not SCSI but, for example, IDE
- then also converting the SCSI commands to the native commands of
- the hardware).</para>
-
- <para>Because we are interested in writing a SCSI adapter driver
- here, from this point on we will consider everything from the
- SIM standpoint.</para>
-
- <para>A typical SIM driver needs to include the following
- CAM-related header files:</para>
-
-<programlisting>#include &lt;cam/cam.h&gt;
-#include &lt;cam/cam_ccb.h&gt;
-#include &lt;cam/cam_sim.h&gt;
-#include &lt;cam/cam_xpt_sim.h&gt;
-#include &lt;cam/cam_debug.h&gt;
-#include &lt;cam/scsi/scsi_all.h&gt;</programlisting>
-
- <para>The first thing each SIM driver must do is register itself
- with the CAM subsystem. This is done during the driver's
- <function>xxx_attach()</function> function (here and further
- xxx_ is used to denote the unique driver name prefix). The
- <function>xxx_attach()</function> function itself is called by
- the system bus auto-configuration code which we don't describe
- here.</para>
-
- <para>This is achieved in multiple steps: first it's necessary to
- allocate the queue of requests associated with this SIM:</para>
-
-<programlisting> struct cam_devq *devq;
-
- if(( devq = cam_simq_alloc(SIZE) )==NULL) {
- error; /* some code to handle the error */
- }</programlisting>
-
- <para>Here SIZE is the size of the queue to be allocated, maximal
- number of requests it could contain. It's the number of requests
- that the SIM driver can handle in parallel on one SCSI
- card. Commonly it can be calculated as:</para>
-
-<programlisting>SIZE = NUMBER_OF_SUPPORTED_TARGETS * MAX_SIMULTANEOUS_COMMANDS_PER_TARGET</programlisting>
-
- <para>Next we create a descriptor of our SIM:</para>
-
-<programlisting> struct cam_sim *sim;
-
- if(( sim = cam_sim_alloc(action_func, poll_func, driver_name,
- softc, unit, max_dev_transactions,
- max_tagged_dev_transactions, devq) )==NULL) {
- cam_simq_free(devq);
- error; /* some code to handle the error */
- }</programlisting>
-
- <para>Note that if we are not able to create a SIM descriptor we
- free the <structname>devq</structname> also because we can do
- nothing else with it and we want to conserve memory.</para>
-
- <para>If a SCSI card has multiple SCSI buses on it then each bus
- requires its own <structname>cam_sim</structname>
- structure.</para>
-
- <para>An interesting question is what to do if a SCSI card has
- more than one SCSI bus, do we need one
- <structname>devq</structname> structure per card or per SCSI
- bus? The answer given in the comments to the CAM code is:
- either way, as the driver's author prefers.</para>
-
- <para>The arguments are :
- <itemizedlist>
-
- <listitem><para><function>action_func</function> - pointer to
- the driver's <function>xxx_action</function> function.
- <funcSynopsis><funcPrototype>
- <funcDef>static void
- <function>xxx_action</function>
- </funcDef>
- <paramdef>
- <parameter>struct cam_sim *sim</parameter>,
- <parameter>union ccb *ccb</parameter>
- </paramdef>
- </funcPrototype></funcSynopsis>
- </para></listitem>
-
- <listitem><para><function>poll_func</function> - pointer to
- the driver's <function>xxx_poll()</function>
- <funcSynopsis><funcPrototype>
- <funcDef>static void
- <function>xxx_poll</function>
- </funcDef>
- <paramdef>
- <parameter>struct cam_sim *sim</parameter>
- </paramdef>
- </funcPrototype></funcSynopsis>
- </para></listitem>
-
- <listitem><para>driver_name - the name of the actual driver,
- such as "ncr" or "wds"</para></listitem>
-
- <listitem><para><structName>softc</structName> - pointer to the
- driver's internal descriptor for this SCSI card. This
- pointer will be used by the driver in future to get private
- data.</para></listitem>
-
- <listitem><para>unit - the controller unit number, for example
- for controller "wds0" this number will be
- 0</para></listitem>
-
- <listitem><para>max_dev_transactions - maximal number of
- simultaneous transactions per SCSI target in the non-tagged
- mode. This value will be almost universally equal to 1, with
- possible exceptions only for the non-SCSI cards. Also the
- drivers that hope to take advantage by preparing one
- transaction while another one is executed may set it to 2
- but this does not seem to be worth the
- complexity.</para></listitem>
-
- <listitem><para>max_tagged_dev_transactions - the same thing,
- but in the tagged mode. Tags are the SCSI way to initiate
- multiple transactions on a device: each transaction is
- assigned a unique tag and the transaction is sent to the
- device. When the device completes some transaction it sends
- back the result together with the tag so that the SCSI
- adapter (and the driver) can tell which transaction was
- completed. This argument is also known as the maximal tag
- depth. It depends on the abilities of the SCSI
- adapter.</para></listitem>
- </itemizedlist>
- </para>
-
- <para>Finally we register the SCSI buses associated with our SCSI
- adapter:</para>
-
-<programlisting> if(xpt_bus_register(sim, bus_number) != CAM_SUCCESS) {
- cam_sim_free(sim, /*free_devq*/ TRUE);
- error; /* some code to handle the error */
- }</programlisting>
-
- <para>If there is one <structName>devq</structName> structure per
- SCSI bus (i.e. we consider a card with multiple buses as
- multiple cards with one bus each) then the bus number will
- always be 0, otherwise each bus on the SCSI card should be get a
- distinct number. Each bus needs its own separate structure
- cam_sim.</para>
-
- <para>After that our controller is completely hooked to the CAM
- system. The value of <structName>devq</structName> can be
- discarded now: sim will be passed as an argument in all further
- calls from CAM and devq can be derived from it.</para>
-
- <para>CAM provides the framework for such asynchronous
- events. Some events originate from the lower levels (the SIM
- drivers), some events originate from the peripheral drivers,
- some events originate from the CAM subsystem itself. Any driver
- can register callbacks for some types of the asynchronous
- events, so that it would be notified if these events
- occur.</para>
-
- <para>A typical example of such an event is a device reset. Each
- transaction and event identifies the devices to which it applies
- by the means of "path". The target-specific events normally
- occur during a transaction with this device. So the path from
- that transaction may be re-used to report this event (this is
- safe because the event path is copied in the event reporting
- routine but not deallocated nor passed anywhere further). Also
- it's safe to allocate paths dynamically at any time including
- the interrupt routines, although that incurs certain overhead,
- and a possible problem with this approach is that there may be
- no free memory at that time. For a bus reset event we need to
- define a wildcard path including all devices on the bus. So we
- can create the path for the future bus reset events in advance
- and avoid problems with the future memory shortage:</para>
-
-<programlisting> struct cam_path *path;
-
- if(xpt_create_path(&amp;path, /*periph*/NULL,
- cam_sim_path(sim), CAM_TARGET_WILDCARD,
- CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
- xpt_bus_deregister(cam_sim_path(sim));
- cam_sim_free(sim, /*free_devq*/TRUE);
- error; /* some code to handle the error */
- }
-
- softc->wpath = path;
- softc->sim = sim;</programlisting>
-
- <para>As you can see the path includes:</para>
-
- <itemizedlist>
- <listitem><para>ID of the peripheral driver (NULL here because we have
- none)</para></listitem>
-
- <listitem><para>ID of the SIM driver
- (<function>cam_sim_path(sim)</function>)</para></listitem>
-
- <listitem><para>SCSI target number of the device (CAM_TARGET_WILDCARD
- means "all devices")</para></listitem>
-
- <listitem><para>SCSI LUN number of the subdevice (CAM_LUN_WILDCARD means
- "all LUNs")</para></listitem>
- </itemizedlist>
-
- <para>If the driver can't allocate this path it won't be able to
- work normally, so in that case we dismantle that SCSI
- bus.</para>
-
- <para>And we save the path pointer in the
- <structName>softc</structName> structure for future use. After
- that we save the value of sim (or we can also discard it on the
- exit from <function>xxx_probe()</function> if we wish).</para>
-
- <para>That's all for a minimalistic initialization. To do things
- right there is one more issue left. </para>
-
- <para>For a SIM driver there is one particularly interesting
- event: when a target device is considered lost. In this case
- resetting the SCSI negotiations with this device may be a good
- idea. So we register a callback for this event with CAM. The
- request is passed to CAM by requesting CAM action on a CAM
- control block for this type of request:</para>
-
-<programlisting> struct ccb_setasync csa;
-
- xpt_setup_ccb(&amp;csa.ccb_h, path, /*priority*/5);
- csa.ccb_h.func_code = XPT_SASYNC_CB;
- csa.event_enable = AC_LOST_DEVICE;
- csa.callback = xxx_async;
- csa.callback_arg = sim;
- xpt_action((union ccb *)&amp;csa);</programlisting>
-
- <para>Now we take a look at the <function>xxx_action()</function>
- and <function>xxx_poll()</function> driver entry points.</para>
-
- <para>
- <funcSynopsis><funcPrototype>
- <funcDef>static void
- <function>xxx_action</function>
- </funcDef>
- <paramdef>
- <parameter>struct cam_sim *sim</parameter>,
- <parameter>union ccb *ccb</parameter>
- </paramdef>
- </funcPrototype></funcSynopsis>
- </para>
-
- <para>Do some action on request of the CAM subsystem. Sim
- describes the SIM for the request, CCB is the request
- itself. CCB stands for "CAM Control Block". It is a union of
- many specific instances, each describing arguments for some type
- of transactions. All of these instances share the CCB header
- where the common part of arguments is stored.</para>
-
- <para>CAM supports the SCSI controllers working in both initiator
- ("normal") mode and target (simulating a SCSI device) mode. Here
- we only consider the part relevant to the initiator mode.</para>
-
- <para>There are a few function and macros (in other words,
- methods) defined to access the public data in the struct sim:</para>
-
- <itemizedlist>
- <listitem><para><function>cam_sim_path(sim)</function> - the
- path ID (see above)</para></listitem>
-
- <listitem><para><function>cam_sim_name(sim)</function> - the
- name of the sim</para></listitem>
-
- <listitem><para><function>cam_sim_softc(sim)</function> - the
- pointer to the softc (driver private data)
- structure</para></listitem>
-
- <listitem><para><function> cam_sim_unit(sim)</function> - the
- unit number</para></listitem>
-
- <listitem><para><function> cam_sim_bus(sim)</function> - the bus
- ID</para></listitem>
- </itemizedlist>
-
- <para>To identify the device, <function>xxx_action()</function> can
- get the unit number and pointer to its structure softc using
- these functions.</para>
-
- <para>The type of request is stored in
- <structField>ccb-&gt;ccb_h.func_code</structField>. So generally
- <function>xxx_action()</function> consists of a big
- switch:</para>
-
-<programlisting> struct xxx_softc *softc = (struct xxx_softc *) cam_sim_softc(sim);
- struct ccb_hdr *ccb_h = &amp;ccb->ccb_h;
- int unit = cam_sim_unit(sim);
- int bus = cam_sim_bus(sim);
-
- switch(ccb_h->func_code) {
- case ...:
- ...
- default:
- ccb_h->status = CAM_REQ_INVALID;
- xpt_done(ccb);
- break;
- }</programlisting>
-
- <para>As can be seen from the default case (if an unknown command
- was received) the return code of the command is set into
- <structField>ccb-&gt;ccb_h.status</structField> and the completed
- CCB is returned back to CAM by calling
- <function>xpt_done(ccb)</function>. </para>
-
- <para><function>xpt_done()</function> does not have to be called
- from <function>xxx_action()</function>: For example an I/O
- request may be enqueued inside the SIM driver and/or its SCSI
- controller. Then when the device would post an interrupt
- signaling that the processing of this request is complete
- <function>xpt_done()</function> may be called from the interrupt
- handling routine.</para>
-
- <para>Actually, the CCB status is not only assigned as a return
- code but a CCB has some status all the time. Before CCB is
- passed to the <function>xxx_action()</function> routine it gets
- the status CCB_REQ_INPROG meaning that it's in progress. There
- are a surprising number of status values defined in
- <filename>/sys/cam/cam.h</filename> which should be able to
- represent the status of a request in great detail. More
- interesting yet, the status is in fact a "bitwise or" of an
- enumerated status value (the lower 6 bits) and possible
- additional flag-like bits (the upper bits). The enumerated
- values will be discussed later in more detail. The summary of
- them can be found in the Errors Summary section. The possible
- status flags are:</para>
-
- <itemizedlist>
-
- <listitem><para><emphasis>CAM_DEV_QFRZN</emphasis> - if the
- SIM driver gets a serious error (for example, the device does
- not respond to the selection or breaks the SCSI protocol) when
- processing a CCB it should freeze the request queue by calling
- <function>xpt_freeze_simq()</function>, return the other
- enqueued but not processed yet CCBs for this device back to
- the CAM queue, then set this flag for the troublesome CCB and
- call <function>xpt_done()</function>. This flag causes the CAM
- subsystem to unfreeze the queue after it handles the
- error.</para></listitem>
-
- <listitem><para><emphasis>CAM_AUTOSNS_VALID</emphasis> - if
- the device returned an error condition and the flag
- CAM_DIS_AUTOSENSE is not set in CCB the SIM driver must
- execute the REQUEST SENSE command automatically to extract the
- sense (extended error information) data from the device. If
- this attempt was successful the sense data should be saved in
- the CCB and this flag set.</para></listitem>
-
- <listitem><para><emphasis>CAM_RELEASE_SIMQ</emphasis> - like
- CAM_DEV_QFRZN but used in case there is some problem (or
- resource shortage) with the SCSI controller itself. Then all
- the future requests to the controller should be stopped by
- <function>xpt_freeze_simq()</function>. The controller queue
- will be restarted after the SIM driver overcomes the shortage
- and informs CAM by returning some CCB with this flag
- set.</para></listitem>
-
- <listitem><para><emphasis>CAM_SIM_QUEUED</emphasis> - when SIM
- puts a CCB into its request queue this flag should be set (and
- removed when this CCB gets dequeued before being returned back
- to CAM). This flag is not used anywhere in the CAM code now,
- so its purpose is purely diagnostic.</para></listitem>
-
- </itemizedlist>
-
- <para>The function <function>xxx_action()</function> is not
- allowed to sleep, so all the synchronization for resource access
- must be done using SIM or device queue freezing. Besides the
- aforementioned flags the CAM subsystem provides functions
- <function>xpt_selease_simq()</function> and
- <function>xpt_release_devq()</function> to unfreeze the queues
- directly, without passing a CCB to CAM.</para>
-
- <para>The CCB header contains the following fields:</para>
-
- <itemizedlist>
-
- <listitem><para><emphasis>path</emphasis> - path ID for the
- request</para></listitem>
-
- <listitem><para><emphasis>target_id</emphasis> - target device
- ID for the request</para></listitem>
-
- <listitem><para><emphasis>target_lun</emphasis> - LUN ID of
- the target device</para></listitem>
-
- <listitem><para><emphasis>timeout</emphasis> - timeout
- interval for this command, in milliseconds</para></listitem>
-
- <listitem><para><emphasis>timeout_ch</emphasis> - a
- convenience place for the SIM driver to store the timeout handle
- (the CAM subsystem itself does not make any assumptions about
- it)</para></listitem>
-
- <listitem><para><emphasis>flags</emphasis> - various bits of
- information about the request spriv_ptr0, spriv_ptr1 - fields
- reserved for private use by the SIM driver (such as linking to
- the SIM queues or SIM private control blocks); actually, they
- exist as unions: spriv_ptr0 and spriv_ptr1 have the type (void
- *), spriv_field0 and spriv_field1 have the type unsigned long,
- sim_priv.entries[0].bytes and sim_priv.entries[1].bytes are byte
- arrays of the size consistent with the other incarnations of the
- union and sim_priv.bytes is one array, twice
- bigger.</para></listitem>
-
- </itemizedlist>
-
- <para>The recommended way of using the SIM private fields of CCB
- is to define some meaningful names for them and use these
- meaningful names in the driver, like:</para>
-
-<programlisting>#define ccb_some_meaningful_name sim_priv.entries[0].bytes
-#define ccb_hcb spriv_ptr1 /* for hardware control block */</programlisting>
-
- <para>The most common initiator mode requests are:</para>
- <itemizedlist>
- <listitem><para><emphasis>XPT_SCSI_IO</emphasis> - execute an
- I/O transaction</para>
-
- <para>The instance "struct ccb_scsiio csio" of the union ccb is
- used to transfer the arguments. They are:</para>
-
- <itemizedlist>
- <listitem><para><emphasis>cdb_io</emphasis> - pointer to
- the SCSI command buffer or the buffer
- itself</para></listitem>
-
- <listitem><para><emphasis>cdb_len</emphasis> - SCSI
- command length</para></listitem>
-
- <listitem><para><emphasis>data_ptr</emphasis> - pointer to
- the data buffer (gets a bit complicated if scatter/gather is
- used)</para></listitem>
-
- <listitem><para><emphasis>dxfer_len</emphasis> - length of
- the data to transfer</para></listitem>
-
- <listitem><para><emphasis>sglist_cnt</emphasis> - counter
- of the scatter/gather segments</para></listitem>
-
- <listitem><para><emphasis>scsi_status</emphasis> - place
- to return the SCSI status</para></listitem>
-
- <listitem><para><emphasis>sense_data</emphasis> - buffer
- for the SCSI sense information if the command returns an
- error (the SIM driver is supposed to run the REQUEST SENSE
- command automatically in this case if the CCB flag
- CAM_DIS_AUTOSENSE is not set)</para></listitem>
-
- <listitem><para><emphasis>sense_len</emphasis> - the
- length of that buffer (if it happens to be higher than size
- of sense_data the SIM driver must silently assume the
- smaller value) resid, sense_resid - if the transfer of data
- or SCSI sense returned an error these are the returned
- counters of the residual (not transferred) data. They do not
- seem to be especially meaningful, so in a case when they are
- difficult to compute (say, counting bytes in the SCSI
- controller's FIFO buffer) an approximate value will do as
- well. For a successfully completed transfer they must be set
- to zero.</para></listitem>
-
- <listitem><para><emphasis>tag_action</emphasis> - the kind
- of tag to use:
-
- <itemizedlist>
- <listitem><para>CAM_TAG_ACTION_NONE - don't use tags for this
- transaction</para></listitem>
- <listitem><para>MSG_SIMPLE_Q_TAG, MSG_HEAD_OF_Q_TAG,
- MSG_ORDERED_Q_TAG - value equal to the appropriate tag
- message (see /sys/cam/scsi/scsi_message.h); this gives only
- the tag type, the SIM driver must assign the tag value
- itself</para></listitem>
- </itemizedlist>
-
- </para></listitem>
-
- </itemizedlist>
-
- <para>The general logic of handling this request is the
- following:</para>
-
- <para>The first thing to do is to check for possible races, to
- make sure that the command did not get aborted when it was
- sitting in the queue:</para>
-
-<programlisting> struct ccb_scsiio *csio = &amp;ccb->csio;
-
- if ((ccb_h->status &amp; CAM_STATUS_MASK) != CAM_REQ_INPROG) {
- xpt_done(ccb);
- return;
- }</programlisting>
-
- <para>Also we check that the device is supported at all by our
- controller:</para>
-
-<programlisting> if(ccb_h->target_id > OUR_MAX_SUPPORTED_TARGET_ID
- || cch_h->target_id == OUR_SCSI_CONTROLLERS_OWN_ID) {
- ccb_h->status = CAM_TID_INVALID;
- xpt_done(ccb);
- return;
- }
- if(ccb_h->target_lun > OUR_MAX_SUPPORTED_LUN) {
- ccb_h->status = CAM_LUN_INVALID;
- xpt_done(ccb);
- return;
- }</programlisting>
-
- <para>Then allocate whatever data structures (such as
- card-dependent hardware control block) we need to process this
- request. If we can't then freeze the SIM queue and remember
- that we have a pending operation, return the CCB back and ask
- CAM to re-queue it. Later when the resources become available
- the SIM queue must be unfrozen by returning a ccb with the
- CAM_SIMQ_RELEASE bit set in its status. Otherwise, if all went
- well, link the CCB with the hardware control block (HCB) and
- mark it as queued.</para>
-
-<programlisting> struct xxx_hcb *hcb = allocate_hcb(softc, unit, bus);
-
- if(hcb == NULL) {
- softc->flags |= RESOURCE_SHORTAGE;
- xpt_freeze_simq(sim, /*count*/1);
- ccb_h->status = CAM_REQUEUE_REQ;
- xpt_done(ccb);
- return;
- }
-
- hcb->ccb = ccb; ccb_h->ccb_hcb = (void *)hcb;
- ccb_h->status |= CAM_SIM_QUEUED;</programlisting>
-
- <para>Extract the target data from CCB into the hardware control
- block. Check if we are asked to assign a tag and if yes then
- generate an unique tag and build the SCSI tag messages. The
- SIM driver is also responsible for negotiations with the
- devices to set the maximal mutually supported bus width,
- synchronous rate and offset.</para>
-
-<programlisting> hcb->target = ccb_h->target_id; hcb->lun = ccb_h->target_lun;
- generate_identify_message(hcb);
- if( ccb_h->tag_action != CAM_TAG_ACTION_NONE )
- generate_unique_tag_message(hcb, ccb_h->tag_action);
- if( !target_negotiated(hcb) )
- generate_negotiation_messages(hcb);</programlisting>
-
- <para>Then set up the SCSI command. The command storage may be
- specified in the CCB in many interesting ways, specified by
- the CCB flags. The command buffer can be contained in CCB or
- pointed to, in the latter case the pointer may be physical or
- virtual. Since the hardware commonly needs physical address we
- always convert the address to the physical one.</para>
-
- <para>A NOT-QUITE RELATED NOTE: Normally this is done by a call
- to vtophys(), but for the PCI device (which account for most
- of the SCSI controllers now) drivers' portability to the Alpha
- architecture the conversion must be done by vtobus() instead
- due to special Alpha quirks. [IMHO it would be much better to
- have two separate functions, vtop() and ptobus() then vtobus()
- would be a simple superposition of them.] In case if a
- physical address is requested it's OK to return the CCB with
- the status CAM_REQ_INVALID, the current drivers do that. But
- it's also possible to compile the Alpha-specific piece of
- code, as in this example (there should be a more direct way to
- do that, without conditional compilation in the drivers). If
- necessary a physical address can be also converted or mapped
- back to a virtual address but with big pain, so we don't do
- that.</para>
-
-<programlisting> if(ccb_h->flags &amp; CAM_CDB_POINTER) {
- /* CDB is a pointer */
- if(!(ccb_h->flags &amp; CAM_CDB_PHYS)) {
- /* CDB pointer is virtual */
- hcb->cmd = vtobus(csio->cdb_io.cdb_ptr);
- } else {
- /* CDB pointer is physical */
-#if defined(__alpha__)
- hcb->cmd = csio->cdb_io.cdb_ptr | alpha_XXX_dmamap_or ;
-#else
- hcb->cmd = csio->cdb_io.cdb_ptr ;
-#endif
- }
- } else {
- /* CDB is in the ccb (buffer) */
- hcb->cmd = vtobus(csio->cdb_io.cdb_bytes);
- }
- hcb->cmdlen = csio->cdb_len;</programlisting>
-
- <para>Now it's time to set up the data. Again, the data storage
- may be specified in the CCB in many interesting ways,
- specified by the CCB flags. First we get the direction of the
- data transfer. The simplest case is if there is no data to
- transfer:</para>
-
-<programlisting> int dir = (ccb_h->flags &amp; CAM_DIR_MASK);
-
- if (dir == CAM_DIR_NONE)
- goto end_data;</programlisting>
-
- <para>Then we check if the data is in one chunk or in a
- scatter-gather list, and the addresses are physical or
- virtual. The SCSI controller may be able to handle only a
- limited number of chunks of limited length. If the request
- hits this limitation we return an error. We use a special
- function to return the CCB to handle in one place the HCB
- resource shortages. The functions to add chunks are
- driver-dependent, and here we leave them without detailed
- implementation. See description of the SCSI command (CDB)
- handling for the details on the address-translation issues.
- If some variation is too difficult or impossible to implement
- with a particular card it's OK to return the status
- CAM_REQ_INVALID. Actually, it seems like the scatter-gather
- ability is not used anywhere in the CAM code now. But at least
- the case for a single non-scattered virtual buffer must be
- implemented, it's actively used by CAM.</para>
-
-<programlisting> int rv;
-
- initialize_hcb_for_data(hcb);
-
- if((!(ccb_h->flags &amp; CAM_SCATTER_VALID)) {
- /* single buffer */
- if(!(ccb_h->flags &amp; CAM_DATA_PHYS)) {
- rv = add_virtual_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);
- }
- } else {
- rv = add_physical_chunk(hcb, csio->data_ptr, csio->dxfer_len, dir);
- }
- } else {
- int i;
- struct bus_dma_segment *segs;
- segs = (struct bus_dma_segment *)csio->data_ptr;
-
- if ((ccb_h->flags &amp; CAM_SG_LIST_PHYS) != 0) {
- /* The SG list pointer is physical */
- rv = setup_hcb_for_physical_sg_list(hcb, segs, csio->sglist_cnt);
- } else if (!(ccb_h->flags &amp; CAM_DATA_PHYS)) {
- /* SG buffer pointers are virtual */
- for (i = 0; i < csio->sglist_cnt; i++) {
- rv = add_virtual_chunk(hcb, segs[i].ds_addr,
- segs[i].ds_len, dir);
- if (rv != CAM_REQ_CMP)
- break;
- }
- } else {
- /* SG buffer pointers are physical */
- for (i = 0; i < csio->sglist_cnt; i++) {
- rv = add_physical_chunk(hcb, segs[i].ds_addr,
- segs[i].ds_len, dir);
- if (rv != CAM_REQ_CMP)
- break;
- }
- }
- }
- if(rv != CAM_REQ_CMP) {
- /* we expect that add_*_chunk() functions return CAM_REQ_CMP
- * if they added a chunk successfully, CAM_REQ_TOO_BIG if
- * the request is too big (too many bytes or too many chunks),
- * CAM_REQ_INVALID in case of other troubles
- */
- free_hcb_and_ccb_done(hcb, ccb, rv);
- return;
- }
- end_data:</programlisting>
-
- <para>If disconnection is disabled for this CCB we pass this
- information to the hcb:</para>
-
-<programlisting> if(ccb_h->flags &amp; CAM_DIS_DISCONNECT)
- hcb_disable_disconnect(hcb);</programlisting>
-
- <para>If the controller is able to run REQUEST SENSE command all
- by itself then the value of the flag CAM_DIS_AUTOSENSE should
- also be passed to it, to prevent automatic REQUEST SENSE if the
- CAM subsystem does not want it.</para>
-
- <para>The only thing left is to set up the timeout, pass our hcb
- to the hardware and return, the rest will be done by the
- interrupt handler (or timeout handler).</para>
-
-<programlisting> ccb_h->timeout_ch = timeout(xxx_timeout, (caddr_t) hcb,
- (ccb_h->timeout * hz) / 1000); /* convert milliseconds to ticks */
- put_hcb_into_hardware_queue(hcb);
- return;</programlisting>
-
- <para>And here is a possible implementation of the function
- returning CCB:</para>
-
-<programlisting> static void
- free_hcb_and_ccb_done(struct xxx_hcb *hcb, union ccb *ccb, u_int32_t status)
- {
- struct xxx_softc *softc = hcb->softc;
-
- ccb->ccb_h.ccb_hcb = 0;
- if(hcb != NULL) {
- untimeout(xxx_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch);
- /* we're about to free a hcb, so the shortage has ended */
- if(softc->flags &amp; RESOURCE_SHORTAGE) {
- softc->flags &amp;= ~RESOURCE_SHORTAGE;
- status |= CAM_RELEASE_SIMQ;
- }
- free_hcb(hcb); /* also removes hcb from any internal lists */
- }
- ccb->ccb_h.status = status |
- (ccb->ccb_h.status &amp; ~(CAM_STATUS_MASK|CAM_SIM_QUEUED));
- xpt_done(ccb);
- }</programlisting>
- </listitem>
-
- <listitem><para><emphasis>XPT_RESET_DEV</emphasis> - send the SCSI "BUS
- DEVICE RESET" message to a device</para>
-
- <para>There is no data transferred in CCB except the header and
- the most interesting argument of it is target_id. Depending on
- the controller hardware a hardware control block just like for
- the XPT_SCSI_IO request may be constructed (see XPT_SCSI_IO
- request description) and sent to the controller or the SCSI
- controller may be immediately programmed to send this RESET
- message to the device or this request may be just not supported
- (and return the status CAM_REQ_INVALID). Also on completion of
- the request all the disconnected transactions for this target
- must be aborted (probably in the interrupt routine).</para>
-
- <para>Also all the current negotiations for the target are lost on
- reset, so they might be cleaned too. Or they clearing may be
- deferred, because anyway the target would request re-negotiation
- on the next transaction.</para></listitem>
-
- <listitem><para><emphasis>XPT_RESET_BUS</emphasis> - send the RESET signal
- to the SCSI bus</para>
-
- <para>No arguments are passed in the CCB, the only interesting
- argument is the SCSI bus indicated by the struct sim
- pointer.</para>
-
- <para>A minimalistic implementation would forget the SCSI
- negotiations for all the devices on the bus and return the
- status CAM_REQ_CMP.</para>
-
- <para>The proper implementation would in addition actually reset
- the SCSI bus (possible also reset the SCSI controller) and mark
- all the CCBs being processed, both those in the hardware queue
- and those being disconnected, as done with the status
- CAM_SCSI_BUS_RESET. Like:</para>
-
-<programlisting> int targ, lun;
- struct xxx_hcb *h, *hh;
- struct ccb_trans_settings neg;
- struct cam_path *path;
-
- /* The SCSI bus reset may take a long time, in this case its completion
- * should be checked by interrupt or timeout. But for simplicity
- * we assume here that it's really fast.
- */
- reset_scsi_bus(softc);
-
- /* drop all enqueued CCBs */
- for(h = softc->first_queued_hcb; h != NULL; h = hh) {
- hh = h->next;
- free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
- }
-
- /* the clean values of negotiations to report */
- neg.bus_width = 8;
- neg.sync_period = neg.sync_offset = 0;
- neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
- | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);
-
- /* drop all disconnected CCBs and clean negotiations */
- for(targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {
- clean_negotiations(softc, targ);
-
- /* report the event if possible */
- if(xpt_create_path(&amp;path, /*periph*/NULL,
- cam_sim_path(sim), targ,
- CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
- xpt_async(AC_TRANSFER_NEG, path, &amp;neg);
- xpt_free_path(path);
- }
-
- for(lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)
- for(h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {
- hh=h->next;
- free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
- }
- }
-
- ccb->ccb_h.status = CAM_REQ_CMP;
- xpt_done(ccb);
-
- /* report the event */
- xpt_async(AC_BUS_RESET, softc->wpath, NULL);
- return;</programlisting>
-
- <para>Implementing the SCSI bus reset as a function may be a good
- idea because it would be re-used by the timeout function as a
- last resort if the things go wrong.</para></listitem>
-
- <listitem><para><emphasis>XPT_ABORT</emphasis> - abort the specified
- CCB</para>
-
- <para>The arguments are transferred in the instance "struct
- ccb_abort cab" of the union ccb. The only argument field in it
- is:</para>
-
- <para><emphasis>abort_ccb</emphasis> - pointer to the CCB to be
- aborted</para>
-
- <para>If the abort is not supported just return the status
- CAM_UA_ABORT. This is also the easy way to minimally implement
- this call, return CAM_UA_ABORT in any case.</para>
-
- <para>The hard way is to implement this request honestly. First
- check that abort applies to a SCSI transaction:</para>
-
-<programlisting> struct ccb *abort_ccb;
- abort_ccb = ccb->cab.abort_ccb;
-
- if(abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {
- ccb->ccb_h.status = CAM_UA_ABORT;
- xpt_done(ccb);
- return;
- }</programlisting>
-
- <para>Then it's necessary to find this CCB in our queue. This can
- be done by walking the list of all our hardware control blocks
- in search for one associated with this CCB:</para>
-
-<programlisting> struct xxx_hcb *hcb, *h;
-
- hcb = NULL;
-
- /* We assume that softc->first_hcb is the head of the list of all
- * HCBs associated with this bus, including those enqueued for
- * processing, being processed by hardware and disconnected ones.
- */
- for(h = softc->first_hcb; h != NULL; h = h->next) {
- if(h->ccb == abort_ccb) {
- hcb = h;
- break;
- }
- }
-
- if(hcb == NULL) {
- /* no such CCB in our queue */
- ccb->ccb_h.status = CAM_PATH_INVALID;
- xpt_done(ccb);
- return;
- }
-
- hcb=found_hcb;</programlisting>
-
- <para>Now we look at the current processing status of the HCB. It
- may be either sitting in the queue waiting to be sent to the
- SCSI bus, being transferred right now, or disconnected and
- waiting for the result of the command, or actually completed by
- hardware but not yet marked as done by software. To make sure
- that we don't get in any races with hardware we mark the HCB as
- being aborted, so that if this HCB is about to be sent to the
- SCSI bus the SCSI controller will see this flag and skip
- it.</para>
-
-<programlisting> int hstatus;
-
- /* shown as a function, in case special action is needed to make
- * this flag visible to hardware
- */
- set_hcb_flags(hcb, HCB_BEING_ABORTED);
-
- abort_again:
-
- hstatus = get_hcb_status(hcb);
- switch(hstatus) {
- case HCB_SITTING_IN_QUEUE:
- remove_hcb_from_hardware_queue(hcb);
- /* FALLTHROUGH */
- case HCB_COMPLETED:
- /* this is an easy case */
- free_hcb_and_ccb_done(hcb, abort_ccb, CAM_REQ_ABORTED);
- break;</programlisting>
-
- <para>If the CCB is being transferred right now we would like to
- signal to the SCSI controller in some hardware-dependent way
- that we want to abort the current transfer. The SCSI controller
- would set the SCSI ATTENTION signal and when the target responds
- to it send an ABORT message. We also reset the timeout to make
- sure that the target is not sleeping forever. If the command
- would not get aborted in some reasonable time like 10 seconds
- the timeout routine would go ahead and reset the whole SCSI bus.
- Because the command will be aborted in some reasonable time we
- can just return the abort request now as successfully completed,
- and mark the aborted CCB as aborted (but not mark it as done
- yet).</para>
-
-<programlisting> case HCB_BEING_TRANSFERRED:
- untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);
- abort_ccb->ccb_h.timeout_ch =
- timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);
- abort_ccb->ccb_h.status = CAM_REQ_ABORTED;
- /* ask the controller to abort that HCB, then generate
- * an interrupt and stop
- */
- if(signal_hardware_to_abort_hcb_and_stop(hcb) < 0) {
- /* oops, we missed the race with hardware, this transaction
- * got off the bus before we aborted it, try again */
- goto abort_again;
- }
-
- break;</programlisting>
-
- <para>If the CCB is in the list of disconnected then set it up as
- an abort request and re-queue it at the front of hardware
- queue. Reset the timeout and report the abort request to be
- completed.</para>
-
-<programlisting> case HCB_DISCONNECTED:
- untimeout(xxx_timeout, (caddr_t) hcb, abort_ccb->ccb_h.timeout_ch);
- abort_ccb->ccb_h.timeout_ch =
- timeout(xxx_timeout, (caddr_t) hcb, 10 * hz);
- put_abort_message_into_hcb(hcb);
- put_hcb_at_the_front_of_hardware_queue(hcb);
- break;
- }
- ccb->ccb_h.status = CAM_REQ_CMP;
- xpt_done(ccb);
- return;</programlisting>
-
- <para>That's all for the ABORT request, although there is one more
- issue. Because the ABORT message cleans all the ongoing
- transactions on a LUN we have to mark all the other active
- transactions on this LUN as aborted. That should be done in the
- interrupt routine, after the transaction gets aborted.</para>
-
- <para>Implementing the CCB abort as a function may be quite a good
- idea, this function can be re-used if an I/O transaction times
- out. The only difference would be that the timed out transaction
- would return the status CAM_CMD_TIMEOUT for the timed out
- request. Then the case XPT_ABORT would be small, like
- that:</para>
-
-<programlisting> case XPT_ABORT:
- struct ccb *abort_ccb;
- abort_ccb = ccb->cab.abort_ccb;
-
- if(abort_ccb->ccb_h.func_code != XPT_SCSI_IO) {
- ccb->ccb_h.status = CAM_UA_ABORT;
- xpt_done(ccb);
- return;
- }
- if(xxx_abort_ccb(abort_ccb, CAM_REQ_ABORTED) < 0)
- /* no such CCB in our queue */
- ccb->ccb_h.status = CAM_PATH_INVALID;
- else
- ccb->ccb_h.status = CAM_REQ_CMP;
- xpt_done(ccb);
- return;</programlisting>
- </listitem>
-
- <listitem><para><emphasis>XPT_SET_TRAN_SETTINGS</emphasis> - explicitly
- set values of SCSI transfer settings</para>
-
- <para>The arguments are transferred in the instance "struct ccb_trans_setting cts"
-of the union ccb:</para>
-
- <itemizedlist>
- <listitem><para><emphasis>valid</emphasis> - a bitmask showing
- which settings should be updated:</para></listitem>
-
- <listitem><para><emphasis>CCB_TRANS_SYNC_RATE_VALID</emphasis>
- - synchronous transfer rate</para></listitem>
-
- <listitem><para><emphasis>CCB_TRANS_SYNC_OFFSET_VALID</emphasis>
- - synchronous offset</para></listitem>
-
- <listitem><para><emphasis>CCB_TRANS_BUS_WIDTH_VALID</emphasis>
- - bus width</para></listitem>
-
- <listitem><para><emphasis>CCB_TRANS_DISC_VALID</emphasis> -
- set enable/disable disconnection</para></listitem>
-
- <listitem><para><emphasis>CCB_TRANS_TQ_VALID</emphasis> - set
- enable/disable tagged queuing</para></listitem>
-
- <listitem><para><emphasis>flags</emphasis> - consists of two
- parts, binary arguments and identification of
- sub-operations. The binary arguments are :</para>
- <itemizedlist>
- <listitem><para><emphasis>CCB_TRANS_DISC_ENB</emphasis> - enable disconnection</para></listitem>
- <listitem><para><emphasis>CCB_TRANS_TAG_ENB</emphasis> -
- enable tagged queuing</para></listitem>
- </itemizedlist>
- </listitem>
-
- <listitem><para>the sub-operations are:</para>
- <itemizedlist>
- <listitem><para><emphasis>CCB_TRANS_CURRENT_SETTINGS</emphasis>
- - change the current negotiations</para></listitem>
-
- <listitem><para><emphasis>CCB_TRANS_USER_SETTINGS</emphasis>
- - remember the desired user values sync_period, sync_offset -
- self-explanatory, if sync_offset==0 then the asynchronous mode
- is requested bus_width - bus width, in bits (not
- bytes)</para></listitem>
- </itemizedlist>
- </listitem>
-
- </itemizedlist>
-
- <para>Two sets of negotiated parameters are supported, the user
- settings and the current settings. The user settings are not
- really used much in the SIM drivers, this is mostly just a piece
- of memory where the upper levels can store (and later recall)
- its ideas about the parameters. Setting the user parameters
- does not cause re-negotiation of the transfer rates. But when
- the SCSI controller does a negotiation it must never set the
- values higher than the user parameters, so it's essentially the
- top boundary.</para>
-
- <para>The current settings are, as the name says,
- current. Changing them means that the parameters must be
- re-negotiated on the next transfer. Again, these "new current
- settings" are not supposed to be forced on the device, just they
- are used as the initial step of negotiations. Also they must be
- limited by actual capabilities of the SCSI controller: for
- example, if the SCSI controller has 8-bit bus and the request
- asks to set 16-bit wide transfers this parameter must be
- silently truncated to 8-bit transfers before sending it to the
- device.</para>
-
- <para>One caveat is that the bus width and synchronous parameters
- are per target while the disconnection and tag enabling
- parameters are per lun.</para>
-
- <para>The recommended implementation is to keep 3 sets of
- negotiated (bus width and synchronous transfer)
- parameters:</para>
-
- <itemizedlist>
- <listitem><para><emphasis>user</emphasis> - the user set, as
- above</para></listitem>
-
- <listitem><para><emphasis>current</emphasis> - those actually
- in effect</para></listitem>
-
- <listitem><para><emphasis>goal</emphasis> - those requested by
- setting of the "current" parameters</para></listitem>
- </itemizedlist>
-
- <para>The code looks like:</para>
-
-<programlisting> struct ccb_trans_settings *cts;
- int targ, lun;
- int flags;
-
- cts = &amp;ccb->cts;
- targ = ccb_h->target_id;
- lun = ccb_h->target_lun;
- flags = cts->flags;
- if(flags &amp; CCB_TRANS_USER_SETTINGS) {
- if(flags &amp; CCB_TRANS_SYNC_RATE_VALID)
- softc->user_sync_period[targ] = cts->sync_period;
- if(flags &amp; CCB_TRANS_SYNC_OFFSET_VALID)
- softc->user_sync_offset[targ] = cts->sync_offset;
- if(flags &amp; CCB_TRANS_BUS_WIDTH_VALID)
- softc->user_bus_width[targ] = cts->bus_width;
-
- if(flags &amp; CCB_TRANS_DISC_VALID) {
- softc->user_tflags[targ][lun] &amp;= ~CCB_TRANS_DISC_ENB;
- softc->user_tflags[targ][lun] |= flags &amp; CCB_TRANS_DISC_ENB;
- }
- if(flags &amp; CCB_TRANS_TQ_VALID) {
- softc->user_tflags[targ][lun] &amp;= ~CCB_TRANS_TQ_ENB;
- softc->user_tflags[targ][lun] |= flags &amp; CCB_TRANS_TQ_ENB;
- }
- }
- if(flags &amp; CCB_TRANS_CURRENT_SETTINGS) {
- if(flags &amp; CCB_TRANS_SYNC_RATE_VALID)
- softc->goal_sync_period[targ] =
- max(cts->sync_period, OUR_MIN_SUPPORTED_PERIOD);
- if(flags &amp; CCB_TRANS_SYNC_OFFSET_VALID)
- softc->goal_sync_offset[targ] =
- min(cts->sync_offset, OUR_MAX_SUPPORTED_OFFSET);
- if(flags &amp; CCB_TRANS_BUS_WIDTH_VALID)
- softc->goal_bus_width[targ] = min(cts->bus_width, OUR_BUS_WIDTH);
-
- if(flags &amp; CCB_TRANS_DISC_VALID) {
- softc->current_tflags[targ][lun] &amp;= ~CCB_TRANS_DISC_ENB;
- softc->current_tflags[targ][lun] |= flags &amp; CCB_TRANS_DISC_ENB;
- }
- if(flags &amp; CCB_TRANS_TQ_VALID) {
- softc->current_tflags[targ][lun] &amp;= ~CCB_TRANS_TQ_ENB;
- softc->current_tflags[targ][lun] |= flags &amp; CCB_TRANS_TQ_ENB;
- }
- }
- ccb->ccb_h.status = CAM_REQ_CMP;
- xpt_done(ccb);
- return;</programlisting>
-
- <para>Then when the next I/O request will be processed it will
- check if it has to re-negotiate, for example by calling the
- function target_negotiated(hcb). It can be implemented like
- this:</para>
-
-<programlisting> int
- target_negotiated(struct xxx_hcb *hcb)
- {
- struct softc *softc = hcb->softc;
- int targ = hcb->targ;
-
- if( softc->current_sync_period[targ] != softc->goal_sync_period[targ]
- || softc->current_sync_offset[targ] != softc->goal_sync_offset[targ]
- || softc->current_bus_width[targ] != softc->goal_bus_width[targ] )
- return 0; /* FALSE */
- else
- return 1; /* TRUE */
- }</programlisting>
-
- <para>After the values are re-negotiated the resulting values must
- be assigned to both current and goal parameters, so for future
- I/O transactions the current and goal parameters would be the
- same and <function>target_negotiated()</function> would return
- TRUE. When the card is initialized (in
- <function>xxx_attach()</function>) the current negotiation
- values must be initialized to narrow asynchronous mode, the goal
- and current values must be initialized to the maximal values
- supported by controller.</para></listitem>
-
- <listitem><para><emphasis>XPT_GET_TRAN_SETTINGS</emphasis> - get values of
- SCSI transfer settings</para>
-
- <para>This operations is the reverse of
- XPT_SET_TRAN_SETTINGS. Fill up the CCB instance "struct
- ccb_trans_setting cts" with data as requested by the flags
- CCB_TRANS_CURRENT_SETTINGS or CCB_TRANS_USER_SETTINGS (if both
- are set then the existing drivers return the current
- settings). Set all the bits in the valid field.</para></listitem>
-
- <listitem><para><emphasis>XPT_CALC_GEOMETRY</emphasis> - calculate logical
- (BIOS) geometry of the disk</para>
-
- <para>The arguments are transferred in the instance "struct
- ccb_calc_geometry ccg" of the union ccb:</para>
-
- <itemizedlist>
-
- <listitem><para><emphasis>block_size</emphasis> - input, block
- (A.K.A sector) size in bytes</para></listitem>
-
- <listitem><para><emphasis>volume_size</emphasis> - input,
- volume size in bytes</para></listitem>
-
- <listitem><para><emphasis>cylinders</emphasis> - output,
- logical cylinders</para></listitem>
-
- <listitem><para><emphasis>heads</emphasis> - output, logical
- heads</para></listitem>
-
- <listitem><para><emphasis>secs_per_track</emphasis> - output,
- logical sectors per track</para></listitem>
-
- </itemizedlist>
-
- <para>If the returned geometry differs much enough from what the
- SCSI controller BIOS thinks and a disk on this SCSI controller
- is used as bootable the system may not be able to boot. The
- typical calculation example taken from the aic7xxx driver
- is:</para>
-
-<programlisting> struct ccb_calc_geometry *ccg;
- u_int32_t size_mb;
- u_int32_t secs_per_cylinder;
- int extended;
-
- ccg = &amp;ccb->ccg;
- size_mb = ccg->volume_size
- / ((1024L * 1024L) / ccg->block_size);
- extended = check_cards_EEPROM_for_extended_geometry(softc);
-
- if (size_mb > 1024 &amp;&amp; extended) {
- ccg->heads = 255;
- ccg->secs_per_track = 63;
- } else {
- ccg->heads = 64;
- ccg->secs_per_track = 32;
- }
- secs_per_cylinder = ccg->heads * ccg->secs_per_track;
- ccg->cylinders = ccg->volume_size / secs_per_cylinder;
- ccb->ccb_h.status = CAM_REQ_CMP;
- xpt_done(ccb);
- return;</programlisting>
-
- <para>This gives the general idea, the exact calculation depends
- on the quirks of the particular BIOS. If BIOS provides no way
- set the "extended translation" flag in EEPROM this flag should
- normally be assumed equal to 1. Other popular geometries
- are:</para>
-
-<programlisting> 128 heads, 63 sectors - Symbios controllers
- 16 heads, 63 sectors - old controllers</programlisting>
-
- <para>Some system BIOSes and SCSI BIOSes fight with each other
- with variable success, for example a combination of Symbios
- 875/895 SCSI and Phoenix BIOS can give geometry 128/63 after
- power up and 255/63 after a hard reset or soft reboot.</para>
- </listitem>
-
- <listitem><para><emphasis>XPT_PATH_INQ</emphasis> - path inquiry, in other
- words get the SIM driver and SCSI controller (also known as HBA
- - Host Bus Adapter) properties</para>
-
- <para>The properties are returned in the instance "struct
-ccb_pathinq cpi" of the union ccb:</para>
-
- <itemizedlist>
-
- <listitem><para>version_num - the SIM driver version number, now
- all drivers use 1</para></listitem>
-
- <listitem><para>hba_inquiry - bitmask of features supported by
- the controller:</para></listitem>
-
- <listitem><para>PI_MDP_ABLE - supports MDP message (something
- from SCSI3?)</para></listitem>
-
- <listitem><para>PI_WIDE_32 - supports 32 bit wide
- SCSI</para></listitem>
-
- <listitem><para>PI_WIDE_16 - supports 16 bit wide
- SCSI</para></listitem>
-
- <listitem><para>PI_SDTR_ABLE - can negotiate synchronous
- transfer rate</para></listitem>
-
- <listitem><para>PI_LINKED_CDB - supports linked
- commands</para></listitem>
-
- <listitem><para>PI_TAG_ABLE - supports tagged
- commands</para></listitem>
-
- <listitem><para>PI_SOFT_RST - supports soft reset alternative
- (hard reset and soft reset are mutually exclusive within a
- SCSI bus)</para></listitem>
-
- <listitem><para>target_sprt - flags for target mode support, 0
- if unsupported</para></listitem>
-
- <listitem><para>hba_misc - miscellaneous controller
- features:</para></listitem>
-
- <listitem><para>PIM_SCANHILO - bus scans from high ID to low
- ID</para></listitem>
-
- <listitem><para>PIM_NOREMOVE - removable devices not included in
- scan</para></listitem>
-
- <listitem><para>PIM_NOINITIATOR - initiator role not
- supported</para></listitem>
-
- <listitem><para>PIM_NOBUSRESET - user has disabled initial BUS
- RESET</para></listitem>
-
- <listitem><para>hba_eng_cnt - mysterious HBA engine count,
- something related to compression, now is always set to
- 0</para></listitem>
-
- <listitem><para>vuhba_flags - vendor-unique flags, unused
- now</para></listitem>
-
- <listitem><para>max_target - maximal supported target ID (7 for
- 8-bit bus, 15 for 16-bit bus, 127 for Fibre
- Channel)</para></listitem>
-
- <listitem><para>max_lun - maximal supported LUN ID (7 for older
- SCSI controllers, 63 for newer ones)</para></listitem>
-
- <listitem><para>async_flags - bitmask of installed Async
- handler, unused now</para></listitem>
-
- <listitem><para>hpath_id - highest Path ID in the subsystem,
- unused now</para></listitem>
-
- <listitem><para>unit_number - the controller unit number,
- cam_sim_unit(sim)</para></listitem>
-
- <listitem><para>bus_id - the bus number,
- cam_sim_bus(sim)</para></listitem>
-
- <listitem><para>initiator_id - the SCSI ID of the controller
- itself</para></listitem>
-
- <listitem><para>base_transfer_speed - nominal transfer speed in
- KB/s for asynchronous narrow transfers, equals to 3300 for
- SCSI</para></listitem>
-
- <listitem><para>sim_vid - SIM driver's vendor id, a
- zero-terminated string of maximal length SIM_IDLEN including
- the terminating zero</para></listitem>
-
- <listitem><para>hba_vid - SCSI controller's vendor id, a
- zero-terminated string of maximal length HBA_IDLEN including
- the terminating zero</para></listitem>
-
- <listitem><para>dev_name - device driver name, a zero-terminated
- string of maximal length DEV_IDLEN including the terminating
- zero, equal to cam_sim_name(sim)</para></listitem>
-
- </itemizedlist>
-
- <para>The recommended way of setting the string fields is using
- strncpy, like:</para>
-
-<programlisting> strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);</programlisting>
-
- <para>After setting the values set the status to CAM_REQ_CMP and mark the
-CCB as done.</para>
- </listitem>
- </itemizedlist>
-
- </sect1>
-
- <sect1>
- <title>Polling</title>
-
- <funcSynopsis><funcPrototype>
- <funcDef>static void
- <function>xxx_poll</function>
- </funcDef>
- <paramdef>
- <parameter>struct cam_sim *sim</parameter>
- </paramdef>
- </funcPrototype></funcSynopsis>
-
- <para>The poll function is used to simulate the interrupts when
- the interrupt subsystem is not functioning (for example, when
- the system has crashed and is creating the system dump). The CAM
- subsystem sets the proper interrupt level before calling the
- poll routine. So all it needs to do is to call the interrupt
- routine (or the other way around, the poll routine may be doing
- the real action and the interrupt routine would just call the
- poll routine). Why bother about a separate function then ?
- Because of different calling conventions. The
- <function>xxx_poll</function> routine gets the struct cam_sim
- pointer as its argument when the PCI interrupt routine by common
- convention gets pointer to the struct
- <structName>xxx_softc</structName> and the ISA interrupt routine
- gets just the device unit number. So the poll routine would
- normally look as:</para>
-
-<programlisting>static void
-xxx_poll(struct cam_sim *sim)
-{
- xxx_intr((struct xxx_softc *)cam_sim_softc(sim)); /* for PCI device */
-}</programlisting>
-
- <para>or</para>
-
-<programlisting>static void
-xxx_poll(struct cam_sim *sim)
-{
- xxx_intr(cam_sim_unit(sim)); /* for ISA device */
-}</programlisting>
-
- </sect1>
-
- <sect1>
- <title>Asynchronous Events</title>
-
- <para>If an asynchronous event callback has been set up then the
- callback function should be defined.</para>
-
-<programlisting>static void
-ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)</programlisting>
-
- <itemizedlist>
- <listitem><para>callback_arg - the value supplied when registering the
- callback</para></listitem>
-
- <listitem><para>code - identifies the type of event</para></listitem>
-
- <listitem><para>path - identifies the devices to which the event
- applies</para></listitem>
-
- <listitem><para>arg - event-specific argument</para></listitem>
- </itemizedlist>
-
- <para>Implementation for a single type of event, AC_LOST_DEVICE,
- looks like:</para>
-
-<programlisting> struct xxx_softc *softc;
- struct cam_sim *sim;
- int targ;
- struct ccb_trans_settings neg;
-
- sim = (struct cam_sim *)callback_arg;
- softc = (struct xxx_softc *)cam_sim_softc(sim);
- switch (code) {
- case AC_LOST_DEVICE:
- targ = xpt_path_target_id(path);
- if(targ <= OUR_MAX_SUPPORTED_TARGET) {
- clean_negotiations(softc, targ);
- /* send indication to CAM */
- neg.bus_width = 8;
- neg.sync_period = neg.sync_offset = 0;
- neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
- | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);
- xpt_async(AC_TRANSFER_NEG, path, &amp;neg);
- }
- break;
- default:
- break;
- }</programlisting>
-
- </sect1>
-
- <sect1>
- <title>Interrupts</title>
-
- <para>The exact type of the interrupt routine depends on the type
- of the peripheral bus (PCI, ISA and so on) to which the SCSI
- controller is connected.</para>
-
- <para>The interrupt routines of the SIM drivers run at the
- interrupt level splcam. So <function>splcam()</function> should
- be used in the driver to synchronize activity between the
- interrupt routine and the rest of the driver (for a
- multiprocessor-aware driver things get yet more interesting but
- we ignore this case here). The pseudo-code in this document
- happily ignores the problems of synchronization. The real code
- must not ignore them. A simple-minded approach is to set
- <function>splcam()</function> on the entry to the other routines
- and reset it on return thus protecting them by one big critical
- section. To make sure that the interrupt level will be always
- restored a wrapper function can be defined, like:</para>
-
-<programlisting> static void
- xxx_action(struct cam_sim *sim, union ccb *ccb)
- {
- int s;
- s = splcam();
- xxx_action1(sim, ccb);
- splx(s);
- }
-
- static void
- xxx_action1(struct cam_sim *sim, union ccb *ccb)
- {
- ... process the request ...
- }</programlisting>
-
- <para>This approach is simple and robust but the problem with it
- is that interrupts may get blocked for a relatively long time
- and this would negatively affect the system's performance. On
- the other hand the functions of the <function>spl()</function>
- family have rather high overhead, so vast amount of tiny
- critical sections may not be good either.</para>
-
- <para>The conditions handled by the interrupt routine and the
- details depend very much on the hardware. We consider the set of
- "typical" conditions.</para>
-
- <para>First, we check if a SCSI reset was encountered on the bus
- (probably caused by another SCSI controller on the same SCSI
- bus). If so we drop all the enqueued and disconnected requests,
- report the events and re-initialize our SCSI controller. It is
- important that during this initialization the controller won't
- issue another reset or else two controllers on the same SCSI bus
- could ping-pong resets forever. The case of fatal controller
- error/hang could be handled in the same place, but it will
- probably need also sending RESET signal to the SCSI bus to reset
- the status of the connections with the SCSI devices.</para>
-
-<programlisting> int fatal=0;
- struct ccb_trans_settings neg;
- struct cam_path *path;
-
- if( detected_scsi_reset(softc)
- || (fatal = detected_fatal_controller_error(softc)) ) {
- int targ, lun;
- struct xxx_hcb *h, *hh;
-
- /* drop all enqueued CCBs */
- for(h = softc->first_queued_hcb; h != NULL; h = hh) {
- hh = h->next;
- free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
- }
-
- /* the clean values of negotiations to report */
- neg.bus_width = 8;
- neg.sync_period = neg.sync_offset = 0;
- neg.valid = (CCB_TRANS_BUS_WIDTH_VALID
- | CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID);
-
- /* drop all disconnected CCBs and clean negotiations */
- for(targ=0; targ <= OUR_MAX_SUPPORTED_TARGET; targ++) {
- clean_negotiations(softc, targ);
-
- /* report the event if possible */
- if(xpt_create_path(&amp;path, /*periph*/NULL,
- cam_sim_path(sim), targ,
- CAM_LUN_WILDCARD) == CAM_REQ_CMP) {
- xpt_async(AC_TRANSFER_NEG, path, &amp;neg);
- xpt_free_path(path);
- }
-
- for(lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)
- for(h = softc->first_discon_hcb[targ][lun]; h != NULL; h = hh) {
- hh=h->next;
- if(fatal)
- free_hcb_and_ccb_done(h, h->ccb, CAM_UNREC_HBA_ERROR);
- else
- free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
- }
- }
-
- /* report the event */
- xpt_async(AC_BUS_RESET, softc->wpath, NULL);
-
- /* re-initialization may take a lot of time, in such case
- * its completion should be signaled by another interrupt or
- * checked on timeout - but for simplicity we assume here that
- * it's really fast
- */
- if(!fatal) {
- reinitialize_controller_without_scsi_reset(softc);
- } else {
- reinitialize_controller_with_scsi_reset(softc);
- }
- schedule_next_hcb(softc);
- return;
- }</programlisting>
-
- <para>If interrupt is not caused by a controller-wide condition
- then probably something has happened to the current hardware
- control block. Depending on the hardware there may be other
- non-HCB-related events, we just do not consider them here. Then
- we analyze what happened to this HCB:</para>
-
-<programlisting> struct xxx_hcb *hcb, *h, *hh;
- int hcb_status, scsi_status;
- int ccb_status;
- int targ;
- int lun_to_freeze;
-
- hcb = get_current_hcb(softc);
- if(hcb == NULL) {
- /* either stray interrupt or something went very wrong
- * or this is something hardware-dependent
- */
- handle as necessary;
- return;
- }
-
- targ = hcb->target;
- hcb_status = get_status_of_current_hcb(softc);</programlisting>
-
- <para>First we check if the HCB has completed and if so we check
- the returned SCSI status.</para>
-
-<programlisting> if(hcb_status == COMPLETED) {
- scsi_status = get_completion_status(hcb);</programlisting>
-
- <para>Then look if this status is related to the REQUEST SENSE
- command and if so handle it in a simple way.</para>
-
-<programlisting> if(hcb->flags &amp; DOING_AUTOSENSE) {
- if(scsi_status == GOOD) { /* autosense was successful */
- hcb->ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
- free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);
- } else {
- autosense_failed:
- free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_AUTOSENSE_FAIL);
- }
- schedule_next_hcb(softc);
- return;
- }</programlisting>
-
- <para>Else the command itself has completed, pay more attention to
- details. If auto-sense is not disabled for this CCB and the
- command has failed with sense data then run REQUEST SENSE
- command to receive that data.</para>
-
-<programlisting> hcb->ccb->csio.scsi_status = scsi_status;
- calculate_residue(hcb);
-
- if( (hcb->ccb->ccb_h.flags &amp; CAM_DIS_AUTOSENSE)==0
- &amp;&amp; ( scsi_status == CHECK_CONDITION
- || scsi_status == COMMAND_TERMINATED) ) {
- /* start auto-SENSE */
- hcb->flags |= DOING_AUTOSENSE;
- setup_autosense_command_in_hcb(hcb);
- restart_current_hcb(softc);
- return;
- }
- if(scsi_status == GOOD)
- free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_REQ_CMP);
- else
- free_hcb_and_ccb_done(hcb, hcb->ccb, CAM_SCSI_STATUS_ERROR);
- schedule_next_hcb(softc);
- return;
- }</programlisting>
-
- <para>One typical thing would be negotiation events: negotiation
- messages received from a SCSI target (in answer to our
- negotiation attempt or by target's initiative) or the target is
- unable to negotiate (rejects our negotiation messages or does
- not answer them).</para>
-
-<programlisting> switch(hcb_status) {
- case TARGET_REJECTED_WIDE_NEG:
- /* revert to 8-bit bus */
- softc->current_bus_width[targ] = softc->goal_bus_width[targ] = 8;
- /* report the event */
- neg.bus_width = 8;
- neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
- xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &amp;neg);
- continue_current_hcb(softc);
- return;
- case TARGET_ANSWERED_WIDE_NEG:
- {
- int wd;
-
- wd = get_target_bus_width_request(softc);
- if(wd <= softc->goal_bus_width[targ]) {
- /* answer is acceptable */
- softc->current_bus_width[targ] =
- softc->goal_bus_width[targ] = neg.bus_width = wd;
-
- /* report the event */
- neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
- xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &amp;neg);
- } else {
- prepare_reject_message(hcb);
- }
- }
- continue_current_hcb(softc);
- return;
- case TARGET_REQUESTED_WIDE_NEG:
- {
- int wd;
-
- wd = get_target_bus_width_request(softc);
- wd = min (wd, OUR_BUS_WIDTH);
- wd = min (wd, softc->user_bus_width[targ]);
-
- if(wd != softc->current_bus_width[targ]) {
- /* the bus width has changed */
- softc->current_bus_width[targ] =
- softc->goal_bus_width[targ] = neg.bus_width = wd;
-
- /* report the event */
- neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
- xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &amp;neg);
- }
- prepare_width_nego_rsponse(hcb, wd);
- }
- continue_current_hcb(softc);
- return;
- }</programlisting>
-
- <para>Then we handle any errors that could have happened during
- auto-sense in the same simple-minded way as before. Otherwise we
- look closer at the details again.</para>
-
-<programlisting> if(hcb->flags &amp; DOING_AUTOSENSE)
- goto autosense_failed;
-
- switch(hcb_status) {</programlisting>
-
- <para>The next event we consider is unexpected disconnect. Which
- is considered normal after an ABORT or BUS DEVICE RESET message
- and abnormal in other cases.</para>
-
-<programlisting> case UNEXPECTED_DISCONNECT:
- if(requested_abort(hcb)) {
- /* abort affects all commands on that target+LUN, so
- * mark all disconnected HCBs on that target+LUN as aborted too
- */
- for(h = softc->first_discon_hcb[hcb->target][hcb->lun];
- h != NULL; h = hh) {
- hh=h->next;
- free_hcb_and_ccb_done(h, h->ccb, CAM_REQ_ABORTED);
- }
- ccb_status = CAM_REQ_ABORTED;
- } else if(requested_bus_device_reset(hcb)) {
- int lun;
-
- /* reset affects all commands on that target, so
- * mark all disconnected HCBs on that target+LUN as reset
- */
-
- for(lun=0; lun <= OUR_MAX_SUPPORTED_LUN; lun++)
- for(h = softc->first_discon_hcb[hcb->target][lun];
- h != NULL; h = hh) {
- hh=h->next;
- free_hcb_and_ccb_done(h, h->ccb, CAM_SCSI_BUS_RESET);
- }
-
- /* send event */
- xpt_async(AC_SENT_BDR, hcb->ccb->ccb_h.path_id, NULL);
-
- /* this was the CAM_RESET_DEV request itself, it's completed */
- ccb_status = CAM_REQ_CMP;
- } else {
- calculate_residue(hcb);
- ccb_status = CAM_UNEXP_BUSFREE;
- /* request the further code to freeze the queue */
- hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
- lun_to_freeze = hcb->lun;
- }
- break;</programlisting>
-
- <para>If the target refuses to accept tags we notify CAM about
- that and return back all commands for this LUN:</para>
-
-<programlisting> case TAGS_REJECTED:
- /* report the event */
- neg.flags = 0 &amp; ~CCB_TRANS_TAG_ENB;
- neg.valid = CCB_TRANS_TQ_VALID;
- xpt_async(AC_TRANSFER_NEG, hcb->ccb.ccb_h.path_id, &amp;neg);
-
- ccb_status = CAM_MSG_REJECT_REC;
- /* request the further code to freeze the queue */
- hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
- lun_to_freeze = hcb->lun;
- break;</programlisting>
-
- <para>Then we check a number of other conditions, with processing
- basically limited to setting the CCB status:</para>
-
-<programlisting> case SELECTION_TIMEOUT:
- ccb_status = CAM_SEL_TIMEOUT;
- /* request the further code to freeze the queue */
- hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
- lun_to_freeze = CAM_LUN_WILDCARD;
- break;
- case PARITY_ERROR:
- ccb_status = CAM_UNCOR_PARITY;
- break;
- case DATA_OVERRUN:
- case ODD_WIDE_TRANSFER:
- ccb_status = CAM_DATA_RUN_ERR;
- break;
- default:
- /* all other errors are handled in a generic way */
- ccb_status = CAM_REQ_CMP_ERR;
- /* request the further code to freeze the queue */
- hcb->ccb->ccb_h.status |= CAM_DEV_QFRZN;
- lun_to_freeze = CAM_LUN_WILDCARD;
- break;
- }</programlisting>
-
- <para>Then we check if the error was serious enough to freeze the
- input queue until it gets proceeded and do so if it is:</para>
-
-<programlisting> if(hcb->ccb->ccb_h.status &amp; CAM_DEV_QFRZN) {
- /* freeze the queue */
- xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
-
- /* re-queue all commands for this target/LUN back to CAM */
-
- for(h = softc->first_queued_hcb; h != NULL; h = hh) {
- hh = h->next;
-
- if(targ == h->targ
- &amp;&amp; (lun_to_freeze == CAM_LUN_WILDCARD || lun_to_freeze == h->lun) )
- free_hcb_and_ccb_done(h, h->ccb, CAM_REQUEUE_REQ);
- }
- }
- free_hcb_and_ccb_done(hcb, hcb->ccb, ccb_status);
- schedule_next_hcb(softc);
- return;</programlisting>
-
- <para>This concludes the generic interrupt handling although
- specific controllers may require some additions.</para>
-
- </sect1>
-
- <sect1>
- <title>Errors Summary</title>
-
- <para>When executing an I/O request many things may go wrong. The
- reason of error can be reported in the CCB status with great
- detail. Examples of use are spread throughout this document. For
- completeness here is the summary of recommended responses for
- the typical error conditions:</para>
-
- <itemizedlist>
-
- <listitem><para><emphasis>CAM_RESRC_UNAVAIL</emphasis> - some
- resource is temporarily unavailable and the SIM driver cannot
- generate an event when it will become available. An example of
- this resource would be some intra-controller hardware resource
- for which the controller does not generate an interrupt when
- it becomes available.</para></listitem>
-
- <listitem><para><emphasis>CAM_UNCOR_PARITY</emphasis> -
- unrecovered parity error occurred</para></listitem>
-
- <listitem><para><emphasis>CAM_DATA_RUN_ERR</emphasis> - data
- overrun or unexpected data phase (going in other direction
- than specified in CAM_DIR_MASK) or odd transfer length for
- wide transfer</para></listitem>
-
- <listitem><para><emphasis>CAM_SEL_TIMEOUT</emphasis> - selection
- timeout occurred (target does not respond)</para></listitem>
-
- <listitem><para><emphasis>CAM_CMD_TIMEOUT</emphasis> - command
- timeout occurred (the timeout function ran)</para></listitem>
-
- <listitem><para><emphasis>CAM_SCSI_STATUS_ERROR</emphasis> - the
- device returned error</para></listitem>
-
- <listitem><para><emphasis>CAM_AUTOSENSE_FAIL</emphasis> - the
- device returned error and the REQUEST SENSE COMMAND
- failed</para></listitem>
-
- <listitem><para><emphasis>CAM_MSG_REJECT_REC</emphasis> - MESSAGE
- REJECT message was received</para></listitem>
-
- <listitem><para><emphasis>CAM_SCSI_BUS_RESET</emphasis> - received
- SCSI bus reset</para></listitem>
-
- <listitem><para><emphasis>CAM_REQ_CMP_ERR</emphasis> -
- "impossible" SCSI phase occurred or something else as weird or
- just a generic error if further detail is not
- available</para></listitem>
-
- <listitem><para><emphasis>CAM_UNEXP_BUSFREE</emphasis> -
- unexpected disconnect occurred</para></listitem>
-
- <listitem><para><emphasis>CAM_BDR_SENT</emphasis> - BUS DEVICE
- RESET message was sent to the target</para></listitem>
-
- <listitem><para><emphasis>CAM_UNREC_HBA_ERROR</emphasis> -
- unrecoverable Host Bus Adapter Error</para></listitem>
-
- <listitem><para><emphasis>CAM_REQ_TOO_BIG</emphasis> - the request
- was too large for this controller</para></listitem>
-
- <listitem><para><emphasis>CAM_REQUEUE_REQ</emphasis> - this
- request should be re-queued to preserve transaction ordering.
- This typically occurs when the SIM recognizes an error that
- should freeze the queue and must place other queued requests
- for the target at the sim level back into the XPT
- queue. Typical cases of such errors are selection timeouts,
- command timeouts and other like conditions. In such cases the
- troublesome command returns the status indicating the error,
- the and the other commands which have not be sent to the bus
- yet get re-queued.</para></listitem>
-
- <listitem><para><emphasis>CAM_LUN_INVALID</emphasis> - the LUN
- ID in the request is not supported by the SCSI
- controller</para></listitem>
-
- <listitem><para><emphasis>CAM_TID_INVALID</emphasis> - the
- target ID in the request is not supported by the SCSI
- controller</para></listitem>
- </itemizedlist>
- </sect1>
-
- <sect1>
- <title>Timeout Handling</title>
-
- <para>When the timeout for an HCB expires that request should be
- aborted, just like with an XPT_ABORT request. The only
- difference is that the returned status of aborted request should
- be CAM_CMD_TIMEOUT instead of CAM_REQ_ABORTED (that's why
- implementation of the abort better be done as a function). But
- there is one more possible problem: what if the abort request
- itself will get stuck? In this case the SCSI bus should be
- reset, just like with an XPT_RESET_BUS request (and the idea
- about implementing it as a function called from both places
- applies here too). Also we should reset the whole SCSI bus if a
- device reset request got stuck. So after all the timeout
- function would look like:</para>
-
-<programlisting>static void
-xxx_timeout(void *arg)
-{
- struct xxx_hcb *hcb = (struct xxx_hcb *)arg;
- struct xxx_softc *softc;
- struct ccb_hdr *ccb_h;
-
- softc = hcb->softc;
- ccb_h = &amp;hcb->ccb->ccb_h;
-
- if(hcb->flags &amp; HCB_BEING_ABORTED
- || ccb_h->func_code == XPT_RESET_DEV) {
- xxx_reset_bus(softc);
- } else {
- xxx_abort_ccb(hcb->ccb, CAM_CMD_TIMEOUT);
- }
-}</programlisting>
-
- <para>When we abort a request all the other disconnected requests
- to the same target/LUN get aborted too. So there appears a
- question, should we return them with status CAM_REQ_ABORTED or
- CAM_CMD_TIMEOUT ? The current drivers use CAM_CMD_TIMEOUT. This
- seems logical because if one request got timed out then probably
- something really bad is happening to the device, so if they
- would not be disturbed they would time out by themselves.</para>
-
- </sect1>
-
-</chapter>
diff --git a/en_US.ISO8859-1/books/arch-handbook/sysinit/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/sysinit/chapter.sgml
deleted file mode 100644
index 468e739d12..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/sysinit/chapter.sgml
+++ /dev/null
@@ -1,161 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: $
--->
-
-<chapter id="sysinit">
- <title>The Sysinit Framework</title>
-
- <para>Sysinit is the framework for a generic call sort and dispatch
- mechanisim. FreeBSD currently uses it for the dynamic
- initialization of the kernel. Sysinit allows FreeBSD's kernel
- subsystems to be reordered, and added, removed, and replaced at
- kernel link time when the kernel or one of its modules is loaded
- without having to edit a staticly ordered initilization routing
- and recompile the kernel. This system also allows kernel modules,
- currently called <firstterm>KLD's</firstterm>, to be seperatly
- compiled, linked, and initilized at boot time and loaded even
- later while the system is already running. This is accomplished
- using the <quote>kernel linker</quote> and <quote>linker
- sets</quote>.</para>
-
- <sect1>
- <title>Terminology</title>
-
- <variablelist>
- <varlistentry>
- <term>Linker Set</term>
- <listitem>
- <para>A linker technique in which the linker gathers
- staticly declared data throughout a program's source files
- into a single contagiously addressable unit of
- data.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </sect1>
-
- <sect1>
- <title>Sysinit Operation</title>
-
- <para>Sysinit relies on the ability of the linker to take static
- data declared at multiple locations throughout a program's
- source and group it together as a single contagious chunk of
- data. This linker technique is called a <quote>linker
- set</quote>. Sysinit uses two linker sets to maintain two data
- sets containing each consumer's call order, function, and a
- pointer to the data to pass to taht function.</para>
-
- <para>Sysinit uses two priorites when ordering the functions for
- execution. The first priority is a subsystem ID giving an
- overall order Sysinit's dispatch of funtions. Current predeclard
- ID's are in <filename>&lt;sys/kernel.h></filename> in the enum
- list <literal>sysinit_sub_id</literal>. The second priority used
- is an element order within the subsystem. Current predeclard
- subsystem element orders are in
- <filename>&lt;sys/kernel.h></filename> in the enum list
- <literal>sysinit_elem_order</literal>.</para>
-
- <para>There are currently two uses for Sysinit. Function dispatch
- at system startup and kernel module loads, and function dispatch
- at system shutdown and kernel module unload.</para>
- </sect1>
-
-
- <sect1>
- <title>Using Sysinit</title>
-
- <sect2>
- <title>Interface</title>
-
- <sect3>
- <title>Headers</title>
-
- <programlisting>&lt;sys/kernel.h></programlisting>
- </sect3>
-
- <sect3>
- <title>Macros</title>
-
- <programlisting>SYSINIT(uniquifier, subsystem, order, func, ident)
- SYSUNINIT(uniquifier, subsystem, order, func, ident)</programlisting>
- </sect3>
- </sect2>
-
- <sect2>
- <title>Startup</title>
-
- <para>The <literal>SYSINIT()</literal> macro creates the
- necessary sysinit data in Sysinit's startup data set for
- Sysinit to sort and dispatch a function at system startup and
- module load. <literal>SYSINIT()</literal> takes a uniquifier
- that Sysinit uses identify the particular function dispatch
- data, the subsystem order, the subsystem element order, the
- function to call, and the data to pass the fuction. All
- functions must take a constant pointer argument.
- </para>
-
- <para>For example:</para>
-
- <programlisting>#include &lt;sys/kernel.h>
-
-void foo_null(void *unused)
-{
- foo_doo();
-}
-SYSINIT(foo_null, SI_SUB_FOO, SI_ORDER_FOO, NULL);
-
-struct foo foo_voodoo = {
- FOO_VOODOO;
-}
-
-void foo_arg(void *vdata)
-{
- struct foo *foo = (struct foo *)vdata;
- foo_data(foo);
-}
-SYSINIT(foo_arg, SI_SUB_FOO, SI_ORDER_FOO, foo_voodoo);
- </programlisting>
- </sect2>
-
- <sect2>
- <title>Shutdown</title>
-
- <para>The <literal>SYSUNINIT()</literal> macro behaves similarly
- to the <literal>SYSINIT()</literal> macro except that it adds
- the Sysinit data to Sysinit's shutdown data set.</para>
-
- <para>For example:</para>
-
- <programlisting>#include &lt;sys/kernel.h>
-
-void foo_cleanup(void *unused)
-{
- foo_kill();
-}
-SYSUNINIT(foo_cleanup, SI_SUB_FOO, SI_ORDER_FOO, NULL);
-
-struct foo_stack foo_stack = {
- FOO_STACK_VOODOO;
-}
-
-void foo_flush(void *vdata)
-{
-}
-SYSUNINIT(foo_flush, SI_SUB_FOO, SI_ORDER_FOO, foo_stack);
- </programlisting>
- </sect2>
- </sect1>
-</chapter>
-
-<!--
- Local Variables:
- mode: sgml
- sgml-declaration: "../chapter.decl"
- sgml-indent-data: t
- sgml-omittag: nil
- sgml-always-quote-attributes: t
- sgml-parent-document: ("../book.sgml" "part" "chapter")
- End:
--->
diff --git a/en_US.ISO8859-1/books/arch-handbook/usb/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/usb/chapter.sgml
deleted file mode 100644
index 9656256e31..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/usb/chapter.sgml
+++ /dev/null
@@ -1,623 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD$
--->
-
-<chapter id="usb">
- <title>USB Devices</title>
-
- <para><emphasis>This chapter was written by &a.nhibma;. Modifications made for
- the handbook by &a.murray;.</emphasis></para>
-
- <sect1>
- <title>Introduction</title>
-
- <para>The Universal Serial Bus (USB) is a new way of attaching
- devices to personal computers. The bus architecture features
- two-way communication and has been developed as a response to
- devices becoming smarter and requiring more interaction with the
- host. USB support is included in all current PC chipsets and is
- therefore available in all recently built PCs. Apple's
- introduction of the USB-only iMac has been a major incentive for
- hardware manufacturers to produce USB versions of their devices.
- The future PC specifications specify that all legacy connectors
- on PCs should be replaced by one or more USB connectors,
- providing generic plug and play capabilities. Support for USB
- hardware was available at a very early stage in NetBSD and was
- developed by Lennart Augustsson for the NetBSD project. The
- code has been ported to FreeBSD and we are currently maintaining
- a shared code base. For the implementation of the USB subsystem
- a number of features of USB are important.</para>
-
- <para><emphasis>Lennart Augustsson has done most of the implementation of
- the USB support for the NetBSD project. Many thanks for this
- incredible amount of work. Many thanks also to Ardy and Dirk for
- their comments and proofreading of this paper.</emphasis></para>
-
- <itemizedlist>
-
- <listitem><para>Devices connect to ports on the computer
- directly or on devices called hubs, forming a treelike device
- structure.</para></listitem>
-
- <listitem><para>The devices can be connected and disconnected at
- run time.</para></listitem>
-
- <listitem><para>Devices can suspend themselves and trigger
- resumes of the host system</para></listitem>
-
- <listitem><para>As the devices can be powered from the bus, the
- host software has to keep track of power budgets for each
- hub.</para></listitem>
-
- <listitem><para>Different quality of service requirements by the
- different device types together with the maximum of 126
- devices that can be connected to the same bus, require proper
- scheduling of transfers on the shared bus to take full
- advantage of the 12Mbps bandwidth available. (over 400Mbps
- with USB 2.0)</para></listitem>
-
- <listitem><para>Devices are intelligent and contain easily
- accessible information about themselves</para></listitem>
-
- </itemizedlist>
-
- <para>The development of drivers for the USB subsystem and devices
- connected to it is supported by the specifications that have
- been developed and will be developed. These specifications are
- publicly available from the USB home pages. Apple has been very
- strong in pushing for standards based drivers, by making drivers
- for the generic classes available in their operating system
- MacOS and discouraging the use of separate drivers for each new
- device. This chapter tries to collate essential information for a
- basic understanding of the present implementation of the USB
- stack in FreeBSD/NetBSD. It is recommended however to read it
- together with the relevant specifications mentioned in the
- references below.</para>
-
- <sect2>
- <title>Structure of the USB Stack</title>
-
- <para>The USB support in FreeBSD can be split into three
- layers. The lowest layer contains the host controller driver,
- providing a generic interface to the hardware and its scheduling
- facilities. It supports initialisation of the hardware,
- scheduling of transfers and handling of completed and/or failed
- transfers. Each host controller driver implements a virtual hub
- providing hardware independent access to the registers
- controlling the root ports on the back of the machine.</para>
-
- <para>The middle layer handles the device connection and
- disconnection, basic initialisation of the device, driver
- selection, the communication channels (pipes) and does
- resource management. This services layer also controls the
- default pipes and the device requests transferred over
- them.</para>
-
- <para>The top layer contains the individual drivers supporting
- specific (classes of) devices. These drivers implement the
- protocol that is used over the pipes other than the default
- pipe. They also implement additional functionality to make the
- device available to other parts of the kernel oruserland. They
- use the USB driver interface (USBDI) exposed by the services
- layer.</para>
- </sect2>
- </sect1>
-
- <sect1 id="usb-hc">
- <title>Host Controllers</title>
-
- <para>The host controller (HC) controls the transmission of
- packets on the bus. Frames of 1 millisecond are used. At the
- start of each frame the host controller generates a Start of
- Frame (SOF) packet.</para>
-
- <para>The SOF packet is used to synchronise to the start of the
- frame and to keep track of the frame number. Within each frame
- packets are transferred, either from host to device (out) or
- from device to host (in). Transfers are always initiated by the
- host (polled transfers). Therefore there can only be one host
- per USB bus. Each transfer of a packet has a status stage in
- which the recipient of the data can return either ACK
- (acknowledge reception), NAK (retry), STALL (error condition) or
- nothing (garbled data stage, device not available or
- disconnected). Section 8.5 of the <ulink
- url="http://www.usb.org/developers/docs.html">USB
- specification</ulink> explains the details of packets in more
- detail. Four different types of transfers can occur on a USB
- bus: control, bulk, interrupt and isochronous. The types of
- transfers and their characteristics are described below (`Pipes'
- subsection).</para>
-
- <para>Large transfers between the device on the USB bus and the
- device driver are split up into multiple packets by the host
- controller or the HC driver.</para>
-
- <para>Device requests (control transfers) to the default endpoints
- are special. They consist of two or three phases: SETUP, DATA
- (optional) and STATUS. The set-up packet is sent to the
- device. If there is a data phase, the direction of the data
- packet(s) is given in the set-up packet. The direction in the
- status phase is the opposite of the direction during the data
- phase, or IN if there was no data phase. The host controller
- hardware also provides registers with the current status of the
- root ports and the changes that have occurred since the last
- reset of the status change register. Access to these registers
- is provided through a virtualised hub as suggested in the USB
- specification [ 2]. Thevirtual hub must comply with the hub
- device class given in chapter 11 of that specification. It must
- provide a default pipe through which device requests can be sent
- to it. It returns the standard andhub class specific set of
- descriptors. It should also provide an interrupt pipe that
- reports changes happening at its ports. There are currently two
- specifications for host controllers available: <ulink
- url="http://developer.intel.com/design/USB/UHCI11D.htm">Universal
- Host Controller Interface</ulink> (UHCI; Intel) and <ulink
- url="http://www.compaq.com/productinfo/development/openhci.html">Open
- Host Controller Interface</ulink> (OHCI; Compaq, Microsoft,
- National Semiconductor). The UHCI specification has been
- designed to reduce hardware complexity byrequiring the host
- controller driver to supply a complete schedule of the transfers
- for each frame. OHCI type controllers are much more independent
- by providing a more abstract interface doing alot of work
- themselves. </para>
-
- <sect2>
- <title>UHCI</title>
-
- <para>The UHCI host controller maintains a framelist with 1024
- pointers to per frame data structures. It understands two
- different data types: transfer descriptors (TD) and queue
- heads (QH). Each TD represents a packet to be communicated to
- or from a device endpoint. QHs are a means to groupTDs (and
- QHs) together.</para>
-
- <para>Each transfer consists of one or more packets. The UHCI
- driver splits large transfers into multiple packets. For every
- transfer, apart from isochronous transfers, a QH is
- allocated. For every type of transfer these QHs are collected
- at a QH for that type. Isochronous transfers have to be
- executed first because of the fixed latency requirement and
- are directly referred to by the pointer in the framelist. The
- last isochronous TD refers to the QH for interrupt transfers
- for that frame. All QHs for interrupt transfers point at the
- QH for control transfers, which in turn points at the QH for
- bulk transfers. The following diagram gives a graphical
- overview of this:</para>
-
- <para>This results in the following schedule being run in each
- frame. After fetching the pointer for the current frame from
- the framelist the controller first executes the TDs for all
- the isochronous packets in that frame. The last of these TDs
- refers to the QH for the interrupt transfers for
- thatframe. The host controller will then descend from that QH
- to the QHs for the individual interrupt transfers. After
- finishing that queue, the QH for the interrupt transfers will
- refer the controller to the QH for all control transfers. It
- will execute all the subqueues scheduled there, followed by
- all the transfers queued at the bulk QH. To facilitate the
- handling of finished or failed transfers different types of
- interrupts are generatedby the hardware at the end of each
- frame. In the last TD for a transfer the Interrupt-On
- Completion bit is set by the HC driver to flag an interrupt
- when the transfer has completed. An error interrupt is flagged
- if a TD reaches its maximum error count. If the short packet
- detect bit is set in a TD and less than the set packet length
- is transferred this interrupt is flagged to notify
- the controller driver of the completed transfer. It is the host
- controller driver's task to find out which transfer has
- completed or produced an error. When called the interrupt
- service routine will locate all the finished transfers and
- call their callbacks.</para>
-
- <para>See for a more elaborate description the <ulink
- url="http://developer.intel.com/design/USB/UHCI11D.htm">UHCI
- specification.</ulink></para>
-
- </sect2>
-
- <sect2>
- <title>OHCI</title>
-
- <para>Programming an OHCI host controller is much simpler. The
- controller assumes that a set of endpoints is available, and
- is aware of scheduling priorities and the ordering of the
- types of transfers in a frame. The main data structure used by
- the host controller is the endpoint descriptor (ED) to which
- aqueue of transfer descriptors (TDs) is attached. The ED
- contains the maximum packet size allowed for an endpoint and
- the controller hardware does the splitting into packets. The
- pointers to the data buffers are updated after each transfer
- and when the start and end pointer are equal, the TD is
- retired to the done-queue. The four types of endpoints have
- their own queues. Control and bulk endpoints are queued each at
- their own queue. Interrupt EDs are queued in a tree, with the
- level in the tree defining the frequency at which they
- run.</para>
-
- <para>framelist interruptisochronous control bulk</para>
-
- <para>The schedule being run by the host controller in each
- frame looks as follows. The controller will first run the
- non-periodic control and bulk queues, up to a time limit set
- by the HC driver. Then the interrupt transfers for that frame
- number are run, by using the lower five bits of the frame
- number as an index into level 0 of the tree of interrupts
- EDs. At the end of this tree the isochronous EDs are connected
- and these are traversed subsequently. The isochronous TDs
- contain the frame number of the first frame the transfer
- should be run in. After all the periodic transfers have been
- run, the control and bulk queues are traversed
- again. Periodically the interrupt service routine is called to
- process the done queue and call the callbacks for each
- transfer and reschedule interrupt and isochronous
- endpoints.</para>
-
- <para>See for a more elaborate description the <ulink
- url="http://www.compaq.com/productinfo/development/openhci.html">
- OHCI specification</ulink>. Services layer The middle layer
- provides access to the device in a controlled way and
- maintains resources inuse by the different drivers and the
- services layer. The layer takes care of the following
- aspects:</para>
-
- <itemizedlist>
- <listitem><para>The device configuration
- information</para></listitem>
- <listitem><para>The pipes to communicate with a
- device</para></listitem>
- <listitem><para>Probing and attaching and detaching form a
- device.</para></listitem>
- </itemizedlist>
-
- </sect2>
- </sect1>
-
- <sect1 id="usb-dev">
- <title>USB Device Information</title>
-
- <sect2>
- <title>Device configuration information</title>
-
- <para>Each device provides different levels of configuration
- information. Each device has one or more configurations, of
- which one is selected during probe/attach. A configuration
- provides power and bandwidth requirements. Within each
- configuration there can be multiple interfaces. A device
- interface is a collection of endpoints. For example USB
- speakers can have an interface for the audio data (Audio
- Class) and an interface for the knobs, dials and buttons (HID
- Class). All interfaces in a configuration areactive at the
- same time and can be attached to by different drivers. Each
- interface can have alternates, providing different quality of
- service parameters. In for example cameras this is used to
- provide different frame sizes and numbers of frames per
- second.</para>
-
- <para>Within each interface 0 or more endpoints can be
- specified. Endpoints are the unidirectional access points for
- communicating with a device. They provide buffers to
- temporarily store incoming or outgoing data from the
- device. Each endpoint has a unique address within
- a configuration, the endpoint's number plus its direction. The
- default endpoint, endpoint 0, is not part of any interface and
- available in all configurations. It is managed by the services
- layer and not directly available to device drivers.</para>
-
- <para>Level 0 Level 1 Level 2 Slot 0</para>
- <para>Slot 3 Slot 2 Slot 1</para>
- <para>(Only 4 out of 32 slots shown)</para>
-
- <para>This hierarchical configuration information is described
- in the device by a standard set of descriptors (see section 9.6
- of the USB specification [ 2]). They can be requested through
- the Get Descriptor Request. The services layer caches these
- descriptors to avoid unnecessary transferson the USB
- bus. Access to the descriptors is provided through function
- calls.</para>
-
- <itemizedlist>
- <listitem><para>Device descriptors: General information about
- the device, like Vendor, Product and Revision Id, supported
- device class, subclass and protocol if applicable, maximum
- packet size for the default endpoint, etc.</para></listitem>
-
- <listitem><para>Configuration descriptors: The number of
- interfaces in this configuration, suspend and resume
- functionality supported and power
- requirements.</para></listitem>
-
- <listitem><para>Interface descriptors: interface class,
- subclass and protocol if applicable, number of alternate
- settings for the interface and the number of
- endpoints.</para></listitem>
-
- <listitem><para>Endpoint descriptors: Endpoint address,
- direction and type, maximum packet size supported and
- polling frequency if type is interrupt endpoint. There is no
- descriptor for thedefault endpoint (endpoint 0) and it is
- never counted in an interface descriptor.</para></listitem>
-
- <listitem><para>String descriptors: In the other descriptors
- string indices are supplied for some fields.These can be
- used to retrieve descriptive strings, possibly in multiple
- languages.</para></listitem>
-
- </itemizedlist>
-
- <para>Class specifications can add their own descriptor types
- that are available through the GetDescriptor Request.</para>
-
- <para>Pipes Communication to end points on a device flows
- through so-called pipes. Drivers submit transfers to endpoints
- to a pipe and provide a callback to be called on completion or
- failure of the transfer (asynchronous transfers) or wait for
- completion (synchronous transfer). Transfers to an endpoint
- are serialised in the pipe. A transfer can either complete,
- fail or time-out (if a time-out has been set). There are two
- types of time-outs for transfers. Time-outs can happen due to
- time-out on the USBbus (milliseconds). These time-outs are
- seen as failures and can be due to disconnection of the
- device. A second form of time-out is implemented in software
- and is triggered when a transfer does not complete within a
- specified amount of time (seconds). These are caused by a
- device acknowledging negatively (NAK) the transferred
- packets. The cause for this is the device not being ready to
- receive data, buffer under- or overrun or protocol
- errors.</para>
-
- <para>If a transfer over a pipe is larger than the maximum
- packet size specified in the associated endpoint descriptor,
- the host controller (OHCI) or the HC driver (UHCI) will split
- the transfer into packets of maximum packet size, with the
- last packet possibly smaller than the maximum
- packetsize.</para>
-
- <para>Sometimes it is not a problem for a device to return less
- data than requested. For example abulk-in-transfer to a modem
- might request 200 bytes of data, but the modem has only 5
- bytes available at that time. The driver can set the short
- packet (SPD) flag. It allows the host controller to accept a
- packet even if the amount of data transferred is less than
- requested. This flag is only valid for in-transfers, as the
- amount of data to be sent to a device is always known
- beforehand. If an unrecoverable error occurs in a device
- during a transfer the pipe is stalled. Before any more data is
- accepted or sent the driver needs to resolve the cause of the
- stall and clear the endpoint stall condition through send the
- clear endpoint halt device request over the default
- pipe. The default endpoint should never stall.</para>
-
- <para>There are four different types of endpoints and
- corresponding pipes: - Control pipe / default pipe: There is
- one control pipe per device, connected to the default endpoint
- (endpoint 0). The pipe carries the device requests and
- associated data. The difference between transfers over the
- default pipe and other pipes is that the protocol for
- thetransfers is described in the USB specification [ 2]. These
- requests are used to reset and configure the device. A basic
- set of commands that must be supported by each device is
- provided in chapter 9 of the USB specification [ 2]. The
- commands supported on this pipe canbe extended by a device
- class specification to support additional
- functionality.</para>
-
- <itemizedlist>
- <listitem><para>Bulk pipe: This is the USB equivalent to a raw
- transmission medium.</para></listitem>
- <listitem><para>Interrupt pipe: The host sends a request for
- data to the device and if the device has nothing to send, it
- will NAK the data packet. Interrupt transfers are scheduled
- at a frequency specifiedwhen creating the
- pipe.</para></listitem>
-
- <listitem><para>Isochronous pipe: These pipes are intended for
- isochronous data, for example video oraudio streams, with
- fixed latency, but no guaranteed delivery. Some support for
- pipes of this type is available in the current
- implementation. Packets in control, bulk and interrupt
- transfers are retried if an error occurs during transmission
- or the device acknowledges the packet negatively (NAK) due to
- for example lack of buffer space to store the incoming
- data. Isochronous packets are however not retried in case of
- failed delivery or NAK of a packet as this might violate the
- timing constraints.</para></listitem>
- </itemizedlist>
-
- <para>The availability of the necessary bandwidth is calculated
- during the creation of the pipe. Transfersare scheduled within
- frames of 1 millisecond. The bandwidth allocation within a
- frame is prescribed by the USB specification, section 5.6 [
- 2]. Isochronous and interrupt transfers areallowed to consume
- up to 90% of the bandwidth within a frame. Packets for control
- and bulk transfers are scheduled after all isochronous and
- interrupt packets and will consume all the remaining
- bandwidth.</para>
-
- <para>More information on scheduling of transfers and bandwidth
- reclamation can be found in chapter 5of the USB specification
- [ 2], section 1.3 of the UHCI specification [ 3] and section
- 3.4.2 of the OHCI specification [4].</para>
-
- </sect2>
- </sect1>
-
- <sect1 id="usb-devprobe">
- <title>Device probe and attach</title>
-
- <para>After the notification by the hub that a new device has been
- connected, the service layer switcheson the port, providing the
- device with 100 mA of current. At this point the device is in
- its default state and listening to device address 0. The
- services layer will proceed to retrieve the various descriptors
- through the default pipe. After that it will send a Set Address
- request to move the device away from the default device address
- (address 0). Multiple device drivers might be able to support
- the device. For example a modem driver might beable to support
- an ISDN TA through the AT compatibility interface. A driver for
- that specific model of the ISDN adapter might however be able to
- provide much better support for this device. To support this
- flexibility, the probes return priorities indicating their level
- of support. Support for a specific revision of a product ranks
- the highest and the generic driver the lowest priority. It might
- also be that multiple drivers could attach to one device if
- there are multiple interfaceswithin one configuration. Each
- driver only needs to support a subset of the interfaces.</para>
-
- <para>The probing for a driver for a newly attached device checks
- first for device specific drivers. If notfound, the probe code
- iterates over all supported configurations until a driver
- attaches in a configuration. To support devices with multiple
- drivers on different interfaces, the probe iteratesover all
- interfaces in a configuration that have not yet been claimed by
- a driver. Configurations that exceed the power budget for the
- hub are ignored. During attach the driver should initialise the
- device to its proper state, but not reset it, as this will make
- the device disconnect itself from the bus and restart the
- probing process for it. To avoid consuming unnecessary bandwidth
- should not claim the interrupt pipe at attach time, but
- should postpone allocating the pipe until the file is opened and
- the data is actually used. When the file is closed the pipe
- should be closed again, eventhough the device might still be
- attached.</para>
-
- <sect2>
- <title>Device disconnect and detach</title>
-
- <para>A device driver should expect to receive errors during any
- transaction with the device. The designof USB supports and
- encourages the disconnection of devices at any point in
- time. Drivers should make sure that they do the right thing
- when the device disappears.</para>
-
- <para>Furthermore a device that has been disconnected and
- reconnected will not be reattached at the same device
- instance. This might change in the future when more devices
- support serial numbers (see the device descriptor) or other
- means of defining an identity for a device have been
- developed.</para>
-
- <para>The disconnection of a device is signalled by a hub in the
- interrupt packet delivered to the hub driver. The status
- change information indicates which port has seen a connection
- change. The device detach method for all device drivers for
- the device connected on that port are called and the structures
- cleaned up. If the port status indicates that in the mean time
- a device has been connected to that port, the procedure for
- probing and attaching the device will be started. A device
- reset will produce a disconnect-connect sequence on the hub
- and will be handled as described above.</para>
-
- </sect2>
- </sect1>
-
- <sect1 id="usb-protocol">
- <title>USB Drivers Protocol Information</title>
-
- <para>The protocol used over pipes other than the default pipe is
- undefined by the USB specification. Information on this can be
- found from various sources. The most accurate source is the
- developer's section on the USB home pages [ 1]. From these pages
- a growing number of deviceclass specifications are
- available. These specifications specify what a compliant device
- should look like from a driver perspective, basic functionality
- it needs to provide and the protocol that is to be used over the
- communication channels. The USB specification [ 2] includes the
- description of the Hub Class. A class specification for Human
- Interface Devices (HID) has been created to cater for keyboards,
- tablets, bar-code readers, buttons, knobs, switches, etc. A
- third example is the class specification for mass storage
- devices. For a full list of device classes see the developers
- sectionon the USB home pages [ 1].</para>
-
- <para>For many devices the protocol information has not yet been
- published however. Information on the protocol being used might
- be available from the company making the device. Some companies
- will require you to sign a Non -Disclosure Agreement (NDA)
- before giving you the specifications. This in most cases
- precludes making the driver open source.</para>
-
- <para>Another good source of information is the Linux driver
- sources, as a number of companies have started to provide drivers
- for Linux for their devices. It is always a good idea to contact
- the authors of those drivers for their source of
- information.</para>
-
- <para>Example: Human Interface Devices The specification for the
- Human Interface Devices like keyboards, mice, tablets, buttons,
- dials,etc. is referred to in other device class specifications
- and is used in many devices.</para>
-
- <para>For example audio speakers provide endpoints to the digital
- to analogue converters and possibly an extra pipe for a
- microphone. They also provide a HID endpoint in a separate
- interface for the buttons and dials on the front of the
- device. The same is true for the monitor control class. It is
- straightforward to build support for these interfaces through
- the available kernel and userland libraries together with the
- HID class driver or the generic driver. Another device that
- serves as an example for interfaces within one configuration
- driven by different device drivers is a cheap keyboard with
- built-in legacy mouse port. To avoid having the cost of
- including the hardware for a USB hub in the device,
- manufacturers combined the mouse data received from the PS/2 port
- on the back of the keyboard and the keypresses from the keyboard
- into two separate interfaces in the same configuration. The
- mouse and keyboard drivers each attach to the appropriate
- interface and allocate the pipes to the two independent
- endpoints.</para>
-
- <para>Example: Firmware download Many devices that have been
- developed are based on a general purpose processor with
- anadditional USB core added to it. Because the development of
- drivers and firmware for USB devices is still very new, many
- devices require the downloading of the firmware after they
- have been connected.</para>
-
- <para>The procedure followed is straightforward. The device
- identifies itself through a vendor and product Id. The first
- driver probes and attaches to it and downloads the firmware into
- it. After that the device soft resets itself and the driver is
- detached. After a short pause the devicere announces its presence
- on the bus. The device will have changed its
- vendor/product/revision Id to reflect the fact that it has been
- supplied with firmware and as a consequence a second driver will
- probe it and attach to it.</para>
-
- <para>An example of these types of devices is the ActiveWire I/O
- board, based on the EZ-USB chip. For this chip a generic firmware
- downloader is available. The firmware downloaded into the
- ActiveWire board changes the revision Id. It will then perform a
- soft reset of the USB part of the EZ-USB chip to disconnect from
- the USB bus and again reconnect.</para>
-
- <para>Example: Mass Storage Devices Support for mass storage
- devices is mainly built around existing protocols. The Iomega
- USB Zipdrive is based on the SCSI version of their drive. The
- SCSI commands and status messages are wrapped in blocks and
- transferred over the bulk pipes to and from the device,
- emulating a SCSI controller over the USB wire. ATAPI and UFI
- commands are supported in a similar fashion.</para>
-
- <para>The Mass Storage Specification supports 2 different types of
- wrapping of the command block.The initial attempt was based on
- sending the command and status through the default pipe and
- using bulk transfers for the data to be moved between the host
- and the device. Based on experience a second approach was
- designed that was based on wrapping the command and status
- blocks and sending them over the bulk out and in endpoint. The
- specification specifies exactly what has to happen when and what
- has to be done in case an error condition is encountered. The
- biggest challenge when writing drivers for these devices is to
- fit USB based protocol into theexisting support for mass storage
- devices. CAM provides hooks to do this in a fairly straight
- forward way. ATAPI is less simple as historically the IDE
- interface has never had many different appearances.</para>
-
- <para>The support for the USB floppy from Y-E Data is again less
- straightforward as a new command set has been designed.</para>
-
- </sect1>
-
-</chapter> \ No newline at end of file
diff --git a/en_US.ISO8859-1/books/arch-handbook/vm/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/vm/chapter.sgml
deleted file mode 100644
index 4710973d5f..0000000000
--- a/en_US.ISO8859-1/books/arch-handbook/vm/chapter.sgml
+++ /dev/null
@@ -1,255 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: doc/en_US.ISO_8859-1/books/developers-handbook/usb/chapter.sgml,v 1.1 2001/04/13 09:05:13 murray Exp $
--->
-
-<chapter id="vm">
- <title>Virtual Memory System</title>
-
- <sect1 id="internals-vm">
- <title>The FreeBSD VM System</title>
-
- <para><emphasis>Contributed by &a.dillon;. 6 Feb 1999</emphasis></para>
-
- <sect2>
- <title>Management of physical
- memory&mdash;<literal>vm_page_t</literal></title>
-
- <para>Physical memory is managed on a page-by-page basis through the
- <literal>vm_page_t</literal> structure. Pages of physical memory are
- categorized through the placement of their respective
- <literal>vm_page_t</literal> structures on one of several paging
- queues.</para>
-
- <para>A page can be in a wired, active, inactive, cache, or free state.
- Except for the wired state, the page is typically placed in a doubly
- link list queue representing the state that it is in. Wired pages
- are not placed on any queue.</para>
-
- <para>FreeBSD implements a more involved paging queue for cached and
- free pages in order to implement page coloring. Each of these states
- involves multiple queues arranged according to the size of the
- processor's L1 and L2 caches. When a new page needs to be allocated,
- FreeBSD attempts to obtain one that is reasonably well aligned from
- the point of view of the L1 and L2 caches relative to the VM object
- the page is being allocated for.</para>
-
- <para>Additionally, a page may be held with a reference count or locked
- with a busy count. The VM system also implements an <quote>ultimate
- locked</quote> state for a page using the PG_BUSY bit in the page's
- flags.</para>
-
- <para>In general terms, each of the paging queues operates in a LRU
- fashion. A page is typically placed in a wired or active state
- initially. When wired, the page is usually associated with a page
- table somewhere. The VM system ages the page by scanning pages in a
- more active paging queue (LRU) in order to move them to a less-active
- paging queue. Pages that get moved into the cache are still
- associated with a VM object but are candidates for immediate reuse.
- Pages in the free queue are truly free. FreeBSD attempts to minimize
- the number of pages in the free queue, but a certain minimum number of
- truly free pages must be maintained in order to accommodate page
- allocation at interrupt time.</para>
-
- <para>If a process attempts to access a page that does not exist in its
- page table but does exist in one of the paging queues ( such as the
- inactive or cache queues), a relatively inexpensive page reactivation
- fault occurs which causes the page to be reactivated. If the page
- does not exist in system memory at all, the process must block while
- the page is brought in from disk.</para>
-
- <para>FreeBSD dynamically tunes its paging queues and attempts to
- maintain reasonable ratios of pages in the various queues as well as
- attempts to maintain a reasonable breakdown of clean v.s. dirty pages.
- The amount of rebalancing that occurs depends on the system's memory
- load. This rebalancing is implemented by the pageout daemon and
- involves laundering dirty pages (syncing them with their backing
- store), noticing when pages are activity referenced (resetting their
- position in the LRU queues or moving them between queues), migrating
- pages between queues when the queues are out of balance, and so forth.
- FreeBSD's VM system is willing to take a reasonable number of
- reactivation page faults to determine how active or how idle a page
- actually is. This leads to better decisions being made as to when to
- launder or swap-out a page.</para>
- </sect2>
-
- <sect2>
- <title>The unified buffer
- cache&mdash;<literal>vm_object_t</literal></title>
-
- <para>FreeBSD implements the idea of a generic <quote>VM object</quote>.
- VM objects can be associated with backing store of various
- types&mdash;unbacked, swap-backed, physical device-backed, or
- file-backed storage. Since the filesystem uses the same VM objects to
- manage in-core data relating to files, the result is a unified buffer
- cache.</para>
-
- <para>VM objects can be <emphasis>shadowed</emphasis>. That is, they
- can be stacked on top of each other. For example, you might have a
- swap-backed VM object stacked on top of a file-backed VM object in
- order to implement a MAP_PRIVATE mmap()ing. This stacking is also
- used to implement various sharing properties, including,
- copy-on-write, for forked address spaces.</para>
-
- <para>It should be noted that a <literal>vm_page_t</literal> can only be
- associated with one VM object at a time. The VM object shadowing
- implements the perceived sharing of the same page across multiple
- instances.</para>
- </sect2>
-
- <sect2>
- <title>Filesystem I/O&mdash;<literal>struct buf</literal></title>
-
- <para>vnode-backed VM objects, such as file-backed objects, generally
- need to maintain their own clean/dirty info independent from the VM
- system's idea of clean/dirty. For example, when the VM system decides
- to synchronize a physical page to its backing store, the VM system
- needs to mark the page clean before the page is actually written to
- its backing s tore. Additionally, filesystems need to be able to map
- portions of a file or file metadata into KVM in order to operate on
- it.</para>
-
- <para>The entities used to manage this are known as filesystem buffers,
- <literal>struct buf</literal>'s, and also known as
- <literal>bp</literal>'s. When a filesystem needs to operate on a
- portion of a VM object, it typically maps part of the object into a
- struct buf and the maps the pages in the struct buf into KVM. In the
- same manner, disk I/O is typically issued by mapping portions of
- objects into buffer structures and then issuing the I/O on the buffer
- structures. The underlying vm_page_t's are typically busied for the
- duration of the I/O. Filesystem buffers also have their own notion of
- being busy, which is useful to filesystem driver code which would
- rather operate on filesystem buffers instead of hard VM pages.</para>
-
- <para>FreeBSD reserves a limited amount of KVM to hold mappings from
- struct bufs, but it should be made clear that this KVM is used solely
- to hold mappings and does not limit the ability to cache data.
- Physical data caching is strictly a function of
- <literal>vm_page_t</literal>'s, not filesystem buffers. However,
- since filesystem buffers are used placehold I/O, they do inherently
- limit the amount of concurrent I/O possible. As there are usually a
- few thousand filesystem buffers available, this is not usually a
- problem.</para>
- </sect2>
-
- <sect2>
- <title>Mapping Page Tables - vm_map_t, vm_entry_t</title>
-
- <para>FreeBSD separates the physical page table topology from the VM
- system. All hard per-process page tables can be reconstructed on the
- fly and are usually considered throwaway. Special page tables such as
- those managing KVM are typically permanently preallocated. These page
- tables are not throwaway.</para>
-
- <para>FreeBSD associates portions of vm_objects with address ranges in
- virtual memory through <literal>vm_map_t</literal> and
- <literal>vm_entry_t</literal> structures. Page tables are directly
- synthesized from the
- <literal>vm_map_t</literal>/<literal>vm_entry_t</literal>/
- <literal>vm_object_t</literal> hierarchy. Remember when I mentioned
- that physical pages are only directly associated with a
- <literal>vm_object</literal>. Well, that isn't quite true.
- <literal>vm_page_t</literal>'s are also linked into page tables that
- they are actively associated with. One <literal>vm_page_t</literal>
- can be linked into several <emphasis>pmaps</emphasis>, as page tables
- are called. However, the hierarchical association holds so all
- references to the same page in the same object reference the same
- <literal>vm_page_t</literal> and thus give us buffer cache unification
- across the board.</para>
- </sect2>
-
- <sect2>
- <title>KVM Memory Mapping</title>
-
- <para>FreeBSD uses KVM to hold various kernel structures. The single
- largest entity held in KVM is the filesystem buffer cache. That is,
- mappings relating to <literal>struct buf</literal> entities.</para>
-
- <para>Unlike Linux, FreeBSD does NOT map all of physical memory into
- KVM. This means that FreeBSD can handle memory configurations up to
- 4G on 32 bit platforms. In fact, if the mmu were capable of it,
- FreeBSD could theoretically handle memory configurations up to 8TB on
- a 32 bit platform. However, since most 32 bit platforms are only
- capable of mapping 4GB of ram, this is a moot point.</para>
-
- <para>KVM is managed through several mechanisms. The main mechanism
- used to manage KVM is the <emphasis>zone allocator</emphasis>. The
- zone allocator takes a chunk of KVM and splits it up into
- constant-sized blocks of memory in order to allocate a specific type
- of structure. You can use <command>vmstat -m</command> to get an
- overview of current KVM utilization broken down by zone.</para>
- </sect2>
-
- <sect2>
- <title>Tuning the FreeBSD VM system</title>
-
- <para>A concerted effort has been made to make the FreeBSD kernel
- dynamically tune itself. Typically you do not need to mess with
- anything beyond the <literal>maxusers</literal> and
- <literal>NMBCLUSTERS</literal> kernel config options. That is, kernel
- compilation options specified in (typically)
- <filename>/usr/src/sys/i386/conf/<replaceable>CONFIG_FILE</replaceable></filename>.
- A description of all available kernel configuration options can be
- found in <filename>/usr/src/sys/i386/conf/LINT</filename>.</para>
-
- <para>In a large system configuration you may wish to increase
- <literal>maxusers</literal>. Values typically range from 10 to 128.
- Note that raising <literal>maxusers</literal> too high can cause the
- system to overflow available KVM resulting in unpredictable operation.
- It is better to leave maxusers at some reasonable number and add other
- options, such as <literal>NMBCLUSTERS</literal>, to increase specific
- resources.</para>
-
- <para>If your system is going to use the network heavily, you may want
- to increase <literal>NMBCLUSTERS</literal>. Typical values range from
- 1024 to 4096.</para>
-
- <para>The <literal>NBUF</literal> parameter is also traditionally used
- to scale the system. This parameter determines the amount of KVA the
- system can use to map filesystem buffers for I/O. Note that this
- parameter has nothing whatsoever to do with the unified buffer cache!
- This parameter is dynamically tuned in 3.0-CURRENT and later kernels
- and should generally not be adjusted manually. We recommend that you
- <emphasis>not</emphasis> try to specify an <literal>NBUF</literal>
- parameter. Let the system pick it. Too small a value can result in
- extremely inefficient filesystem operation while too large a value can
- starve the page queues by causing too many pages to become wired
- down.</para>
-
- <para>By default, FreeBSD kernels are not optimized. You can set
- debugging and optimization flags with the
- <literal>makeoptions</literal> directive in the kernel configuration.
- Note that you should not use <option>-g</option> unless you can
- accommodate the large (typically 7 MB+) kernels that result.</para>
-
- <programlisting>makeoptions DEBUG="-g"
-makeoptions COPTFLAGS="-O -pipe"</programlisting>
-
- <para>Sysctl provides a way to tune kernel parameters at run-time. You
- typically do not need to mess with any of the sysctl variables,
- especially the VM related ones.</para>
-
- <para>Run time VM and system tuning is relatively straightforward.
- First, use softupdates on your UFS/FFS filesystems whenever possible.
- <filename>/usr/src/contrib/sys/softupdates/README</filename> contains
- instructions (and restrictions) on how to configure it up.</para>
-
- <para>Second, configure sufficient swap. You should have a swap
- partition configured on each physical disk, up to four, even on your
- <quote>work</quote> disks. You should have at least 2x the swap space
- as you have main memory, and possibly even more if you do not have a
- lot of memory. You should also size your swap partition based on the
- maximum memory configuration you ever intend to put on the machine so
- you do not have to repartition your disks later on. If you want to be
- able to accommodate a crash dump, your first swap partition must be at
- least as large as main memory and <filename>/var/crash</filename> must
- have sufficient free space to hold the dump.</para>
-
- <para>NFS-based swap is perfectly acceptable on -4.x or later systems,
- but you must be aware that the NFS server will take the brunt of the
- paging load.</para>
- </sect2>
- </sect1>
-
-</chapter>
diff --git a/en_US.ISO8859-1/books/handbook/basics/disk-layout.kil b/en_US.ISO8859-1/books/handbook/basics/disk-layout.kil
deleted file mode 100644
index 85820c2878..0000000000
--- a/en_US.ISO8859-1/books/handbook/basics/disk-layout.kil
+++ /dev/null
Binary files differ
diff --git a/en_US.ISO8859-1/books/handbook/book.sgml b/en_US.ISO8859-1/books/handbook/book.sgml
index f07264b320..1ff7706f47 100644
--- a/en_US.ISO8859-1/books/handbook/book.sgml
+++ b/en_US.ISO8859-1/books/handbook/book.sgml
@@ -1,7 +1,7 @@
<!--
The FreeBSD Documentation Project
- $FreeBSD: doc/en_US.ISO8859-1/books/handbook/book.sgml,v 1.110 2001/08/04 19:32:09 jim Exp $
+ $FreeBSD: doc/en_US.ISO8859-1/books/handbook/book.sgml,v 1.114 2001/09/18 04:14:43 murray Exp $
-->
<!DOCTYPE BOOK PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
@@ -18,13 +18,13 @@
<!ENTITY % authors PUBLIC "-//FreeBSD//ENTITIES DocBook Author Entities//EN">
%authors;
<!ENTITY % mailing-lists PUBLIC "-//FreeBSD//ENTITIES DocBook Mailing List Entities//EN"> %mailing-lists;
-<!ENTITY % newsgroups SYSTEM "newsgroups.ent"> %newsgroups;
+<!ENTITY % newsgroups PUBLIC "-//FreeBSD//ENTITIES DocBook Newsgroup Entities//EN"> %newsgroups;
<!ENTITY % not.published "INCLUDE">
<!-- The currently released version of FreeBSD. This value is used to
create some links on web sites and such, so do NOT change it until
it's really release time -->
-<!ENTITY rel.current CDATA "4.3">
+<!ENTITY rel.current CDATA "4.4">
<!ENTITY % chap.introduction "IGNORE">
<!ENTITY % chap.install "IGNORE">
@@ -46,7 +46,6 @@
<!ENTITY % chap.advanced-networking "IGNORE">
<!ENTITY % chap.mail "IGNORE">
<!ENTITY % chap.cutting-edge "IGNORE">
-<!ENTITY % chap.contrib "IGNORE">
<!ENTITY % chap.policies "IGNORE">
<!ENTITY % chap.kerneldebug "IGNORE">
<!ENTITY % chap.linuxemu "IGNORE">
@@ -169,7 +168,6 @@
<![ %chap.security; [ &chap.security; ]]>
<![ %chap.printing; [ &chap.printing; ]]>
<![ %chap.disks; [ &chap.disks; ]]>
- <![ %chap.backups; [ &chap.backups; ]]>
<![ %chap.l10n; [ &chap.l10n; ]]>
<![ %chap.sound; [ &chap.sound; ]]>
<![ %chap.serialcomms; [ &chap.serialcomms; ]]>
@@ -177,7 +175,6 @@
<![ %chap.advanced-networking; [ &chap.advanced-networking; ]]>
<![ %chap.mail; [ &chap.mail; ]]>
<![ %chap.cutting-edge; [ &chap.cutting-edge; ]]>
- <![ %chap.contrib; [ &chap.contrib; ]]>
<![ %chap.policies; [ &chap.policies; ]]>
<![ %chap.kerneldebug; [ &chap.kerneldebug; ]]>
<![ %chap.linuxemu; [ &chap.linuxemu; ]]>
diff --git a/en_US.ISO8859-1/books/handbook/introduction/chapter.sgml b/en_US.ISO8859-1/books/handbook/introduction/chapter.sgml
index c4b8b02501..0f8d1937b5 100644
--- a/en_US.ISO8859-1/books/handbook/introduction/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/introduction/chapter.sgml
@@ -1,7 +1,7 @@
<!--
The FreeBSD Documentation Project
- $FreeBSD: doc/en_US.ISO8859-1/books/handbook/introduction/chapter.sgml,v 1.58 2001/08/14 23:02:37 logo Exp $
+ $FreeBSD: doc/en_US.ISO8859-1/books/handbook/introduction/chapter.sgml,v 1.59 2001/08/16 18:35:03 chern Exp $
-->
<chapter id="introduction">
@@ -34,8 +34,8 @@
read about <link linkend="history">the history of FreeBSD</link>,
or the <link linkend="relnotes">current release</link>. If you
are interested in contributing something to the Project (code,
- hardware, unmarked bills), see the <link
- linkend="contrib">contributing to FreeBSD</link> section.</para>
+ hardware, unmarked bills), see the <ulink url="
+ http://www.FreeBSD.org/doc/en_US.ISO8859-1/articles/contributing/article.html">Contributing to FreeBSD</ulink> article.</para>
</sect1>
<sect1 id="nutshell">
@@ -767,8 +767,9 @@
<para>Providing code is not the only way of contributing to
the project; for a more complete list of things that need
- doing, please refer to the <link linkend="contrib">how to
- contribute</link> section in this handbook.</para>
+ doing, please refer to the <ulink
+ url="http://www.FreeBSD.org">FreeBSD Project web
+ site</ulink>.</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/en_US.ISO8859-1/books/handbook/mirrors/chapter.sgml b/en_US.ISO8859-1/books/handbook/mirrors/chapter.sgml
index c447e38c9c..3e320d91af 100644
--- a/en_US.ISO8859-1/books/handbook/mirrors/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/mirrors/chapter.sgml
@@ -1,7 +1,7 @@
<!--
The FreeBSD Documentation Project
- $FreeBSD: doc/en_US.ISO8859-1/books/handbook/mirrors/chapter.sgml,v 1.150 2001/09/02 11:16:16 murray Exp $
+ $FreeBSD: doc/en_US.ISO8859-1/books/handbook/mirrors/chapter.sgml,v 1.155 2001/09/18 12:54:12 dcs Exp $
-->
<appendix id="mirrors">
@@ -51,7 +51,7 @@
<listitem>
<address>
<otheraddr>Daemon News</otheraddr>
- <street>2680 Bayshore Parkway, Suite 307</street>
+ <street>2672 Bayshore Parkway, Suite 610</street>
<city>Mountain View</city>, <state>CA</state> <postcode>94043</postcode>
<country>USA</country>
Phone: <phone>+1 650 694-4949</phone>
@@ -83,7 +83,7 @@
<listitem>
<address>
<otheraddr>Cylogistics</otheraddr>
- <street>2680 Bayshore Parkway, Suite 307</street>
+ <street>2672 Bayshore Parkway, Suite 610</street>
<city>Mountain View</city>, <state>CA</state> <postcode>94043</postcode>
<country>USA</country>
Phone: <phone>+1 650 694-4949</phone>
@@ -1116,6 +1116,15 @@
</varlistentry>
<varlistentry>
+ <term>RELENG_4_4</term>
+
+ <listitem>
+ <para>The release branch for FreeBSD-4.4, used only
+ for security advisories and other seriously critical fixes.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>RELENG_4_3</term>
<listitem>
@@ -1150,6 +1159,14 @@
<variablelist>
<varlistentry>
+ <term>RELENG_4_4_0_RELEASE</term>
+
+ <listitem>
+ <para>FreeBSD 4.4.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>RELENG_4_3_0_RELEASE</term>
<listitem>
@@ -1985,6 +2002,16 @@
</varlistentry>
<varlistentry>
+ <term>tag=RELENG_4_4</term>
+
+ <listitem>
+ <para>The release branch for FreeBSD-4.4, used only
+ for security advisories and other seriously critical
+ fixes.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>tag=RELENG_4_3</term>
<listitem>
@@ -2018,6 +2045,14 @@
<variablelist>
<varlistentry>
+ <term>tag=RELENG_4_4_0_RELEASE</term>
+
+ <listitem>
+ <para>FreeBSD 4.4.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>tag=RELENG_4_3_0_RELEASE</term>
<listitem>
@@ -3296,6 +3331,11 @@ doc/ja_JP.eucJP</screen>
<para>cvsup3.br.FreeBSD.org (maintainer
<email>camposr@matrix.com.br</email>)</para>
</listitem>
+
+ <listitem>
+ <para>cvsup4.br.FreeBSD.org (maintainer
+ <email>cvsup@tcoip.com.br</email>)</para>
+ </listitem>
</itemizedlist>
</listitem>
</varlistentry>
@@ -3821,7 +3861,7 @@ doc/ja_JP.eucJP</screen>
<itemizedlist>
<listitem>
<para>cvsup1.FreeBSD.org (maintainer
- <email>skynyrd@opus.cts.cwu.edu</email>), Washington
+ <email>cwt@networks.cwu.edu</email>), Washington
state</para>
</listitem>
@@ -3846,6 +3886,11 @@ doc/ja_JP.eucJP</screen>
</listitem>
<listitem>
+ <para>cvsup6.FreeBSD.org (maintainer
+ <email>cvsup@cvsup.adelphiacom.net</email>), Illinois</para>
+ </listitem>
+
+ <listitem>
<para>cvsup7.FreeBSD.org (maintainer
<email>jdp@FreeBSD.org</email>), Washington state</para>
</listitem>
diff --git a/en_US.ISO8859-1/books/handbook/multimedia/Makefile b/en_US.ISO8859-1/books/handbook/multimedia/Makefile
deleted file mode 100644
index 0ea7f84352..0000000000
--- a/en_US.ISO8859-1/books/handbook/multimedia/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-#
-# Build the Handbook with just the content from this chapter.
-#
-# $FreeBSD$
-#
-
-CHAPTERS= sound/chapter.sgml
-
-VPATH= ..
-
-MASTERDOC= ${.CURDIR}/../${DOC}.${DOCBOOKSUFFIX}
-
-DOC_PREFIX?= ${.CURDIR}/../../../..
-
-.include "../Makefile"
diff --git a/en_US.ISO8859-1/books/handbook/multimedia/chapter.sgml b/en_US.ISO8859-1/books/handbook/multimedia/chapter.sgml
deleted file mode 100644
index 5ceb0bf41c..0000000000
--- a/en_US.ISO8859-1/books/handbook/multimedia/chapter.sgml
+++ /dev/null
@@ -1,368 +0,0 @@
-<!--
- The FreeBSD Documentation Project
-
- $FreeBSD: doc/en_US.ISO8859-1/books/handbook/sound/chapter.sgml,v 1.15 2001/08/16 18:35:08 chern Exp $
--->
-
-<chapter id="sound">
- <chapterinfo>
- <authorgroup>
- <author>
- <firstname>Moses</firstname>
- <surname>Moore</surname>
- <contrib>Contributed by </contrib>
- </author>
- </authorgroup>
- <!-- 20 November 2000 -->
- </chapterinfo>
-
- <title>Sound</title>
-
- <sect1>
- <title>Synopsis</title>
-
- <para>FreeBSD supports a wide variety of sound cards, allowing you
- to enjoy high fidelity output from your computer. This includes
- the ability to record and playback audio in the MPEG Audio Layer
- 3 (MP3), WAV, and Ogg Vorbis formats as well as many other
- formats. The FreeBSD Ports Collection also contains
- applications allowing you to edit your recorded audio, add sound
- effects, and control attached MIDI devices.</para>
-
-<!-- XXX we need to talk about ripping MP3s here. -->
-
- <para>After reading this chapter you will know:</para>
- <itemizedlist>
- <listitem><para>How to locate your sound card.</para></listitem>
- <listitem><para>How to configure your system so that your sound card is
- recognized.</para></listitem>
- <listitem><para>Methods to test that your card is working using
- sample applications.</para></listitem>
- <listitem><para>How to troubleshoot your sound setup.</para></listitem>
- </itemizedlist>
-
- <para>Before reading this chapter you should:</para>
-
- <itemizedlist>
- <listitem><para>Know how to configure and install a new kernel (<xref
- linkend="kernelconfig">).</para></listitem>
- </itemizedlist>
- </sect1>
-
- <sect1>
- <title>Locating the Correct Device</title>
-
- <indexterm><primary>PCI</primary></indexterm>
- <indexterm><primary>ISA</primary></indexterm>
- <indexterm><primary>sound cards</primary></indexterm>
- <para>Before you begin, you should know the model of the card you
- have, the chip it uses, and whether it is a PCI or ISA card.
- FreeBSD supports a wide variety of both PCI and ISA cards. If you
- do not see your card in the following list, check the &man.pcm.4;
- manual page. This is not a complete list; however, it does list
- some of the most common cards.</para>
-
- <itemizedlist>
- <listitem>
- <para>Crystal 4237, 4236, 4232, 4231</para>
- </listitem>
-
- <listitem>
- <para>Yamaha OPL-SAx</para>
- </listitem>
-
- <listitem>
- <para>OPTi931</para>
- </listitem>
-
- <listitem>
- <para>Ensoniq AudioPCI 1370/1371</para>
- </listitem>
-
- <listitem>
- <para>ESS Solo-1/1E</para>
- </listitem>
-
- <listitem>
- <para>NeoMagic 256AV/ZX</para>
- </listitem>
-
- <listitem>
- <para>Sound Blaster Pro, 16, 32, AWE64, AWE128, Live</para>
- </listitem>
-
- <listitem>
- <para>Creative ViBRA16</para>
- </listitem>
-
- <listitem>
- <para>Advanced Asound 100, 110, and Logic ALS120</para>
- </listitem>
-
- <listitem>
- <para>ES 1868, 1869, 1879, 1888</para>
- </listitem>
-
- <listitem>
- <para>Gravis UltraSound</para>
- </listitem>
-
- <listitem>
- <para>Aureal Vortex 1 or 2</para>
- </listitem>
- </itemizedlist>
-
- <indexterm>
- <primary>kernel</primary>
- <secondary>configuration</secondary>
- </indexterm>
- <para>The driver you use in your kernel depends on the kind of card
- you have. The sections below provide more information and what
- you will need to add to your <link linkend="kernelconfig">kernel
- configuration</link>.</para>
-
- <sect2>
- <title>Creative, Advance, and ESS Sound Cards</title>
-
- <para>If you have one of the above cards, you will need to
- add</para>
-
- <programlisting>device pcm</programlisting>
-
- <para>to your kernel. If you have a PnP ISA card, you will also
- need to add</para>
-
- <programlisting>device sbc</programlisting>
-
- <para>to your kernel. For a non-PnP ISA card, add</para>
-
- <programlisting>device pcm</programlisting>
-
- <para>and</para>
-
- <programlisting>device sbc0 at isa? port0x220 irq 5 drq 1 flags 0x15</programlisting>
-
- <para>to your kernel. Those are the default settings. You may
- need to change the IRQ, etc. See the &man.sbc.4; manual page for
- more information.</para>
-
- <note>
- <para>The Sound Blaster Live is not supported under FreeBSD 4.0
- without a patch, which this document will not cover. It is
- recommended that you update to the latest -STABLE before
- trying to use this card.</para>
- </note>
- </sect2>
-
- <sect2>
- <title>Gravis UltraSound Cards</title>
-
- <para>For a PnP ISA card, you will need to add</para>
-
- <programlisting>device pcm</programlisting>
-
- <para>and</para>
-
- <programlisting>device gusc</programlisting>
-
- <para>to your kernel. If you have a non-PnP ISA card, you will
- need to add</para>
-
- <programlisting>device pcm</programlisting>
-
- <para>and</para>
-
- <programlisting>device gus0 at isa? port 0x220 irq 5 drq 1 flags 0x13</programlisting>
-
- <para>to your kernel. You may need to change the IRQ, etc. See
- the &man.gusc.4; manual page for more information.</para>
- </sect2>
-
- <sect2>
- <title>Crystal Sound Cards</title>
-
- <para>For Crystal cards, you will need both</para>
-
- <programlisting>device pcm</programlisting>
-
- <para>and</para>
-
- <programlisting>device csa</programlisting>
-
- <para>in your kernel.</para>
- </sect2>
-
- <sect2>
- <title>Generic Support</title>
-
- <para>For PnP ISA or PCI cards, you will need to add</para>
-
- <programlisting>device pcm</programlisting>
-
- <para>to your kernel configuration. If you have a non-PnP ISA
- sound card that does not have a bridge driver, you will need
- to add</para>
-
- <programlisting>device pcm0 at isa? irq 10 drq 1 flags 0x0</programlisting>
-
- <para>to your kernel configuration. You may need to change the
- IRQ, etc., to match your hardware configuration.</para>
- </sect2>
- </sect1>
-
- <sect1>
- <title>Recompiling the Kernel</title>
-
- <para>After adding the driver(s) you need to your kernel
- configuration, you will need to recompile your kernel. Please see
- <xref linkend="kernelconfig-building"> of the handbook for
- more information.</para>
- </sect1>
-
- <sect1>
- <title>Creating and Testing the Device Nodes</title>
-
- <indexterm><primary>device nodes</primary></indexterm>
- <para>After you reboot, log in and run <command>cat
- /dev/sndstat</command>. You should see output similar to the
- following:</para>
-
- <programlisting>FreeBSD Audio Driver (newpcm) Sep 21 2000 18:29:53
-Installed devices:
-pcm0: &lt;Aureal Vortex 8830&gt; at memory 0xfeb40000 irq 5 (4p/1r +channels duplex)</programlisting>
-
- <para>If you see an error message, something went wrong earlier. If
- that happens, go through your kernel configuration file again and
- make sure you chose the correct device.</para>
-
- <para>If it reported no errors and returned
- <devicename>pcm0</devicename>, <command>su</command> to
- <username>root</username> and do the following:</para>
-
- <screen>&prompt.root; <userinput>cd /dev</userinput>
-&prompt.root; <userinput>sh MAKEDEV snd0</userinput></screen>
-
- <para>If it reported no errors and returned
- <devicename>pcm1</devicename>, <command>su</command> to
- <username>root</username> and do the following:</para>
-
- <screen>&prompt.root; <userinput>cd /dev</userinput>
-&prompt.root; <userinput>sh MAKEDEV snd1</userinput></screen>
-
- <para>Please note that either of the above commands will
- <emphasis>not</emphasis> create a
- <devicename>/dev/snd</devicename> device! Instead it creates a
- group of device nodes including:</para>
-
- <informaltable frame="none">
- <tgroup cols="2">
- <thead>
- <row>
- <entry>Device</entry>
- <entry>Description</entry>
- </row>
- </thead>
-
- <tbody>
- <row>
- <entry><devicename>/dev/audio</devicename></entry>
- <entry>SPARC-compatible audio device</entry>
- </row>
-
- <row>
- <entry><devicename>/dev/dsp</devicename></entry>
- <entry>Digitized voice device</entry>
- </row>
-
- <row>
- <entry><devicename>/dev/dspW</devicename></entry>
- <entry>Like <devicename>/dev/dsp</devicename>, but 16 bits
- per sample</entry>
- </row>
-
- <row>
- <entry><devicename>/dev/midi</devicename></entry>
- <entry>Raw midi access device</entry>
- </row>
-
- <row>
- <entry><devicename>/dev/mixer</devicename></entry>
- <entry>Control port mixer device</entry>
- </row>
-
- <row>
- <entry><devicename>/dev/music</devicename></entry>
- <entry>Level 2 sequencer interface</entry>
- </row>
-
- <row>
- <entry><devicename>/dev/sequencer</devicename></entry>
- <entry>Sequencer device</entry>
- </row>
-
- <row>
- <entry><devicename>/dev/pss</devicename></entry>
- <entry>Programmable device interface</entry>
- </row>
- </tbody>
- </tgroup>
- </informaltable>
-
- <para>If all goes well, you should now have a functioning sound
- card. If you do not, see the next section.</para>
- </sect1>
-
- <sect1>
- <title>Common Problems</title>
-
- <qandaset>
- <indexterm><primary>device node</primary></indexterm>
- <qandaentry>
- <question>
- <para>I get an unsupported subdevice XX error!</para>
- </question>
-
- <answer>
- <para>One or more of the device nodes was not created
- correctly. Repeat the steps above.</para>
- </answer>
- </qandaentry>
-
- <indexterm><primary>I/O port</primary></indexterm>
- <qandaentry>
- <question>
- <para>I get a sb_dspwr(XX) timed out error!</para>
- </question>
-
- <answer>
- <para>The I/O port is not set correctly.</para>
- </answer>
- </qandaentry>
-
- <indexterm><primary>IRQ</primary></indexterm>
- <qandaentry>
- <question>
- <para>I get a bad irq XX error!</para>
- </question>
-
- <answer>
- <para>The IRQ is set incorrectly. Make sure that the set IRQ
- and the sound IRQ are the same.</para>
- </answer>
- </qandaentry>
-
- <qandaentry>
- <question>
- <para>I get a <errorname>xxx: gus pcm not attached, out of
- memory</errorname>
- error. What causes that?</para>
- </question>
-
- <answer>
- <para>If this happens, it is because there is not enough
- available memory to use the device.</para>
- </answer>
- </qandaentry>
- </qandaset>
- </sect1>
-</chapter>