How to use the iowarrior kernel module
--------------------------------------

In this document I will try to provide some basic knowledge about how to
access IOWarriors on Linux. If your are looking for information about the
IowKit-Library you're in the wrong place. I will only talk about low-level IO
with the IOWarrior.

By using the term low level I mean :

1) Opening the device
2) Getting information about the device
3) Reading
4) Writing
5) Closing the device

1) Opening the device
---------------------
Before I can show you how to open the device we have to look at the
Hardware. On the USB-level the IOWarrior provides 2 interfaces. The 
consequence for Linux is that a single IOWarrior actually shows up as 2
independent devices. I assume you already read the data-sheet for the
chip which talks about the IO-Pin and SpecialMode functions. 
These are exactly the 2 devices that get connected to your Linux
box when you plug the iowarrior in.
So, in order to use all the features the IOWarrior has to offer you need to
open both devices (interfaces). Even if you don't need any of the SpecialMode
functions I would suggest to open both of them, because another application
might open the SpecialMode-device behind your back and enable for instance
the LCD-mode. Here you will loose control of over some of the IO-Pins without
even noticing it.

Another thing that has always be kept in mind, is that only ONE process can open
and use an IOWarrior at a time. So once an application has claimed the
device through an open-call it has exclusive access to that IOWarrior
until it releases the device with a close()-call. 

This behavior is different from Windows where the IOWarrior is shared among
applications which can all write to the device at the same time. (If you intend
to do something crucial with the IOWarrior this obviously not what you want.)

Sorry, we're still not ready to open the IOWarrior, because I haven't told you
where the IOWarrior will show up on the filesystem. 
If you read the INSTALL-file you already know that best solution is to let
udev do the dirty work of creating the device-files to which the IOWarriors 
get connected.
So I assume you ran 'make udev' from the Makefile which informed udev what to
do when an IOWarrior gets plugged in. With the default settings (defined in
the '10-iowarrior.rules' file) all IOWarrior devices will be created in the
directory '/dev/usb' and the name of the device-files will start with
'iowarrior' to which a number between 0..15 is appended. 
If no IOWarrior is plugged in, the directory '/dev/usb' will show no traces 
of an iowarriorXX file. The directory probably might not even exist. 
But as soon as you plug an IOWarrior the directory will be populated with
2 new files, one for the IO-Pin functions and one for the SpecialMode
functions of the IOWarrior. Both files will have read and write access
granted to all users (otherwise a user-space application would not be able to
use them).

Now, which one is the file for the SpecialModes an which one the file for the
IO-Pins? The definite answer is : You can't tell!

People who already have some experience with the IOWarrior on Linux might say:
    A filename that ends with an even number (including 0) will 
    be the IO-Pin device, an odd number is therefore the 
    SpecialModes-device!

Please spare me the details of explaining how major and minor numbers for
device-files are handled inside the Linux kernel, but the above assumption is
false! It probably works for 99% of all users, but you're the one that gets
blamed when your application chokes on a machine that uses a different
kernel configuration. (There is an easy solution to this but we will have to
put that question on the wait_queue until we reach chapter 2)

But now let's get that device open:

int fd=-1; 

if(( fd = open( "/dev/usb/iowarrior0", O_RDWR)) < 0 ) {
     printf( "iowarrior open failed %d\n",errno );
     exit( 1 );
}	   

As you see it's a plain open() call. The only interesting part is the value of
errno when open() fails. If the file is not where its supposed to be, errno
will be set to EBADF as usual. But the kernel module for the iowarrior will
also report three other possible reasons why the open() failed:

errno=EBUSY
   As I mentioned before, an IOWarrior can only be accessed by one process at
   a time. This error tells you that some other application is already using
   the IOWarrior. 
errno=ENODEV
   Let take this as : The device should be there, but it isn't!
   Something prevented the kernel-module from setting up the device for
   you. This sounds rather vague ... but I never actually saw that error
   returned. 
errno=EFAULT
   There was a problem setting up the device before use. Reason could be that
   something is already wrong with the USB-core on you machine. But there is
   definitely nothing you can do about it in your application.

But lets be positive, let pretend open() returned a file-descriptor for us and
move on to Chapter 2...

2) Getting information about the device
---------------------------------------
The device open, now lets see what it has to offer us. There are a few things
you want to know about the IOWarrior before you start using it. If there is
only a single IOWarrior attached to your box, I guess you already know
everything  about it. But once they start to pile up we need to know a little
bit more about each of the devices.

The module implements an ioctl()-call that tells us more about the device we
just opened. The ioctl() needs a few definitions from the header of the
iowarrior module, so your application must include the file "iowarrior.h". 

The actual device information is provided in an instance of type :

struct iowarrior_info {
    int vendor;		
    int product;	
    char serial[9];	
    int revision;	
    int speed;		
    int power;		
    int if_num;		
    unsigned int packet_size;	
};

Let me explain what each member of the struct is good for:

vendor
   Not very interesting, it's the CodeMercs Vendor Id, the company that
   produces the IOWarrior. The kernel needs this value to decide which driver
   is responsible for a device. It's there for completeness.

product
   This is important! It tells you which type of IOWarrior is connected
   here. Check this value against the constants
   USB_DEVICE_ID_CODEMERS_IOWxx
   defined in 'iowarrior.h' to find out.

serial
   Every single IOWarrior-Product has a unique serial number which is returned
   as string with 8 Hex digits, terminated by '\0'. When it gets
   printed it shows up as something like "00000c64". I used the term
   "IOWarrior-Product" to emphasize that there will be for instance no two
   IOWarrior24 that have the same serial, but there sure is an IOWarrior40
   and an IOWarrior24 that have the same serial. You really want to save that
   value for later usage, because this is the way to match up the IO-Pin
   functions device and the Special-Mode functions device for an IOWarrior. 
   If you find the same product and the same serial its the same silicon!

   I lied! 
   There are some IOWarrior40 that actually have NO serial number. The
   driver will consequently return the empty string for them. 
   These were the first devices produced and if you don't already have one, 
   there is no chance you'll buy one by accident. 

revision
   Another item to remember. This tells you the revision number of the
   IOWarrior. The number is incremented on bug-fix-releases or when new
   features are added to the SpecialModes. The data-sheet lists all revision
   changes, so be prepared for missing features on older devices.

speed
   Tells you the speed on the USB-bus on which the device operates. This will
   always be the same along a product. The value is set to one of the constants
   0 - when there was a problem detecting the speed 
   1 - USB 1.1 low-speed (IOWarrior24 and IOWarrior40)
   2 - USB 1.1 high-speed (IOWarrior56)
   3 - USB 2.0 full-speed (no IOWarrior supports this yet)
	  
power
   Gives you the power consumption the device demands in milli amps. Since the
   IOWarriors can be configured to use either 100 or 500 mA, these are the
   values you wil get. 

if_num
   Here we are! This is the value that tells you whether you are looking at the
   IO-Pin device or the SpecialMode device:
   If it's  0  it's the IO-PINs
   IF it's  1  it's the SpecialModes
   End of story, open() problem solved!

packet_size
   This is the number of bytes to be written to or read from the device.
   It is a fixed value based on :
   whether it's the IO-PIN or SpecialMode functions you're looking at
   and the specific product-id of the IOWarrior
   More on this in the next chapters.

Here's how to get all this info from a device:

struct iowarrior_info info;

if( (ioctl( fd, IOW_GETINFO, &info) == -1 ) {
    printf( "Unable to retrieve device info %d\n",errno );
    goto exit;
}

The first argument 'fd' is the file-descriptor from the open() call.
The second 'IOW_GETINFO' is a constant defined in 'iowarrior.h'
And the third is a pointer to a struct iowarrior_info that is to be updated with
the device information. 
In the code we use a struct created on the stack. The ioctl expects a
pointer as the third argument, so we use the address-operator for the info.

If the ioctl() returns 0, the values in info were updated from the device.
Otherwise errno is set to one of the values

errno=ENODEV
   The device was unplugged! If your open call succeeded, someone pulled
   the plug in the meantime. 
errno=EFAULT
   The kernel detected that your third argument, the pointer to a struct
   iowarrior_info was referencing an illegal address (most likely NULL)
errno=ENOTTY
   Something was wrong with the second argument (use the #define value from
   the header like I did).
   You will also get this error when you try to pack the struct with some
   compiler option. Internally the ioctl does some magic with the size
   of the struct to find out which one of the ioctl's you requested. So the
   call will fail because you change the size of the struct by packing it.


3) Reading
----------
This is almost just like reading from any file on you system. "Almost" in this
case means that you are limited in the number of bytes you can read. When new
data arrives from an IOWarrior it comes in chunks of bytes. The size for such
a Report is determined by the type of product and whether it comes from the
IO-Pin functions or the SpecialMode functions. Here is a table for the
different sizes:
 
           Report Sizes
-------------------------------------
|Product     | IO-Pin | SpecialMode |
-------------------------------------
|IOWarrior24 |    2   |      8      |
|IOWarrior40 |    4   |      8      |
|IOWarrior56 |    7   |     64      |
-------------------------------------
             Table 1

You can only read the exact number of bytes as stated in Table 1 from an
IOWarrior with a single read(). The read will either return all the bytes you
requested or none. If you don't want to check the product-type on each read or
write simply use the value of 'iowarrior.packet_size' in the previous chapter. 
The read call will also block if no data is immediately available, so you
probably want to use select() to monitor the arrival of new data.

char buf[64];  //make sure it is big enough
int size=info.packet_size; //take the value from the previous chapter

if(read(fd,buf,size)!=size) {
    printf( "Error in read %d\n",errno );
    goto exit;
}
else {
    //do something with the data in buf
}

Here are the errno values that are returned when read() failed

errno=ENODEV
    The device was unplugged
errno=EINVAL
    You requested to read more or less than the number bytes allowed 
    (please refer to the values in the table)
errno=EAGAIN
    You have set F_NONBLOCK for your filedescriptor and no data is currently
    available. 
errno=EFAULT
    The address for the buffer into which the data was to be read is invalid
    (Most likely NULL)
errno=ERESTART
    An uncaught signal was send to your application. (You hit ctrl-C for an
    app that was stuck in a blocking read ?)

Internally the module uses a buffer of 16 reports from which the data is read.
When the buffer is filled up and a new Report arrives, the oldest value from
buffer is lost. So check for new values according to the expected data-rate of
the hardware. The IOWarrior24 and IOWarrior40 can send a new report at an
interval of 8 ms, the IOWarrior needs 1 ms for this. So if you expect fast bursts
of signals that toggle the IO-Pins on and off you better be fast not to lose
reports.

3.1 Another way of reading ?
----------------------------
Not very much known as its not documented: there is also an ioctl() for
reading from the device. The difference between the ioctl() and the 'normal'
read() is that the ioctl() does not look at the contents of the internal
buffer, it sends a request to the IOWarrior itself to report the current
status of the IO-PINs. 
This can be useful if you had opened a device but nothing happened yet on the
IO-PINs. The buffer will be empty and you can't get the current state of the
IO-Pins. This works almost the same as using the SpecialMode
'GetCurrentPinsStatus'. The only extra the ioctl() has to offer is
that you can call this function from the IO-PIN device. There is no
side-effect of the ioctl() on the internal buffer.

You do not have to specify the number of bytes to read into the buffer you
supply. The function simply copies the number of bytes from Table 1 into
the buffer. So make sure it is big enough!

char buf[7];  //this is big enough for all IOWarriors

if(ioctl(fd,IOW_READ,buf)) {
    printf( "Error in read %d\n",errno );
    goto exit;
}
else {
    //do something with the data in buf
}

If the ioctl() returns 0, the buffers contents are set to the state of the
IO-PINs. Otherwise errno is set to one of the values

errno=ENODEV
   The device was unplugged!  
errno=EFAULT
   The kernel detected that your third argument, the pointer to the buffer
   was referencing an illegal address (most likely NULL)
errno=ENOTTY
   Something was wrong with the second argument (use the #define value from
   the header like I did).

4) Writing
----------
Like with reading, there are also two different ways to send new data to the
IOWarrior. Until recently an ioctl() call was used for writing to the
device. But with the release of the IOWarrior56 this approach does not work
any more. The new chip enforced the implementation of a generic write()
function which is the recommended way of writing to the devices from now on. 

4.1) The write() approach
-------------------------
Starting with version 0.4 of the iowarrior kernel-module, there is a generic
write() function. 
The number of bytes you want to write to an IOWarrior must match the values in
in Table 1 from the chapter about reading. The only exclusion is that the module
allows you to write nothing (0 bytes). 

The write looks like you already expected it :

//take the value from the previous chapter
char buf[64];  //make sure it is big enough
int size=info.packet_size; //take the value from chapter 2

buf[0]=0xFF //setup the data to be written
buf[1]=0x??
.....

if(write(fd,buf,size)!=size) {
    printf( "Error in write %d\n",errno );
    goto exit;
}
else {
    //do something with the data in buf
}

and here are the errno values that are returned when write failed

errno=ENODEV
    The device was unplugged
errno=EINVAL
    You requested to write more or less than the number bytes allowed 
    (please refer to the values in Table 1 )
errno=EAGAIN
    You have set F_NONBLOCK for your filedescriptor but the write
    would block because the device is busy. 
errno=EFAULT
    The address for the buffer that contains the data to be written is invalid
    (Most likely NULL)
errno=ERESTART
    An uncaught signal was send to your application. (You hit ctrl-C for an
    app that was stuck in a blocking read ?)
errno=ENOMEM
    The driver was unable to allocate some resources in kernel memory.
    
But that's not the whole story:
In the write() function the data is handed over to the Linux USB-core which
also reports errors not mentioned here. But this list is simply too long to be
repeated. But it's already installed with the Linux kernel-sources so have a
look at the file 
'/usr/src/linux/Documentation/usb/error-codes.txt'
If write() returns something unusual it might help. (I found out that my
external hub quit working when I plug it in an USB2.0 port).

Another question that might come up : How long does a write take?
For the IOWarrior24 and the IOWarrior40 you could expect something like 4-5
milliseconds for each write access. For an IOWarrior56 it's 1 millisecond.
(Not if you backup your harddisk over USB at the same time!)

I strongly suggest the use of select() to check whether the IOWarrior
is ready to accept new data. If you send fast bursts of data and the
device is busy, your application is put to sleep inside the kernel. It will be
woken up when the new data can be written, but some tests showed that it is
much faster to use select() than to block in kernel mode.

4.2) The ioctl() approach
-------------------------
Up until recently using an ioctl() was the only way to send data to an
IOWarrior. You can now consider this as deprecated, though it probably will never
go away. If you intent on using an IOWarrior56 in the near future you can
skip this whole chapter. Writing through the ioctl() to the IOWarrior56 is not
and never will be possible. If you try it anyway, it will simply return -1, set
errno to EINVAL and put an error-message in the kernel logs.

But if you have to maintain older code, here's the short version how it works:

char buf[8];  //this is big enough for all IOWarriors 
              //because the IOWarrior56 is not supported

buf[0]=0xFF //setup the data to be written
buf[1]=0x??
.....

if(ioctl(fd,IOW_WRITE,buf)) {
    printf( "Error in write %d\n",errno );
    goto exit;
}

If the ioctl() returns 0, the buffers contents were written (that is the 
number of bytes from Table 1). Otherwise errno is set to one of the values

errno=ENODEV
   The device was unplugged!  
errno=EFAULT
   The kernel detected that your third argument, the pointer to the buffer
   was referencing an illegal address (most likely NULL)
errno=ENOTTY
   Something was wrong with the second argument (use the #define value from
   the header like I did).
errno=EINVAL
   You tried to write something to an unsupported product (IOWarrior56)


5) Closing the device
---------------------
This is going to be short! Please do it! Even when an IOWarrior was unplugged
there are still kernel resources allocated by the module. They have to be kept
around until you call close() on your file-descriptor. 

We all know what close looks like

if(close(fd)) {
    printf( "Error in close %d\n",errno );
    goto exit;
}

Even if close() returns something else than 0 you're done.
The only error that is special for an IOWarrior is 

errno=ENODEV
   The device was unplugged...  

...but that was probably the reason why you closed it.



Thanks for reading, and have fun

Eberhard Fahle August 4 2006

Questions, objections, corrections to <e.fahle@wayoda.org>

----------------------------------------------------------------------------
Legal disclaimer 

This document is ©2006 by Eberhard Fahle.
I make no claims as to the completeness or correctness of the information
contained in this document.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
----------------------------------------------------------------------------

