< Previous PageNext Page > Hide TOC

Hello I/O Kit: Creating a Device Driver With Xcode

This tutorial describes how to write an I/O Kit device driver for Mac OS X. The driver you’ll create is a simple driver that prints text messages, but doesn’t actually control any hardware. The tutorial assumes that you are working in a Mac OS X development environment.

Important: This tutorial does not show you how to build a universal I/O Kit device driver. If you need to do this, you first should work through the tutorial to learn the basics of driver development on Mac OS X. Then, to find out how to configure your Xcode project to build a universal driver, see Technical Note TN2163: Building Universal I/O Kit Drivers.

For information on issues related to building a universal device driver, see Developing a Device Driver to Run on an Intel-Based Macintosh; for information on building universal binaries in general, see Universal Binary Programming Guidelines, Second Edition.

Contents:

Anatomy of a Device Driver
Roadmap
Create an I/O Kit Project using Xcode
Create an I/O Kit Extension Project
Edit the Driver’s Property List
Edit the Driver’s Build Settings
Implement the Header File
Implement the Driver’s Entry Points
Build the Project
Fix Any Errors
Test the Device Driver
Load the Driver
Using Console Mode
Where to Go Next


Anatomy of a Device Driver

An I/O Kit device driver is a special type of kernel extension (KEXT) that tells the kernel how to handle a particular device or family of devices. In Mac OS X, all kernel extensions are implemented as bundles, folders that the Finder treats as single files. Kernel extensions have names ending in .kext. In addition, all kernel extensions contain the following:

In addition, a device driver has several special requirements that other KEXTs do not. Specifically:

Roadmap

Here are the steps you’ll follow to create the HelloIOKit device driver:

  1. “Create an I/O Kit Project using Xcode”

  2. “Build the Project”

  3. “Test the Device Driver”

You’ll use the Xcode application to create and build your driver. You’ll use the Terminal application to type the commands to load and test your driver and view the results.

If you have not used Xcode much, you may wish to read Xcode Quick Tour for Mac OS X.

Create an I/O Kit Project using Xcode

This section describes how to create the project that will be used in writing your device driver. A project is a document that contains all of your files and targets. Targets are the things you can build from your project’s files. A simple project has just one target that builds an application. A complex project may contain several targets.

The parts of your project can be found later in your Desktop. These parts include the source files, as well as the targets (your KEXT). Xcode does not store these files in any special format, so you can view or edit the source files with another editing program if you wish. For now, however, we recommend using Xcode.

Here’s how you’ll create the device driver project:

  1. “Create an I/O Kit Extension Project”

  2. “Edit the Driver’s Build Settings”

  3. “Edit the Driver’s Property List”

  4. “Implement the Header File”

The examples below assume that you will be logging in as an administrator of your machine. The account name in the examples is admin. If you use a different login account, be sure to substitute accordingly. Some of the commands you will be using require root privileges so you will be using the sudo command. The sudo command allows permitted users (such as an admin user) to execute a given command with root privileges. An admin user is a user who has “Allow user to administer this computer” checked in the Security view of the Accounts system preference. If you use a different login account, make sure it has administrative access.

Create an I/O Kit Extension Project

Device drivers are created and edited in Xcode, Apple’s Integrated Development Environment (IDE).

From a Desktop Finder window, locate and launch the Xcode application, found at /Developer/Applications/Xcode. If this is the first time Xcode has been run, you’ll see the new user Assistant. The Assistant asks you to make some decisions about your environment. For now, choose the defaults.

When you have finished with the Assistant, choose New Project from the File menu. In the New Project Assistant, scroll down to the Kernel Extension section and choose IOKit Driver. Click Next.

For the Project Name, enter “HelloIOKit”. The default location is your home directory; however, if you create many projects, you should make a directory to hold them. Edit the Location to specify a “Projects” subdirectory, for example:

/Users/admin/Projects

When you click Finish, Xcode creates the new project and displays its project window, as shown in Figure 1. Notice that the new project contains several files already, including two source files — HelloIOKit.h and HelloIOKit.cpp.


Figure 1  The new HelloIOKit driver project in Xcode


Edit the Driver’s Property List

Your kernel extension contains a property list, or plist, which tells the operating system what your KEXT contains and what it needs. If viewed from a text editor, the property list would be in XML format. However, you will be viewing and editing the plist information from within Xcode, so you don’t need to worry about the underlying format.

Each element of a property list has a name, or key, a Class type (for example, String, Number, Dictionary, and so forth), and a value. A Dictionary is a collection of zero or more objects, each of which is associated with a name. Objects may be added, removed, or located in a Dictionary by their name.

By default, Xcode allows you to edit your driver’s property list as plain XML text in the Xcode editor window. However, it’s easier to view and edit the property list file with the Property List Editor application. You can tell Xcode to open property list files using Property List Editor by following these steps:

  1. Choose Xcode > Preferences and click the File Types icon. The File Types pane lists all the folder and file types that Xcode handles and the preferred editor for each type.

  2. Click the disclosure triangle for file. Then click the disclosure triangle for text.

  3. Select text.xml and click Default (Plain Text File) in the Preferred Editor column.

  4. Choose External Editor and select Other from the menu that appears.

  5. Select Property List Editor in /Developer/Applications/Utilities/Property List Editor and click OK.

  6. Now Xcode lists External Editor (Currently Property List Editor) in the Preferred Editor column for text.xml. Click OK to close the Xcode Preferences window.

Now you can edit your driver’s Info.plist file using Property List Editor:

  1. Select HelloIOKit in the Groups & Files view. Double-click Info.plist in the Xcode project window. Xcode starts Property List Editor, which displays the Info.plist file. (If Property List Editor displays a second editing window named Untitled, dismiss it by clicking the red close button in the upper left.)

  2. In Property List Editor, click the disclosure triangle next to Root. You should see the elements of the property list file, as shown in Figure 2.

    Figure 2  Info.plist entries

  3. Change the value of the CFBundleIdentifier property from the default chosen by Xcode.

    Apple has adopted a “reverse-DNS” naming convention to avoid namespace collisions. This is important because all kernel extensions share a single “flat” namespace.

    By default, Xcode names a new I/O Kit driver com.yourcompany.driver.<projname>, where <projname> is the name you chose for your project. You should replace the first two parts of this name with your actual reverse-DNS prefix (for example: com.apple, ecu.ucsd, and so forth). For this tutorial, you will use the prefix com.MyTutorial.

    On the line for CFBundleIdentifier, double-click on com.yourcompany.driver.HelloIOKit in the Value field. Double-click on yourcompany and change this string to MyTutorial.

  4. The CFBundleVersion property contains the version number of your driver in the ‘vers’ resource style (for more information on this, see http://developer.apple.com/documentation/mac/Toolbox/Toolbox-454.html and Technical Note TN1132).

    By default, Xcode assigns new kernel extensions the version number 1.0.0d1. You can change this value if you wish, but your driver must declare some version in the ‘vers’ resource style to be loaded successfully.

    For this tutorial, leave the CFBundleVersion property set to the default value 1.0.0d1.

  5. Darwin/Mac OS X requires every KEXT to declare its dependencies on other loadable extensions or in-kernel components in the OSBundleLibraries dictionary at the top level of its plist. Because every KEXT is linked against the kernel, every KEXT must at least declare its dependence on the kernel itself. However, it is often better to declare dependence on specific kernel subcomponents rather than on the kernel as a whole. For more information on dependencies and to find out how to determine the dependencies of other KEXTs you may write, see “Kernel Extension Dependencies.” For this tutorial, you will add three elements to the OSBundleLibraries dictionary to declare your driver’s dependence on the kernel subcomponents com.apple.kernel.iokit, com.apple.kernel.libkern, and com.apple.kernel.mach.

    Click on the line for OSBundleLibraries and click the disclosure triangle at the beginning of the line. The button previously labeled New Sibling should change to New Child. If not, check to be sure the disclosure triangle next to OSBundleLibraries is pointing down.

    Click the button that now says New Child (as soon as you do, it will change back to New Sibling). A new item will appear below OSBundleLibraries. Change the name from New item to com.apple.kernel.iokit. Leave the Class set to String and double-click on the Value field and enter 6.9.9.

    Clicking the New Sibling button each time, add two more elements to the OSBundleLibraries dictionary.

     
    com.apple.kernel.libkern String 6.9.9
    com.apple.kernel.mach String 6.9.9
  6. Note that IOKitPersonalities is a Dictionary. Specifically, IOKitPersonalities is a representation of an OSDictionary, serialized into XML property list format. This Dictionary defines personalities for your driver; each personality contains properties for matching and loading a driver. You will implement one personality, named HelloIOKit, within the IOKitPersonalities Dictionary. The HelloIOKit personality is also a Dictionary, containing additional elements with various types.

    Click on the line for IOKitPersonalities and click the disclosure triangle at the beginning of the line. The button previously labeled New Sibling should change to say New Child. If not, check to be sure the disclosure triangle next to IOKitPersonalities is pointing down.

    Click the button that now says New Child (as soon as you do, it will change back to New Sibling). A new item will appear below IOKitPersonalities. Change the name from New item to HelloIOKit. Click on the Class field for this new item (it currently says String). In the popup menu, select Dictionary.

    With the line for HelloIOKit selected, click the disclosure triangle at the beginning of the line. The button previously labeled New Sibling changes to say New Child.

    Click the New Child Button (as soon as you do, it will change back to New Sibling). A new item will appear below HelloIOKit. Change the name from New item to CFBundleIdentifier (you can copy and paste the name from above in the property list). For the value, type (or copy and paste) com.MyTutorial.driver.HelloIOKit.

    Click the button that now says New Sibling. A new item will appear within the HelloIOKit dictionary. Change the name from New item to IOClass. Edit the value to be com_MyTutorial_driver_HelloIOKit. Note that this is the same name used before as the CFBundleIdentifier, except that you must use underbars, “_”, rather than dots, “.”, in the class name. Again, Apple recommends this “reverse-DNS” convention for naming your class, to ensure consistency and reduce name collisions among drivers.

    Important:  Be sure that the value field of IOClass contains underbars, “_”, rather than dots, “.”, as used in CFBundleIdentifier. This string will be used to create the class for your device driver.

    Now you will create the property list elements that define a successful match for your driver, so that it can be loaded. You’ll also add the IOKitDebug property to help in debugging driver matching.

    Note: If you include the IOKitDebug property with a nonzero value in your driver’s property list, it will provide information about your driver’s matching and loading. When you write a functional driver following the guidelines in this tutorial, however, you should give the IOKitDebug property a zero value because a nonzero value will prevent your driver from loading into the kernel during a safe-boot. For more information on safe booting, see Loading Kernel Extensions at Boot Time.

    Important:  IOMatchCategory is a special property list element that allows multiple drivers to match on a single nub. The presence of IOMatchCategory allows HelloIOKit to match on IOResources (a special nub at the root of the I/O Registry that provides system-wide resources) without preventing other drivers from matching on it. When you write a functional driver following the guidelines in this tutorial, you should not include the IOMatchCategory element in your driver’s property list at all unless there is a valid reason for your driver to match on the same device nub at the same time as another driver (for example, a serial port with multiple devices attached to it).

    The vast majority of drivers should not match on IOResources. A rare exception to this is a driver for a virtual device, because a virtual device does not produce its own nub in the I/O Registry. If you’re developing such a driver, you must include the IOMatchCategory property to make sure your driver doesn’t claim IOResources and prevent other drivers from matching on it. To ensure your driver’s IOMatchCategory value is unique, use your driver’s class name, in reverse-DNS notation and with underbars instead of dots (for example, com_MyTutorial_driver_HelloIOKit).

    Clicking the button that says New Sibling each time, add the following property list elements. Be sure to change the class of the first, IOKitDebug, from a string to a number using the popup menu. Be sure to enter the names and values exactly as shown below.

    Table 1  HelloIOKit personality dictionary values

    Name

    Class

    Value

    IOKitDebug

    Number

    65535

    IOMatchCategory

    String

    com_MyTutorial_driver_HelloIOKit

    IOProviderClass

    String

    IOResources

    IOResourceMatch

    String

    IOKit

    Important:  If you insert these elements in a different order, Property List Editor immediately rearranges the list to be alphabetical. As you add or edit each element, pay careful attention to be sure you are editing the line you think!

    When you have finished adding property list elements, the screen should look like the example shown in Figure 3.

    Figure 3  Info.plist entries after additions

  7. Click File > Save in the Property List Editor menu to save your changes. Then choose Property List Editor > Quit Property List Editor to return to your project in Xcode.

Edit the Driver’s Build Settings

Xcode defines several settings that contain information about your KEXT that get compiled into its executable. You will be editing a few of these settings to make sure they correspond to elements in the driver’s plist.

  1. On the left, in the Groups & Files view, click the disclosure triangle next to Targets and select HelloIOKit (you don’t have to click the disclosure triangle next to HelloIOKit).

  2. Click the Get Info button in the tool bar (it’s the round blue button with an “i” in the middle).

  3. Select the Build view and be sure Customized Settings is selected in the Collection pop-up menu. Scroll down to the bottom of the Customized Settings list and change the name of the module from the default chosen by Xcode (the value of the MODULE_NAME setting).

    Click on the line for MODULE_NAME and double-click on com.yourcompany.driver.HelloIOKit in the Value field. Double-click on yourcompany and change this string to MyTutorial. Be sure that the MODULE_NAME value matches the value of the CFBundleIdentifier you entered in your driver’s Info.plist file or your driver will not run.

  4. Note that the MODULE_START and MODULE_STOP settings are not listed. The I/O Kit provides the initialization and termination routines for drivers; you need not (and should not) specify these methods in your source code or in the driver’s build settings.

  5. The value of the MODULE_VERSION setting is in the ‘vers’ resource style and must be numerically identical to the CFBundleVersion value in the driver’s plist or your driver may not load.

    For this tutorial, make sure MODULE_VERSION has the value 1.0.0d1.

    Important:  You must make sure that the MODULE_VERSION value in the Customized Settings panel is numerically equal to the CFBundleVersion string in the Info.plist file or your driver may not load. You must also make sure the MODULE_NAME value in the Customized Settings panel matches the CFBundleIdentifier string in the Info.plist file or your driver will not run.

Before going on to the next section, you might want to go back to the Property List Editor view of the Info.plist file and copy the string com_MyTutorial_driver_HelloIOKit; you’ll need it for the next few steps. To do this, click the disclosure triangle next to HelloIOKit in the Groups & Files view, click the disclosure triangle next to Resources, and double-click Info.plist. View the Info.plist file by clicking the disclosure triangle next to Root. Select com_MyTutorial_driver_HelloIOKit in the Value field for the IOClass member of the HelloIOKit personality and choose Copy from the Edit menu. Then choose Property List Editor > Quit Property List Editor to return to Xcode.

Implement the Header File

With the disclosure triangle next to HelloIOKit pointing down, click on the disclosure triangle next to Source. Double-click on HelloIOKit.h to display the header file. The default header file does nothing. You need to add code before it does anything useful. Figure 4 shows where you will find the HelloIOKit.h file in the project window.


Figure 4  Viewing source code in Xcode


Edit the contents of HelloIOKit.h to match the code in Listing 1, pasting the string you copied from the Info.plist file (com_MyTutorial_driver_HelloIOKit) where shown.

Notice that the first line of HelloIOKit.h includes the header file IOService.h. This header file defines many of the methods and services on which device drivers depend. The header file is located in the IOKit folder of Kernel.framework. When you develop your own driver, be sure only to include header files from Kernel.framework (in addition to header files you create) because only these files have meaning in the kernel environment. If you include other header files, your driver might compile but the functions and services defined in those header files would not be available in the kernel.

Pay special attention to the lines that contain the string com_MyTutorial_driver_HelloIOKit. If this were not a tutorial, the actual name of your driver’s class (as copied and pasted) would go here.

Important:  Be very sure that this string is exactly as entered for the value of IOClass in the Info.plist file. Other than changing dots to underbars, this name should also be identical to the CFBundleIdentifier defined in the Info.plist file and to the MODULE_NAME in the Customized Settings panel. If these strings do not match, your driver will not load and run properly.

Aside from the importance of the class name, the OSDeclareDefaultStructors macro used in this file is very important. If you don’t use this macro correctly, or in the proper place, your driver won’t run.

In the header file, the OSDeclareDefaultStructors macro must be the first line in the class’s declaration. It takes one argument: the class’s name. It declares the class’s constructors and destructors for you, in the manner the I/O Kit expects.

Edit HelloIOKit.h to match the code below:

Listing 1  HelloIOKit.h

 
#include <IOKit/IOService.h>
class com_MyTutorial_driver_HelloIOKit : public IOService
{
OSDeclareDefaultStructors(com_MyTutorial_driver_HelloIOKit)
public:
    virtual bool init(OSDictionary *dictionary = 0);
    virtual void free(void);
    virtual IOService *probe(IOService *provider, SInt32 *score);
    virtual bool start(IOService *provider);
    virtual void stop(IOService *provider);
};
 

When you have finished editing HelloIOKit.h, copy the class name string (com_MyTutorial_driver_HelloIOKit) again; you will need it in the next step. Then double-click on HelloIOKit.cpp in the Groups & Files view of the Xcode project window. Again, the default file does nothing. You will need to add code before it can do anything useful.

Implement the Driver’s Entry Points

The following list describes some of the entry points that the I/O Kit uses. You will need to implement these entry points in your driver’s source file.

Most of the methods come in pairs: one performs some action and creates some data structures, the other undoes that action and releases those data structures. Generally a module must define only the probe, start and stop methods, using its superclass’s definitions for the rest.

Important:  Be very sure that the class name is exactly the same string you used in HelloIOKit.h and entered for the value of the IOClass member of the HelloIOKit personality. Other than changing dots to underbars, this name should also be identical to the CFBundleIdentifier defined in the Info.plist file and to the MODULE_NAME in the Customized Settings panel. If these strings do not match, your driver will not load and run properly.

Aside from the importance of the class name, the OSDefineMetaClassAndStructors macro used in this file is very important. If you don’t use this macro correctly, or in the proper place, your driver won’t run.

In the C++ source file, the OSDefineMetaClassAndStructors macro must appear before you define any of your class’s methods. This macro takes two arguments: your class’s name and the name of your class’s superclass. The macro defines the class’s constructors, destructors, and several other methods I/O Kit requires.

Notice that HelloIOKit.cpp includes only Kernel.framework header files, in addition to HelloIOKit.h. Aside from header files you define, you should include only header files from Kernel.framework in your driver.

Edit HelloIOKit.cpp to match the code below:

Listing 2  HelloIOKit.cpp

 
#include <IOKit/IOLib.h>
#include "HelloIOKit.h"
extern "C" {
#include <pexpert/pexpert.h>//This is for debugging purposes ONLY
}
 
// Define my superclass
#define super IOService
 
// REQUIRED! This macro defines the class's constructors, destructors,
// and several other methods I/O Kit requires. Do NOT use super as the
// second parameter. You must use the literal name of the superclass.
OSDefineMetaClassAndStructors(com_MyTutorial_driver_HelloIOKit, IOService)
 
bool com_MyTutorial_driver_HelloIOKit::init(OSDictionary *dict)
{
    bool res = super::init(dict);
    IOLog("Initializing\n");
    return res;
}
 
void com_MyTutorial_driver_HelloIOKit::free(void)
{
    IOLog("Freeing\n");
    super::free();
}
 
IOService *com_MyTutorial_driver_HelloIOKit::probe(IOService *provider, SInt32
*score)
{
    IOService *res = super::probe(provider, score);
    IOLog("Probing\n");
    return res;
}
 
bool com_MyTutorial_driver_HelloIOKit::start(IOService *provider)
{
    bool res = super::start(provider);
    IOLog("Starting\n");
    return res;
}
 
void com_MyTutorial_driver_HelloIOKit::stop(IOService *provider)
{
    IOLog("Stopping\n");
    super::stop(provider);
}
 

The IOLog method is similar to printf, but runs faster and flushes its output more frequently.

Two additional methods are implemented by I/O Kit and rarely, if ever, should be overridden by your driver code. attach is called by your driver’s provider after a successful match; it causes your driver to be added to the I/O Registry. detach is called after an unsuccessful probe, or when a driver is removed from the I/O Registry.

Save your changes by choosing File > Save in the Xcode menu.

Build the Project

Click the Build button (it looks like a hammer) in the upper left corner of the Xcode editor window or select Build from the Build menu.

If you didn’t save earlier, Xcode asks you whether to save some modified files. If this is the case, select all the files and click “Save All”. Figure 5 shows the Save dialog.


Figure 5  Save before building


Xcode starts building your project. It stops if it reaches an error in the code.

Fix Any Errors

If you made any errors typing in the code, Xcode will stop and show you the errors, in both the editor window (if you haven’t closed it) and the main project window. In the project window, click the disclosure triangle next to Errors and Warnings in the Groups & Files view to reveal the files that contain errors. If you select a file listed under Errors and Warnings, Xcode lists the errors in that file, each accompanied by a short description of the problem. Double-click the file name to open an editor window and edit the source code. Correct any errors and build the project again.

Test the Device Driver

This section shows how to test the driver. You’ll load your driver using the kextload command, you’ll use the kextstat command to see that the driver has been loaded, and finally, you’ll unload your driver from the kernel using the kextunload command.

You’ll use the Terminal application to type the commands to load and unload your module. You’ll view the results as they are written to the system log file, /var/log/system.log.

Note: This tutorial uses the “%” prompt when it shows the commands you type in the Terminal application. This is the default prompt of the tcsh shell. If you’re using a different shell, you may see a different prompt. The prompt in the bash shell, which is the default shell in Mac OS X version 10.3, is “$”.

Note that you use kextload and kextunload only when testing a driver. When a KEXT is fully installed under Darwin/Mac OS X, the Kernel Extension Manager takes care of loading and running (and unloading) drivers.

The following sections show you how to test your driver.

Getting Root Privileges

To use the kextload and kextunload commands you must have root or super user privileges. Instead of logging in as root, for this tutorial you will use the sudo command which gives permitted users the ability to execute a given command as root.

By default, Darwin/Mac OS X allows admin and root users to use the sudo command, so make sure you are currently logged in to an admin account before using sudo. Recall that an admin user is a user who has “Allow user to administer this computer” checked in the Security view of the Accounts system preference.

Start the Terminal Application

  1. Start the Terminal application. From a Desktop Finder window, locate and launch the Terminal application, found at /Applications/Utilities/Terminal.

  2. To view the system log file, enter the following command at the Terminal prompt:

    % tail -f /var/log/system.log
  3. Open a second window in the Terminal application. Choose File > New Shell from the Terminal menu. Position this window so that you can view both windows easily. You will load your driver from the second window.

  4. In the second Terminal window, move to the directory that contains your driver. Xcode stores your driver in the Debug folder of the build directory of your project location (unless you’ve set a different location for build products using Xcode’s Preferences dialog).

    Use the cd command to move to the appropriate directory. For example:

    % cd Projects/HelloIOKit/build/Debug

    This directory contains your KEXT. You can use the ls command to view the contents of this directory. For example:

    % ls
    HelloIOKit.kext

    Your KEXT should have the name HelloIOKit.kext. Note that this name is formed from the Project name and a suffix, .kext.

    Important:  For purposes of packaging, distribution, and installation, the filename of the KEXT (apart from the suffix) does not matter. However, the name of the KEXT binary and the KEXT’s class (stored in the KEXT’s property list) should be unique, using the recommended “reverse-DNS” naming convention.

    From a Desktop Finder window, a KEXT appears as a single file (look for it from the Desktop if you like). From the Terminal application, however, a KEXT appears as a directory. The KEXT directory contains the contents of your KEXT, including the plist (Contents/Info.plist) and the KEXT binary (Contents/MacOS/HelloIOKit).

  5. View the contents of the KEXT directory. Use the find command.

    For example:

     
    % find HelloIOKit.kext
    HelloIOKit.kext
    HelloIOKit.kext/Contents
    HelloIOKit.kext/Contents/Info.plist
    HelloIOKit.kext/Contents/MacOS
    HelloIOKit.kext/Contents/MacOS/HelloIOKit
    HelloIOKit.kext/Contents/Resources
    HelloIOKit.kext/Contents/Resources/English.lproj
    HelloIOKit.kext/Contents/Resources/English.lproj/InfoPlist.strings
    HelloIOKit.kext/Contents/Resources/Info.plist

You are now ready to load and run (and then unload) your driver.

Load the Driver

Because kernel extensions contain code and data that are loaded into the kernel, the most protected environment in the operating system, their file ownership and permissions must be set to prevent unauthorized tampering. All KEXT bundles (all files and folders in the KEXT, including the KEXT binary) must be owned by the user root and the group wheel. In addition, the folders and files of the KEXT bundle must have their permissions set so that they are not writable by any user other than the super user.

For development purposes, however, you can make a root-owned copy of your KEXT binary to load and test. You should not change the ownership and permissions of any of your KEXT files in your own directory, because you will no longer be able to save them after working on them. For this tutorial, you will use the sudo command to copy the KEXT binary (HelloIOKit.kext) to the /tmp directory and load and unload the KEXT from there. Using sudo to copy the KEXT binary gives the KEXT the super user’s ownership and permissions and leaves the original KEXT alone so you can revise and save it as you choose.

Note: Every time you make changes to your KEXT and rebuild it, you need to repeat the following steps to copy the new version to the /tmp directory to load and test it.

  1. At the prompt, you’ll use the cp -R command with the sudo command to copy your driver to /tmp. The cp command copies files from one place to another and the -R option tells cp to copy a directory and its entire subtree (like HelloIOKit.kext). When prompted for a password, enter your admin password. (Note that nothing is displayed as you type the password.)

    !

    Warning:  If you use tab-completion to avoid typing all of HelloIOKit.kext, you’ll get a “/” after the KEXT name. Be sure to delete this slash before you press Return. If you don’t, the cp command will copy only the contents of the HelloIOKit.kext directory to /tmp, instead of copying the directory and its entire subtree.

    Type the following to copy your driver to /tmp:

     
    % sudo cp -R HelloIOKit.kext /tmp
    Password:
     

    Check the ownership and permissions of your driver by moving to the /tmp directory and using the ls -l command:

     
        % cd /tmp
        % ls -l
        drwxr-xr-x 3 root wheel 102 Jun 19 10:30 HelloIOKit.kext
     

    The -l makes the ls command display extra information about the files in a directory.

  2. From the /tmp directory, use the kextload command with the sudo command; this loads your driver, runs its initialization (start) method, and registers it with the kernel. Note that you may not have to re-enter your password this time. This is because you’re allowed to continue to use sudo for a short period (usually about 5 minutes) without reauthenticating.

    For example:

     
    % sudo kextload -v HelloIOKit.kext
    kextload: extension HelloIOKit.kext appears to be valid
    kextload: notice: extension HelloIOKit.kext has debug properties set
    kextload: loading extension HelloIOKit.kext
    kextload: HelloIOKit.kext loaded successfully
    kextload: loading personalities named:
    kextload:   HelloIOKit
    kextload: sending 1 personality to the kernel
    kextload: matching started for HelloIOKit.kext
     

    The -v is optional; it makes kextload provide more verbose information.

  3. In the other Terminal window, view the system log. In a few moments, several lines will appear, for example:

     
    Matching service count = 1
    Initializing
    com_MyTutorial_driver_HelloIOKit::probe(IOResources)
    Probing
    com_MyTutorial_driver_HelloIOKit::start(IOResources) <1>
    Starting
     
  4. Use the kextstat command to check the status of the driver. This command displays the status of all dynamically-loaded kernel modules. Enter the command:

    % kextstat

    You’ll see several lines of output, including a line for your driver (at the end).

     
    Id  Refs AddressSizeWiredName (Version) <Linked Against>
     1  0   0x4b940000x170000x16000ATIR128 (0.1)
     2  2   0x4bbb0000x100000xf000com.apple.IOAudioFamily (0.1a)
    10  0   0x4d180000x70000x6000SIP-NKE (0.1a)
    11  0   0x50c0000 0x40000x3000 com.MyTutorial.driver.HelloIOKit (1.0.0d1) <10>
    ...
  5. Unload the driver. Use the kextunload command with the sudo command. This unloads your driver by running its termination (stop) function.

    For example, from the /tmp directory:

     
    % sudo kextunload HelloIOKit.kext
    unload kext HelloIOKit.kext succeeded
     

    Note that you may have to enter your admin password this time if it’s been more than a few minutes since the last time you entered your password.

  6. View the system log. In a few moments, several lines will appear, for example:

     
    Stopping
    Freeing
     
  7. Stop the system log display. The tail -f command you used to view the system log will continue to run until you stop it.

    In the Terminal window displaying the system log, press the control key and the c key at the same time.

Using Console Mode

As an alternative to the Terminal application, you can load and test your KEXT from console mode. In console mode, all system messages (such as this kernel extension’s message “Probing”) are written directly to your monitor screen. Messages appear much more quickly than when they are written to the system log file.

However, you should keep in mind that console mode is not as flexible as the Terminal application. There are no windows, you cannot view your code in Xcode or run other applications at the same time, and you cannot use copy or paste.

To use console mode, follow these steps:

  1. Log out of your account.

    From the Desktop, choose Log Out from the Finder menu.

  2. From the login screen, log in to console mode.

    Type >console as the user name, leave the password blank, and press Return. Be sure to include the > character at the beginning of the name. The screen turns black and looks like an old ASCII “glass terminal”. This is console mode.

  3. At the prompt, log in to your admin account.

  4. Move to the directory that contains your KEXT. Use the cd command.

    For example:

    cd /Users/admin/Projects/HelloIOKit/build
  5. Follow the instructions given in “Load the Driver.”

  6. Remember that the console messages will come directly to your screen; you do not need to view the system log file.

  7. When you have finished, log out of console mode by entering the command logout.

Where to Go Next

Congratulations! You’ve now written, built, loaded, and unloaded a device driver. In the next tutorial in this series, “Hello Debugger: Debugging a Device Driver With GDB,” you’ll learn how to debug your driver using a two-machine debugging environment.

If you’re interested, you can use the man command to read the manual pages for kextload, kextstat, and kextunload. For example, from a Terminal window, enter the command

man kextstat

More information about the ‘vers’ resource can be found in the “Finder Interface” chapter of Inside Macintosh—Files, or online at http://developer.apple.com/documentation/mac/Toolbox/Toolbox-454.html.

Additional useful reference material can be found in Core Foundation Documentation. Look here for documentation about Bundles, Property Lists, Collections (such as Dictionaries) and more.

As you become more experienced in driver development, you should also read the articles in this document that cover more advanced topics, such as how to declare dependencies so that your driver targets the appropriate version of Mac OS X. The following articles provide more information on these topics:

In addition, you might want to read Technical Note TN2163, “Building Universal I/O Kit Drivers” to learn the steps you need to take to configure an Xcode I/O Kit driver project to produce a universal driver.



< Previous PageNext Page > Hide TOC


© 2003, 2007 Apple Inc. All Rights Reserved. (Last updated: 2007-10-31)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.