Subject: Converted pseudo device docs
To: None <netbsd-docs@netbsd.org>
From: None <dsieger@techfak.uni-bielefeld.de>
List: netbsd-docs
Date: 04/04/2007 01:18:51
--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi again.
Attached are a converted htdocs/Documentation/kernel/pseudo/index.list
and the corresponding patch (also adds a link to the page, which
doesn't seem to exist up until now).

Regards,
Daniel

-- 
Daniel Sieger
Faculty of Technology
Bielefeld University
wwwhomes.uni-bielefeld.de/dsieger

--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="pseudo.diff"

Index: layout.xml
===================================================================
RCS file: /cvsroot/htdocs/layout.xml,v
retrieving revision 1.229
diff -u -r1.229 layout.xml
--- layout.xml	3 Apr 2007 18:35:06 -0000	1.229
+++ layout.xml	3 Apr 2007 23:16:15 -0000
@@ -75,6 +75,7 @@
         <tocentry page="Documentation/kernel/kgdb.xml" filename="kgdb.html"/>
 	<tocentry page="Documentation/kernel/porting_netbsd_arm_soc.xml" filename="porting_netbsd_arm_soc.html"/>
 	<tocentry page="Documentation/kernel/profiling/index.xml" dir="profiling" filename="."/>
+	<tocentry page="Documentation/kernel/pseudo/index.xml" dir="pseudo" filename="."/>
 	<tocentry page="Documentation/kernel/programming.xml" filename="programming.html"/>
 	<tocentry page="Documentation/kernel/uvm.xml" filename="uvm.html" />
       </tocentry>
Index: Documentation/index.xml
===================================================================
RCS file: /cvsroot/htdocs/Documentation/index.xml,v
retrieving revision 1.29
diff -u -r1.29 index.xml
--- Documentation/index.xml	29 Mar 2007 05:22:44 -0000	1.29
+++ Documentation/index.xml	3 Apr 2007 23:16:16 -0000
@@ -473,6 +473,11 @@
           url="http://www.home.unix-ag.org/bmeurer/NetBSD/howto-lkm.html">
           Introduction to NetBSD loadable kernel modules</ulink>
         </listitem>
+	<listitem>
+	  <ulink
+	  url="kernel/pseudo/index.html">Writing a pseudo
+	  device</ulink> 
+	</listitem>
         <listitem>
           <ulink
           url="http://www.freebsd.org/doc/en_US.ISO8859-1/books/design-44bsd/index.html">
Index: Documentation/kernel/pseudo/Makefile
===================================================================
RCS file: /cvsroot/htdocs/Documentation/kernel/pseudo/Makefile,v
retrieving revision 1.5
diff -u -r1.5 Makefile
--- Documentation/kernel/pseudo/Makefile	14 Oct 2005 08:59:55 -0000	1.5
+++ Documentation/kernel/pseudo/Makefile	3 Apr 2007 23:16:16 -0000
@@ -1,5 +1,5 @@
 #	$NetBSD: Makefile,v 1.5 2005/10/14 08:59:55 rillig Exp $
 
-LISTDOCS+=	index.list
+XMLDOCS+=	index
 
 .include "../../../share/mk/web.site.mk"

--0OAP2g/MAC+5xKAE
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="index.xml"

<?xml version="1.0"?>
<!DOCTYPE webpage
  PUBLIC "-//NetBSD//DTD Website-based NetBSD Extension//EN"
        "http://www.NetBSD.org/XML/htdocs/lang/share/xml/website-netbsd.dtd">

<webpage id="Documentation-kernel-pseudo">
  <config param="desc" value="NetBSD Documentation: Writing a pseudo device"/>
  <config param="cvstag" value="$NetBSD: $"/>
  <config param="rcsdate" value="$Date: $"/>
  <head>

  <!-- Copyright (c) 1994-2007
        The NetBSD Foundation, Inc.  ALL RIGHTS RESERVED. -->

    <title>NetBSD Documentation: Writing a pseudo device</title>
  </head>

  <sect1 role="toc">
    <sect2 id="pseudo">
      <title>Writing a pseudo device</title>

      <sect3 id="intro">
        <title>Introduction</title>
	
	<para>This document is meant to provide a guide to someone who
	  wants to start writing kernel drivers.  The document covers
	  the writing of a simple pseudo-device driver.  You will need
	  to be familiar with building kernels, makefiles and the
	  other arcana involved in installing a new kernel as these
	  are not covered by this document.  Also not covered is
	  kernel programming itself - this is quite different to
	  programming at the user level in many ways.  Having said all
	  that, this document will give you the process that is
	  required to get your code into and recognised by the
	  kernel.</para>
      </sect3>

      <sect3 id="your_code">
        <title>Your code</title>

	<para>The file <ulink
	  url="./pseudo_dev_skel.c">pseudo_dev_skel.c</ulink> gives the
	  framework for a pseudo-device and the file <ulink
	  url="./pseudo_dev_skel.h">pseudo_dev_skel.h</ulink> defines
	  the kernel function prototypes and the ioctl data structure
	  plus the ioctl number itself.  Note that, unlike a normal
	  device driver, a pseudo-device does not have a probe routine
	  because this is not necessary.  This simplifies life because
	  we do not need to deal with the autoconfig framework.  The
	  skeleton file given is for a pseudo-device that supports the
	  open, close and ioctl calls.  This is about the minimum
	  useful set of calls you can have in a real pseudo-device.
	  There are other calls to support read, write, mmap and other
	  device functions but they all follow the same pattern as
	  open, close and ioctl so they have been omitted for
	  clarity.</para>

	<para>Probably the first important decision you need to make
	  is what you are going to call your new device.  This needs
	  to be done up front as there are a lot of convenience macros
	  that generate kernel structures by prepending your device
	  name to the function call names, will help if you have an
	  idea of the config file entry you want to have.  The config
	  file entry does not have to match the header file name.  In
	  our skeleton driver we have decided to call the
	  pseudo-device <quote>skeleton</quote>, so we shall have a
	  config file entry called skeleton.  This means that the
	  attach, open, close and ioctl function calls are named
	  skeletonattach, skeletonopen, skeletonclose and
	  skeletonioctl respectively.  Another important decision is
	  what sort of device you are writing - either a character or
	  block device as this will affect how your code interacts
	  with the kernel and, of course, your code itself.  The
	  decision of block vs character device depends a lot on the
	  underlying hardware the driver talks to, if the device the
	  driver talks to operates by reading and write data in fixed
	  chunks then a block device is a good choice, an example of
	  such a device is a hard disk which usually reads and writes
	  data in blocks of 512 byte sectors.  If the hardware reads
	  and writes data one byte at a time then a character device
	  is normally the best choice, an example of such a device is
	  a serial line driver.  Note that some drivers support both a
	  block mode and character mode of access to a device, in this
	  case the character mode is sometimes called the "raw" device
	  because it gives access to the hardware without the data
	  blocking abstractions operating on the access.  For a
	  pseudo-device the choice is more flexible because there is
	  no underlying hardware to consider.  The choice is driven by
	  the use the pseudo-device is going to be put to, a block
	  device may be useful if you are going to emulate a hard-disk
	  or similar.  Our skeleton driver is to be a character
	  device.</para>

	<para>Once the decisions have been made we can start cutting
	  code, before we do this though we need to decide where our
	  file should go.  If you are writing a pseudo-device that
	  will be used by multiple architectures then the appropriate
	  place to put the driver code is in
	  <filename>/usr/src/sys/dev</filename>.  If the pseudo-device
	  is specific to a particular architecture then put the driver
	  code under the architecture specific directory, for example
	  on the i386 architecture this would be
	  <filename>/usr/src/sys/arch/i386/i386</filename>.  The
	  include file should go into
	  <filename>/usr/src/sys/sys</filename> for the architecture
	  independent device and in the <filename>include</filename>
	  directory under the architecture specific directory for an
	  architecture specific device, for example, on the i386
	  architecture this would be
	  <filename>/usr/src/sys/arch/i386/include</filename>.  In
	  either case ensure that you update the relevant
	  <filename>Makefile</filename> so your include file is
	  installed.  One thing you will note is the <code>struct
	  skeleton_softc</code> at the top of <ulink
	  href="pseudo_dev_skel.c">pseudo_dev_skel.c</ulink>.  You
	  must have a softc structure declared with the name of your
	  device with "_softc" appended and the first element of this
	  struct needs to be a <code>struct device</code> type, the
	  name of the entry is not important but it must be first as
	  the autoconfig system relies on the softc struct being
	  declared <!-- ??? is this really true ??? --> and that its
	  first element is a struct device.  There needs to be a softc
	  struct for each minor number a device handles.  The softc
	  structure can hold more elements than just the struct device
	  if the minor devices require state information to be kept
	  about them.</para>

      </sect3>

      <sect3 id="functions">
        <title>The functions</title>

	<para>The kernel interfaces to your device via a set of
	  function calls which will be called when a user level
	  programme accesses your device.  A device need not support
	  all the calls, as we will see later, but at a minimum a
	  useful device needs to support an open and close on it.
	  Remember the function names need to be prepended with your
	  device name.  The functions are:</para>

	<orderedlist>
	  <listitem>
	    <para><function>attach()</function></para>
	    <para>This function is called once when the kernel is
	      initialising.  It is used to set up any variables that
	      are referenced in later calls or for allocating kernel
	      memory needed for buffers.  The attach function is
	      passed one parameter which is the number of devices this
	      driver is expected to handle.</para>
	  </listitem>
	  <listitem>
	    <para><function>open()</function></para>
	    <para>As the name suggests, this function will be called
	      when a user level programme performs an open(2) call on
	      the device.  At its simplest the open function may just
	      return success.  More commonly, the open call will
	      validate the request and may allocate buffers or
	      initialise other driver state to support calls to the
	      other driver functions.  The open call is passed the
	      following parameters:</para>
	    <itemizedlist>
	      <listitem>
	        <para><parameter>dev</parameter></para>
		<para>This is the device minor number the open is
		  being performed on.</para>
              </listitem>
	      <listitem>
	        <para><parameter>flags</parameter></para>
		<para>flags passed to the open call</para> <!-- ??? flags passed to the open call by user ??? -->
	      </listitem>
	      <listitem>
	        <para><parameter>mode</parameter></para>
		<para>mode for open</para> <!-- ??? mode for open ??? -->
	      </listitem>
	      <listitem>
	        <para><parameter>proc</parameter></para>
		<para>This is a pointer to the proc structure of the
                  process that has requested the open.  It allows for
                  validation of credentials of the process.</para>
	      </listitem>
	    </itemizedlist>
	  </listitem>
	  <listitem>
	    <para><function>close()</function></para>
	    <para>This closes an open device.  Depending on the driver
	      this may be as simple as just returning success or it
	      could involve free'ing previously allocated memory
	      and/or updating driver state variables to indicate the
	      device is no longer open.  The parameters for the close
	      function call are the same as those described for
	      open.</para>
	  </listitem>
	  <listitem>
	    <para><function>read()</function></para>
	    <para>Read data from your device.  The parameters for the
	      function are:</para>
	    <itemizedlist>
	      <listitem>
	        <para><parameter>dev</parameter></para>
		<para>The minor number of the device.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>uio</parameter></para>
		<para>This is a pointer to a uio struct.  The read
		  function will fill in the uio struct with the data
		  it wants to return to the user.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>flags</parameter></para>
		<para>flags</para> <!-- ??? wuffor ??? -->
	      </listitem>
	    </itemizedlist>
	  </listitem>
	  <listitem>
	    <para><function>write()</function></para>
	    <para>Write data to your device.  The parameters for the
	      write function are the same as those for a read function
	      - the only difference being that the uio structure
	      contains data to be written to the device.</para>
	  </listitem>
	  <listitem>
	    <para><function>ioctl()</function></para>
	    <para>Perform an ioctl on your device.  The parameters for
	      the ioctl call are:</para>
	    <itemizedlist>
	      <listitem>
	        <para><parameter>dev</parameter></para>
		<para>The minor number of the device.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>cmd</parameter></para>
		<para>The ioctl command to be performed.  The commands are
                  defined in a header file which both the kernel code
                  and the user level code reference.  See the
		  <ulink href="pseudo_dev_skel.c">sample
		  header</ulink> for an example.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>data</parameter></para>
		<para>This is a pointer to the parameters passed in by
                  the user level code.  What is in this parameter
                  depends on the implementation of the ioctl and also
                  on the actual ioctl command being issued.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>flags</parameter></para>
		<para>flags</para> <!-- ??? wuffor ??? -->
	      </listitem>
	      <listitem>
	        <para><parameter>proc</parameter></para>
		<para>The proc structure that is associated with the user
                  level process making the ioctl request.</para>
	      </listitem>
	    </itemizedlist>
	  </listitem>
	  <listitem>
	    <para><function>stop()</function></para>
	    <para>Stop output on tty style device.</para>
	    <itemizedlist>
	      <listitem>
	        <para><parameter>tty</parameter></para>
		<para>tty associated with the device</para>
	      </listitem>
	      <listitem>
	        <para><parameter>flags</parameter></para>
		<para>flags</para> <!-- ??? -->
	      </listitem>
	    </itemizedlist>
	  </listitem>
	  <listitem>
	    <para><function>poll()</function></para>
	    <para>Checks the device for data that can be read from it.
	      The parameters are:</para>
	    <itemizedlist>
	      <listitem>
	        <para><parameter>dev</parameter></para>
		<para>The minor number of the device used.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>events</parameter></para>
		<para>The event(s) that the user level call is polling
		  for.</para>
	      </listitem>
	      <listitem>
	        <para>proc</para>
		<para>The proc structure that is associated with the
		  user level process making the ioctl request.</para>
	      </listitem>
	    </itemizedlist>
	  </listitem>
	  <listitem>
	    <para><function>mmap()</function></para>
	    <para>Supports the capability of mmap'ing a driver buffer
	      into a user level programme's memory space.  The
	      parameters are:</para>
	    <itemizedlist>
	      <listitem>
	        <para><parameter>dev</parameter></para>
		<para>The minor device number of the device
		  used.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>offset</parameter></para>
		<para>The offset from the start of the buffer at which
		  to start the mmap.</para>
	      </listitem>
	      <listitem>
	        <para><parameter>prot</parameter></para>
		<para>The type of mmap to perform, either read only,
                  write only or read write.  The device driver need
                  not support all modes.</para>
	      </listitem>
	    </itemizedlist>
	  </listitem>
	</orderedlist>

	<para>The function names your device driver supports must be
	  inserted into a <code>struct cdevsw</code> for a character
	  device and/or a <code>struct bdevsw</code> that has the name
	  of your module appended with either <code>_cdevsw</code> or
	  <code>_bdevsw</code>.  For our sample pseudo-device this
	  structure would be called <code>skeleton_cdevsw</code> since
	  we decided that our pseudo-device would be a character
	  device only.  Note that these structures have entries in
	  them for all the device interface functions but your device
	  may only implement a subset of these functions.  Instead of
	  forcing everyone to implement stub functions for the unused
	  ones there are a set of pre-declared stubs prefixed with
	  either <code>no</code> (e.g. <code>noread</code>,
	  <code>nowrite</code> and so on) which will return
	  <code>ENODEV</code> when called or <code>null</code>
	  (e.g. <code>nullread</code>, <code>nullwrite</code> and so
	  on) which will return success, effectively providing a null
	  operation.  For the functions in the cdevsw and/or bdevsw
	  that your driver does not support simply use one of the
	  predeclared stubs.</para>

      </sect3>

      <sect3 id="newdevice">
        <title>Making the kernel aware of the new device</title>

	<para>Once you have done the coding of your pseudo-device it
	  is then time
	  to hook your code into the kernel so that it can be accessed.  Note
	  that the process of hooking a pseudo-device into the kernel differs
	  a lot from that of a normal device.  Since a pseudo-device is
	  either there or not the usual device probe and auto-configuration is
	  bypassed and entries made into kernel structures at the source
	  level instead of at run time.  To make the kernel use your code you
	  have to modify these files:</para>

        <orderedlist>
	  <listitem>
	    <para><filename>/usr/src/sys/conf/majors</filename> or
	      <filename>/usr/src/sys/&lt;arch&gt;/conf/majors.&lt;arch&gt;</filename></para>

	    <para>These files contain lists of device major numbers
	      for NetBSD. The file
	      <filename>/usr/src/sys/conf/majors</filename> contains
	      the major numbers for devices that are machine
	      independent, that is, available on all architectures
	      that NetBSD supports.  If the device is only relevant to
	      a particular architecture then the file
	      <filename>/usr/src/sys/&lt;arch&gt;/conf/majors.&lt;arch&gt;</filename>
	      must be used where &lt;arch&gt; is replaced with the
	      architecture in question.  These files contain entries
	      of the form</para>

	    <programlisting>
device-major    prefix		type      number	condition</programlisting>
	    <para>The exact syntax for these lines is described in the
	      &man.config.5; man page but for the purposes of our
	      example the  components of the line are:</para>

	    <itemizedlist>
	      <listitem>
	        <para>device-major</para>
		<para>A keyword indicating this is a device major
		  number entry.</para>
	      </listitem>
	      <listitem>
	        <para>prefix</para>
		<para>The prefix that will be applied to all the
		  driver functions when their names are automatically
		  generated.  In our example this would be
		  <code>skeleton</code>.</para>
	      </listitem>
	      <listitem>
	        <para>type</para>
		<para>The type of major device this is, it may be
		  either <code>char</code> or <code>block</code>.  You
		  may specify both a char and block device by
		  repeating the type/number pair for both.</para>
	      </listitem>
	      <listitem>
	        <para>number</para>
		<para>The major number for the device, choose the next
		  available number.  Make a note of this number as you
		  will need it to make the device node in
		  <filename>/dev</filename>.</para>
	      </listitem>
	      <listitem>
	        <para>condition</para>
		<para>The condition on which this device will be
		  included in the kernel.  This should match the
		  pseudo-device entry you put in the conf file
		  (described below).</para>
	      </listitem>
	    </itemizedlist>

	    <para>For our example skeleton pseudo device we want a
	      character device, and have decided that the driver is
	      machine specific to the i386 architecture.  After making
	      these decisions we can edit the
	      <filename>/usr/src/sys/arch/i386/conf/majors.i386</filename>
	      file, we find that major number 140 is available so we
	      add the line:</para>
	    <programlisting>
device-major	skeleton	char	140	skeleton</programlisting>
	  </listitem>
	</orderedlist>
      </sect3>

      <sect3 id="config">
        <title>Making &man.config.1; aware of the new device</title>

	<para>To make &man.config.1; aware of our new pseudo device we
	  need to edit the file in either
	  <filename>/usr/src/sys/conf/files</filename> (for
	  architecture independent devices) or
	  <filename>/usr/src/sys/arch/&lt;arch&gt;/conf/files.&lt;arch&gt;</filename>
	  where &lt;arch&gt; is the relevant architecture.  This file
	  tells config what valid device names there are and which
	  files are associated with these devices.  Firstly we look
	  for the section that defines the pseudo-devices.  This
	  section has lines that start with <code>defpseudo</code>.
	  Since we have decided that our driver is to be an
	  architecture specific one we edit the
	  <filename>/usr/src/sys/arch/i386/conf/files.i386</filename>
	  file and once we have found the correct section we can add a
	  line like this:</para>

	<programlisting>
defpseudo skeleton</programlisting>

        <para>Which tells &man.config.1; we have a pseudo-device
	  called skeleton.  Next we need to tell &man.config.1; which
	  files are associated with the skeleton pseudo-device.  In
	  this case we only have one file but a more complex
	  pseudo-device may have more files, simply add each file
	  required on a line in the same manner.  For our example we
	  only need one line that looks like this:</para>

	<programlisting>
file dev/skeleton.c	   skeleton	needs-flag</programlisting>

        <para>The <code>file</code> on the line is a key word to say
	  we are defining a device to file association.  The second
	  field is the location of the file relative to the root of
	  the kernel source tree (normally,
	  <filename>/usr/src/sys</filename>).  The third field is the
	  name of the driver that this file is associated with, in our
	  case this is skeleton - our sample pseudo-device.  The
	  fourth and last field is a control flag that tells
	  &man.config.1; to write the <filename>skeleton.h</filename>
	  include file. Note that here the file is called
	  <filename>skeleton.c</filename>, if we were using the
	  example files here, we would have to either rename
	  <filename>pseudo_dev_skel.c</filename> to
	  <filename>skeleton.c</filename> or change this entry.  Since
	  we said above that we are calling it skeleton, it would
	  probably be more suitable to call it
	  <filename>skeleton.c</filename>.</para>

	</sect3>

	<sect3 id="kernelconfig">
	  <title>Adding the new device to the kernel config
	    file</title>

	  <para>Once &man.config.1; has been told about the device,
	    adding it to the kernel config file is simple.  To add our
	    skeleton device we add the line:</para>

	  <programlisting>
pseudo-device  skeleton</programlisting>

          <para>To the kernel config file, note the name of the
	    pseudo-device matches the name given in the
	    <code>defpseudo</code> line in the previous section.  New
	    defines can be added to the kernel makefile by using the
	    <code>options</code> kernel config file keyword, config
	    will build a makefile with the options named added as
	    <option>-D</option> command line options to the
	    <command>cc</command> command.</para>
	    
	</sect3>

	<sect3 id="userlevel">
	  <title>Allowing user level programmes access to the new
	    device</title>

	  <para>After building and installing a new kernel there is
	    one last thing that needs to be done to access the new
	    pseudo-device, this is to make a device node for talking
	    to the new device.  The device node can be made on any
	    file system that allows you to access devices from it but,
	    by convention, device nodes are created in
	    <filename>/dev</filename>.  To make the device node you
	    will need to use &man.mknod.8; to create a device node
	    with the major number you noted in section 4.i.  In our
	    case the &man.mknod.8; command would look like
	    this:</para>

	  <screen>&rprompt; <userinput>mknod /dev/skel c 140 0</userinput></screen>

	  <para>Once this has been done you should be able to open
	    your new device and test it out.  The file <ulink
	    url="sample.c">sample.c</ulink> shows the skeleton pseudo
	    device in action.  This file assumes you have followed the
	    instructions here and have created
	    <filename>/dev/skel</filename>, this device is opened and
	    a parameter structure passed to the device driver via an
	    ioctl call.  To compile the sample code use this command
	    line:</para>
	    
	  <screen>&uprompt; <userinput>cc -o sample sample.c</userinput></screen>

	  <para>Which will produce a binary called sample.
	    <emphasis>NOTE:</emphasis> you will have to have run
	    <command>make includes</command> in the directory you
	    copied <filename>pseudo_dev_skel.h</filename> to install
	    the header file into the system includes directory
	    otherwise the compiler will complain about a missing
	    include file.  Once you have compiled the programme, run
	    it and then look at your kernel messages either on the
	    console screen or in
	    <filename>/var/log/messages</filename>, they should have a
	    message that looks like this:</para>

	  <programlisting>
May 17 20:32:57 siren /netbsd: Got number of 42 and string of Hello World</programlisting>

          <para>Which is a message printed by the skeleton ioctl
	    handler when it
	    receives a SKELTEST ioctl request; notice that the number and the
	    string printed are the ones we put into the <code>param</code>
	    structure in <filename>sample.c</filename>.</para>

	</sect3>
    </sect2>
  </sect1>

  <parentsec url="./" text="NetBSD Documentation: Kernel"/>
  
</webpage>
--0OAP2g/MAC+5xKAE--