Difference between revisions of "Using the EMAC GPIO Class"

From wiki.emacinc.com
Jump to: navigation, search
(more porting)
(more porting)
Line 10: Line 10:
  
 
An EMAC GPIO Class Device has three main components. A clear understanding of these components is important for proper use and implementation.
 
An EMAC GPIO Class Device has three main components. A clear understanding of these components is important for proper use and implementation.
 +
  
 
{{imbox | type=notice | text='''NOTE:''' The code listings on this page are simple examples of how the EMAC GPIO Class may be used; they are not intended to be compiled or run in their listed state.}}
 
{{imbox | type=notice | text='''NOTE:''' The code listings on this page are simple examples of how the EMAC GPIO Class may be used; they are not intended to be compiled or run in their listed state.}}
Line 131: Line 132:
 
-->
 
-->
 
===Lock===
 
===Lock===
 +
 
{{imbox | type = notice | text = '''Note:''' Locking support is currently not available on all EMAC boards. This feature was introduced in the later versions of the 2.6.28 kernel for the AT91-based boards. It will be implemented in other kernel trees in the future.}}
 
{{imbox | type = notice | text = '''Note:''' Locking support is currently not available on all EMAC boards. This feature was introduced in the later versions of the 2.6.28 kernel for the AT91-based boards. It will be implemented in other kernel trees in the future.}}
 +
 +
 
Many GPIO devices may be accessed by multiple processes simultaneously, which leads to the possibility of race conditions. The EMAC GPIO Class includes a locking mechanism to prevent these cases when used correctly. Without the locking mechanism, synchronization needs to be performed in userspace.
 
Many GPIO devices may be accessed by multiple processes simultaneously, which leads to the possibility of race conditions. The EMAC GPIO Class includes a locking mechanism to prevent these cases when used correctly. Without the locking mechanism, synchronization needs to be performed in userspace.
  
Line 198: Line 202:
 
The ioctl commands in this section do not affect the lock of the GPIO device and are only available in kernels supporting the GPIO Class locking feature. They are simple versions of the standard commands listed in the [[#Locking]] section. Before calling one of these commmands, the process must acquire the lock using the <code>GPIOLOCK</code> command. The purpose of these commands is to allow synchronization between multiple processes or threads when accessing the same device. For example, when preforming a read followed by a write, the lock would need to be held for the duration of both commands to ensure that incorrect values are not written to the device.
 
The ioctl commands in this section do not affect the lock of the GPIO device and are only available in kernels supporting the GPIO Class locking feature. They are simple versions of the standard commands listed in the [[#Locking]] section. Before calling one of these commmands, the process must acquire the lock using the <code>GPIOLOCK</code> command. The purpose of these commands is to allow synchronization between multiple processes or threads when accessing the same device. For example, when preforming a read followed by a write, the lock would need to be held for the duration of both commands to ensure that incorrect values are not written to the device.
  
* <code>DATAREAD_NL</code>: Read the current value of the device's ''data'' member.
+
* <code>DATAREAD_NL</code>: Read the current value of the device's <code>data</code> member.
* <code>DATAWRITE_NL</code>: Write a new value to the device's ''data'' member.
+
* <code>DATAWRITE_NL</code>: Write a new value to the device's <code>data</code> member.
* <code>DDRREAD_NL</code>: Read the current value of the device's ''ddr'' member.
+
* <code>DDRREAD_NL</code>: Read the current value of the device's <code>ddr</code> member.
* <code>DDRWRITE_NL</code>: Write a new value to the device's ''ddr'' member.
+
* <code>DDRWRITE_NL</code>: Write a new value to the device's <code>ddr</code> member.
* <code>INDEXREAD_NL</code> the current value of the device's ''index'' member.
+
* <code>INDEXREAD_NL</code> the current value of the device's <code>index</code> member.
* <code>INDEXWRITE_NL</code>: Write a new value to the device's ''index'' member.
+
* <code>INDEXWRITE_NL</code>: Write a new value to the device's <code>index</code> member.
  
 
===== Asynchronous Notification =====
 
===== Asynchronous Notification =====
 
The ioctl commands in this section are for use with the asynchronous notification feature of the EMAC GPIO Class. Most devices do not use this feature and these commands are only available on kernel's that support this feature. Low-level implementation is highly device-specific.
 
The ioctl commands in this section are for use with the asynchronous notification feature of the EMAC GPIO Class. Most devices do not use this feature and these commands are only available on kernel's that support this feature. Low-level implementation is highly device-specific.
  
* ''SETNOTIFY'': Set a new notification bitmask for the current file descriptor. The value is passed to the ioctl call as a pointer to a 32-bit integer. The gpio lock is acquired and released during this command.
+
* <code>SETNOTIFY</code>: Set a new notification bitmask for the current file descriptor. The value is passed to the ioctl call as a pointer to a 32-bit integer. The gpio lock is acquired and released during this command.
* ''GETNOTIFY'': Get the current notification bitmask for this file descriptor. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
+
* <code>GETNOTIFY</code>: Get the current notification bitmask for this file descriptor. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
* ''DATAREADQ'': Read the first value from the data queue. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
+
* <code>DATAREADQ</code>: Read the first value from the data queue. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
* ''GETQUEUESIZE'': Read the current number of values in the data queue for this file descriptor. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
+
* <code>GETQUEUESIZE</code>: Read the current number of values in the data queue for this file descriptor. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
 +
 
 +
 
 +
==== Programming ====
 +
 
 +
The following general examples demonstrate the character driver programming interface.
 +
 
 +
 
 +
{{imbox | type=content| text=The examples below do not check the return value of calls for errors. Be sure to check for and handle errors in a production system.}}
 +
 
 +
 
 +
To access a device from a C application, it must first be opened using ''open()'':
 +
<syntaxhighlight lang=c>
 +
int fd;
 +
fd = open(DEVICE, O_RDWR);
 +
...
 +
close(fd);
 +
</syntaxhighlight>
 +
 
 +
If the open command is successful, any supported ioctl command may be used to access the device. The example below sets the value of the ''data'' member to 0xFF
 +
 
 +
<syntaxhighlight lang=c>
 +
__u32 arg;
 +
...
 +
arg = 0xff;
 +
ioctl(fd, DATAWRITE, &arg);
 +
</syntaxhighlight>
 +
 
 +
The example below uses the locking mechanism to perform a sequence of accesses to a device:
 +
<syntaxhighlight lang=c>
 +
#define CONTROLBIT (1 << 3)
 +
__u32 arg;
 +
...
 +
ioctl(fd, GPIOLOCK, NULL);
 +
arg = 1;
 +
ioctl(fd, INDEXWRITE_NL, &arg);
 +
ioctl(fd, DATAREAD_NL, &arg);
 +
/* set control bit */
 +
arg &= CONTROLBIT;
 +
ioctl(fd, DATAWRITE_NL, &arg);
 +
ioctl(fd, GPIOUNLOCK, NULL);
 +
</syntaxhighlight>
 +
 
 +
 
 +
==== Asynchronous Notification Example ====
 +
 
 +
 
 +
Asynchronous notification for the EMAC GPIO Class is a new feature in some kernels. This was added to the driver at the same time as locking support and utilizes the ''fasync'' system call. Essentially, this feature allows the kernel driver to notify the user application of some event through a signal. Typically this is used for devices which support hardware interrupts. Currently, no standard EMAC devices utilize the asynchronous notification feature.
 +
 
 +
 
 +
For each open file descriptor of a certain device, it is possible to set a notification mask indicating which types of events will trigger notification as well as which signal will be sent to the process from the driver. The default signal is ''SIGIO''. When an event occurs, a value will be written to a queue for each file descriptor that is registered for notification of the event. The notification mask, events, and queue functionality are all implementation-specific.
 +
 
 +
 
 +
The following example utilizes the asynchronous notification interface for a device that supports it. This particular device is an interrupt-enabled register that will notify a process if the type of interrupt that occurs matches the notification mask of the particular file descriptor. When an applicable interrupt occurs, the data register will immediately be queued into the file descriptor specific data queue.
 +
{{imbox | text='''Note:''' The device utilized in the example below does not exist on any EMAC systems. The example is provided purely for the purpose of illustrating the asynchronous notification interface.}}
 +
 
 +
The following code is <code>gpio-fasync-example.c</code>:
 +
<syntaxhighlight lang=c>
 +
#define _GNU_SOURCE
 +
 
 +
#include <fcntl.h>
 +
#include <sys/types.h>
 +
#include <sys/stat.h>
 +
#include <sys/ioctl.h>
 +
#include <stdio.h>
 +
#include <stdlib.h>
 +
#include <unistd.h>
 +
#include <asm/types.h>
 +
#include <signal.h>
 +
#include "gpio_char.h"
 +
 
 +
static sig_atomic_t caught = 0;
 +
static sig_atomic_t run = 1;
 +
 
 +
void sig_handle(int sig)
 +
{
 +
if (sig == SIGIO)
 +
caught = 1;
 +
else /* termination */
 +
run = 0;
 +
}
 +
 
 +
#define DEVICE      "/dev/gpio_async"
 +
#define NOTIFY_MASK (1 << 4)
 +
 
 +
/**
 +
* function to enable asynchronous notification on the given file descriptor
 +
* for the specified signal
 +
* @param fd the open GPIO device file descriptor to enable notification for
 +
* @param sig the signal to be sent by the kernel for event notification
 +
*/
 +
static void set_fasync(int fd, int sig);
 +
 
 +
/**
 +
* function to set the event notification flags for the driver on a given
 +
* file descriptor.
 +
* @param fd the open GPIO device file descriptor for notification
 +
* @param notify bitmask specifying which events this file descriptor should
 +
*  be notified on
 +
* @return the result of the ioctl() call to set the notification flags
 +
*/
 +
static int set_notify_flags(int fd, __u32 notify);
 +
 
 +
/**
 +
* function to get the queue size for the given file descriptor
 +
* @param fd the open GPIO device file descriptor to query
 +
* @return the current queue size as reported by the driver
 +
*/
 +
static __u32 get_queue_size(int fd);
 +
 
 +
int main(int argc, char *argv[]){
 +
 
 +
int fd;
 +
__u32 data;
 +
struct sigaction sact;
 +
sigset_t block_mask;
 +
sigset_t suspend_mask;
 +
sigset_t old_mask;
 +
 
 +
/* set up signal handlers */
 +
sact.sa_handler = sig_handle;
 +
sigemptyset(&sact.sa_mask);
 +
sact.sa_flags = 0;
 +
sigaction(SIGIO, &sact, NULL);
 +
sigaction(SIGTERM, &sact, NULL);
 +
sigaction(SIGINT, &sact, NULL);
 +
 
 +
/* Open all devices */
 +
if ((fd = open(DEVICE, O_RDWR)) <= 0) {
 +
fprintf(stderr, "Error on open %s\n", DEVICE);
 +
exit(EXIT_FAILURE);
 +
}
 +
 
 +
/* allow sigsuspend to be interrupted by any signal */
 +
sigemptyset(&suspend_mask);
 +
 
 +
/*
 +
* note that it is advantageous to mask all requested signals at
 +
* this point until all configuration is completed at which point
 +
* they will be unmasked by sigsuspend(). This also makes sense for
 +
* the implementation here, as the signals will be blocked again after
 +
* sigsuspend() returns during the DATAREADQ calls, preventing race
 +
* conditions. The call to sigprocmask() below does this.
 +
*/
 +
sigemptyset(&block_mask);
 +
sigaddset(&block_mask, SIGIO);
 +
 
 +
sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
 +
 
 +
/* enable fasync notification on device */
 +
set_fasync(fd, SIGIO);
 +
 
 +
/* Inform the driver which flags each fd should be notified on */
 +
set_notify_flags(fd, NOTIFY_MASK);
 +
 
 +
printf("Waiting for signals\n");
 +
fflush(stdout);
 +
 
 +
while (run) {
 +
sigsuspend(&suspend_mask);
 +
if (caught) {
 +
while (get_queue_size(fd) > 0) {
 +
ioctl(fd, DATAREADQ, &data);
 +
printf("Queued value 0x%X\n", data);
 +
caught = 0;
 +
}
 +
}
 +
}
 +
 
 +
printf("Terminating...\n");
 +
close(fd);
 +
 
 +
exit(EXIT_SUCCESS);
 +
}
 +
 
 +
static void set_fasync(int fd, int sig)
 +
{
 +
int flags;
 +
 
 +
flags = fcntl(fd, F_GETFL);
 +
flags |= FASYNC;
 +
/* set owner... tells who to send the signal to */
 +
/*
 +
* note that in a threaded application, getpid() should be replaced
 +
* with getid()
 +
*/
 +
fcntl(fd, F_SETOWN, getpid());
 +
fcntl(fd, F_SETSIG, sig);
 +
/* Now that configuration is complete, set FASYNC mode */
 +
fcntl(fd, F_SETFL, flags);
 +
}
 +
 
 +
static int set_notify_flags(int fd, __u32 notify)
 +
{
 +
return ioctl(fd, SETNOTIFY, &notify);
 +
}
 +
 
 +
static __u32 get_queue_size(int fd)
 +
{
 +
__u32 data;
 +
 
 +
ioctl(fd, GETQUEUESIZE, &data);
 +
return data;
 +
}
 +
 
 +
</syntaxhighlight>
 +
 
 +
The following code is <code>gpio_char.h</code>
 +
<syntaxhighlight lang=c>
 +
#ifndef GPIO_CHAR_H_
 +
#define GPIO_CHAR_H_
 +
 
 +
/* Userspace version of kernel header file */
 +
 
 +
#include <linux/ioctl.h>
 +
 
 +
#define RTDM_CLASS_GPIO 0x80
 +
 
 +
/* standard read/write functions */
 +
#define DDRREAD        _IOR(RTDM_CLASS_GPIO,0,char)
 +
#define DDRWRITE        _IOW(RTDM_CLASS_GPIO,0,char)
 +
#define DATAREAD        _IOR(RTDM_CLASS_GPIO,1,char)
 +
#define DATAWRITE      _IOW(RTDM_CLASS_GPIO,1,char)
 +
#define INDEXREAD      _IOR(RTDM_CLASS_GPIO,2,char)
 +
#define INDEXWRITE      _IOW(RTDM_CLASS_GPIO,2,char)
 +
 
 +
/* nolock functions */
 +
#define DDRREAD_NL _IOR(RTDM_CLASS_GPIO, 3, char)
 +
#define DDRWRITE_NL _IOW(RTDM_CLASS_GPIO, 3, char)
 +
#define DATAREAD_NL _IOR(RTDM_CLASS_GPIO, 4, char)
 +
#define DATAWRITE_NL _IOW(RTDM_CLASS_GPIO, 4, char)
 +
#define INDEXREAD_NL _IOR(RTDM_CLASS_GPIO, 5, char)
 +
#define INDEXWRITE_NL _IOW(RTDM_CLASS_GPIO, 5, char)
 +
 
 +
/* lock/unlock */
 +
#define GPIOLOCK _IO( RTDM_CLASS_GPIO, 6)
 +
#define GPIOUNLOCK _IO( RTDM_CLASS_GPIO, 7)
 +
 
 +
#define DATAREADQ      _IOR(RTDM_CLASS_GPIO, 8, char)
 +
#define GETNOTIFY      _IOR(RTDM_CLASS_GPIO, 9, char)
 +
#define SETNOTIFY      _IOW(RTDM_CLASS_GPIO, 10, char)
 +
#define GETQUEUESIZE    _IOR(RTDM_CLASS_GPIO, 11, char)
 +
 
 +
#endif /*GPIO_CHAR_H_*/
 +
</syntaxhighlight>
 +
 
  
 
<!--[[Category:Custom Development]]-->
 
<!--[[Category:Custom Development]]-->

Revision as of 19:16, 14 January 2014

TODO: {{#todo:Revise; List related hardware eventually; (12.17.13-18:20->KY+);(12.18.13-19:00->MD+);(12.19.13-11:21->MW+);(01.14.14-14:20->KY-)|Klint Youngmeyer|oe 4, oe 5,mw,ky,Revise,md}}

The EMAC GPIO Class is a generic programming interface for General Purpose Input Output (GPIO) devices in the Linux Kernel. The GPIO class driver has two main interfaces under standard Linux: Linux sysfs and character device. The GPIO class is extremely flexible and uses a set of function pointers at the kernel level to allow for specialized read/write functions for each device. EMAC utilizes the GPIO class to create devices that use the same simple interface, such as GPIO registers, simple configuration variables, hardware registers, and Analog-to-Digital converter drivers where the user needs to retrieve a single value from the device.

Supported Devices

Most of EMAC's ARM based SOM-Carrier combinations and some of EMAC's x86 boards support the EMAC GPIO Class. For a list of GPIOs on each device, please see the EMAC website at emacinc.com/products/system_on_module.


Components

An EMAC GPIO Class Device has three main components. A clear understanding of these components is important for proper use and implementation.


Data

The data member of a GPIO device is the primary component. In the most simple devices, this is the only component accessible from userspace. The data member is read and/or written to access or set the current value of the GPIO device. The actual function of the data member is implementation specific. For a GPIO device in its purest form, such as the GPIO ports present in many of EMAC's CPLD designs, the data member is a register value with each of the 8 least significant bits representing the digital state of a single line in the GPIO port. In contrast, A/D devices implemented with the EMAC GPIO Class will typically provide the analog value of the currently selected channel in the data member.

The data member is allocated as a 32-bit unsigned integer regardless of implementation. Many devices use only the least significant byte or the least significant bit.

DDR

The ddr or Data Direction Register is a member of the EMAC GPIO Class that is used primarily for direction-configurable GPIO devices. Many devices do not register this aspect of the interface. The ddr member is used to determine if a particular device should act as an input or output. Depending on the implementation, many devices are bit-configurable GPIO devices, meaning that each bit/line in the GPIO device can be configured as an input or output by its respective value in the ddr. Typically, a '1' value in the ddr configures the device as an output and a '0' value in the ddr configures the device as an input. Bidirectional GPIO devices are generally configured as inputs by default until explicitly configured as an output to prevent hardware contention.

For example, an EMAC GPIO Class device porta is a one-byte bit-configurable bidirectional GPIO device. Each bit in the data register corresponds to the digital status of the corresponding line porta[0-7]. On reset, the ddr member is read at 0x00, indicating that the device is configured with all lines as inputs. If the ddr member is set to 0xAA (binary 1010 1010) bits 0, 2, 4, and 6 will remain configured as inputs while the rest of the bits will be configured as outputs. Reading and setting the value in the data member will react according to the configuration set in the ddr. The ddr is allocated as a 32-bit unsigned integer regardless of implementation.

Index

Many EMAC GPIO Class devices are implemented as indexed GPIO Devices. These devices utilize a member named index which impacts the device according to the implementation. The index member is typically used to specify a memory offset from some base for the data member or some type of channel setting or controller selection. Many devices ignore the index value or do not register this member at all.

An example of an indexed GPIO Class device is the indexed_atod device (and other A/D devices) found on many EMAC boards. In this case, the index member is used to set the current channel of the A/D to read from the data member. When the index is set to 0, channel 0 will be accessed. When the index is set to 3, the data register will reflect the current value on channel 3 of the device. Another example would be two identical counter registers in a device where setting the index would control which counter was accessed when reading the data register. This would be an alternative to registering a separate device for each counter.

Indexed GPIO devices should check the applicable range for the index when the user attempts to set the device. For example, an 8-channel A/D device implemented as an EMAC GPIO Class device would have a valid range for the index member of 0-7. Attempting to set the index to anything greater than 7 would cause the index to be set to the maximum allowed value of 7.

Lock


Many GPIO devices may be accessed by multiple processes simultaneously, which leads to the possibility of race conditions. The EMAC GPIO Class includes a locking mechanism to prevent these cases when used correctly. Without the locking mechanism, synchronization needs to be performed in userspace.

The lock is implemented as a struct mutex in the GPIO Class device structure. The lock is obtained by the GPIO driver interface through a call to mutex_lock() and freed using mutex_unlock(). This means that any attempts to lock an already locked device will cause the caller to sleep until the lock is freed by the process holding the lock. A nonblocking method using mutex_trylock() may be implemented in the future.

All access to a device must be synchronized using the lock. Further details on this implementation are described in later sections.

User Interfaces

This section describes the two user interfaces available for the GPIO class. The sysfs layer is very handy for scripting and testing. For higher performance and more flexible access, the character driver interface is used, generally from a C application. This provides a simple ioctl() interface.

Sysfs

The sysfs filesystem is a virtual filesystem provided by the Linux kernel that allows for a file-based interface to kernel driver parameters. The EMAC GPIO Class utilizes sysfs to provide a simple read/write interface to the GPIO Class device. This interface is easily accessible from the Linux shell and through shell scripting.

The GPIO Class driver registers a directory at /sys/class/gpio assuming that sysfs has been mounted on the system. Under this directory, every GPIO Class device registered with the system will have a directory (i.e. /sys/class/gpio/porta). Within this directory, there will be some files that are a part of the sysfs heirarchy as well as the GPIO Class interface files data, ddr, and index. Depending on the type of device and its implementation, the ddr and index files may or may not exist; data should always be present.


Reading

Reading the current value of a device's settings is as simple as reading the respective file. The cat command is typically used for this. The output is formatted as ASCII hexadecimal characters. For example, to read the value of the data member of the porta device, run the command:

emac-oe$ cat /sys/class/gpio/porta/data

The implementation of the read function for the data, ddr, and index members using the sysfs interface automatically acquires and frees the lock on the respective GPIO device. The actual value returned depends on the device implementation.

Writing

Writing a new setting to a member of the GPIO device through the sysfs interface is accomplished by writing a new value to the respective file. The echo command is typically used along with file redirection to set the new value directly, although the output of any command could be used as long as it is formatted correctly. For example, to set the porta device to 0xFF (all on) the user would first set all bits to output using the ddr register and then write 0xFF to the data register as shown below:

emac-oe$ echo 0xff > /sys/class/gpio/porta/ddr
emac-oe$ echo 0xff > /sys/class/gpio/porta/data

The implementation of the write function for the data, ddr, and index members using the sysfs interface automatically acquires and frees the lock on the respective GPIO device. The low-level effects of the write function depends on the device implementation.

Character Driver

The character driver interface is the preferred method for accessing EMAC GPIO Class devices from programmed applications. This interface is significantly more efficient than the sysfs layer and easier to program. In kernels that support the GPIO Class locking mechanism, the character driver interface offers user control over the lock that is not available using the sysfs interface. The character driver interface uses the ioctl() system call. Newer kernels also support an fasync method for signal-based event notification from the driver to the user application.

Available IOCTLs

Several different ioctl command are available for use. The user must know the supported features of the device before using an ioctl command. For example, many devices do not support the ddr or index functionality. All ioctl commands will return a negative error code on failure.

Locking

The ioctl commands in this section will acquire and free the GPIO lock on kernels that include support for the lock. These commands are the most efficient to use when a single GPIO read/write is required. On kernels that do not support the GPIO lock, these commands simply preform the advertised action.

  • DATAREAD: Read the current value of the devices data member by calling the data_read function. Return value is stored in a 32-bit integer which is passed to the ioctl via a pointer.
  • DATAWRITE: Write a new value to the device's data member by calling the data_write function. The value to write is passed to the ioctl call through a pointer to a 32-bit integer.
  • DDRREAD: Read the current value of the device's ddr member by calling the ddr_read function. Return value is stored in a 32-bit integer which is passed to the ioctl via a pointer.
  • DDRWRITE: Write a new value to the device's ddr member by calling the ddr_write function. The value to write is passed to the ioctl call through a pointer to a 32-bit integer.
  • INDEXREAD: Read the current value of the devices index member by calling the index_read function. Return value is stored in a 32-bit integer which is passed to the ioctl via a pointer.
  • INDEXWRITE: Write a new value to the device's index member by calling the index_write function. The value to write is passed to the ioctl call through a pointer to a 32-bit integer.
Lock Control

The ioctl commands in this section are only available on kernels that support the GPIO Class locking mechanism. They are used to control the current state of the lock.

  • GPIOLOCK: Lock the device's mutex through a call to mutex_lock(). This command will sleep until the lock has been acquired.
  • GPIOUNLOCK: Release the device's mutex through a call to mutex_unlock(). This command should not be called unless the lock has already been acquired using GPIOLOCK.
No-Lock

The ioctl commands in this section do not affect the lock of the GPIO device and are only available in kernels supporting the GPIO Class locking feature. They are simple versions of the standard commands listed in the #Locking section. Before calling one of these commmands, the process must acquire the lock using the GPIOLOCK command. The purpose of these commands is to allow synchronization between multiple processes or threads when accessing the same device. For example, when preforming a read followed by a write, the lock would need to be held for the duration of both commands to ensure that incorrect values are not written to the device.

  • DATAREAD_NL: Read the current value of the device's data member.
  • DATAWRITE_NL: Write a new value to the device's data member.
  • DDRREAD_NL: Read the current value of the device's ddr member.
  • DDRWRITE_NL: Write a new value to the device's ddr member.
  • INDEXREAD_NL the current value of the device's index member.
  • INDEXWRITE_NL: Write a new value to the device's index member.
Asynchronous Notification

The ioctl commands in this section are for use with the asynchronous notification feature of the EMAC GPIO Class. Most devices do not use this feature and these commands are only available on kernel's that support this feature. Low-level implementation is highly device-specific.

  • SETNOTIFY: Set a new notification bitmask for the current file descriptor. The value is passed to the ioctl call as a pointer to a 32-bit integer. The gpio lock is acquired and released during this command.
  • GETNOTIFY: Get the current notification bitmask for this file descriptor. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
  • DATAREADQ: Read the first value from the data queue. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.
  • GETQUEUESIZE: Read the current number of values in the data queue for this file descriptor. The value is returned via a pointer to a 32-bit integer passed to the ioctl call. The gpio lock is not acquired and does not need to be held for this command.


Programming

The following general examples demonstrate the character driver programming interface.



To access a device from a C application, it must first be opened using open():

int fd;
fd = open(DEVICE, O_RDWR);
...
close(fd);

If the open command is successful, any supported ioctl command may be used to access the device. The example below sets the value of the data member to 0xFF

__u32 arg;
...
arg = 0xff;
ioctl(fd, DATAWRITE, &arg);

The example below uses the locking mechanism to perform a sequence of accesses to a device:

#define CONTROLBIT (1 << 3)
__u32 arg;
...
ioctl(fd, GPIOLOCK, NULL);
arg = 1;
ioctl(fd, INDEXWRITE_NL, &arg);
ioctl(fd, DATAREAD_NL, &arg);
/* set control bit */
arg &= CONTROLBIT;
ioctl(fd, DATAWRITE_NL, &arg);
ioctl(fd, GPIOUNLOCK, NULL);


Asynchronous Notification Example

Asynchronous notification for the EMAC GPIO Class is a new feature in some kernels. This was added to the driver at the same time as locking support and utilizes the fasync system call. Essentially, this feature allows the kernel driver to notify the user application of some event through a signal. Typically this is used for devices which support hardware interrupts. Currently, no standard EMAC devices utilize the asynchronous notification feature.


For each open file descriptor of a certain device, it is possible to set a notification mask indicating which types of events will trigger notification as well as which signal will be sent to the process from the driver. The default signal is SIGIO. When an event occurs, a value will be written to a queue for each file descriptor that is registered for notification of the event. The notification mask, events, and queue functionality are all implementation-specific.


The following example utilizes the asynchronous notification interface for a device that supports it. This particular device is an interrupt-enabled register that will notify a process if the type of interrupt that occurs matches the notification mask of the particular file descriptor. When an applicable interrupt occurs, the data register will immediately be queued into the file descriptor specific data queue.

The following code is gpio-fasync-example.c:

#define _GNU_SOURCE

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <asm/types.h>
#include <signal.h>
#include "gpio_char.h"

static sig_atomic_t caught = 0;
static sig_atomic_t run = 1;

void sig_handle(int sig)
{
	if (sig == SIGIO)
		caught = 1;
	else /* termination */
		run = 0;
}

#define DEVICE      "/dev/gpio_async"
#define NOTIFY_MASK (1 << 4)

/**
 * function to enable asynchronous notification on the given file descriptor
 * for the specified signal
 * @param fd the open GPIO device file descriptor to enable notification for
 * @param sig the signal to be sent by the kernel for event notification
 */
static void set_fasync(int fd, int sig);

/**
 * function to set the event notification flags for the driver on a given 
 * file descriptor.
 * @param fd the open GPIO device file descriptor for notification
 * @param notify bitmask specifying which events this file descriptor should
 *   be notified on
 * @return the result of the ioctl() call to set the notification flags
 */
static int set_notify_flags(int fd, __u32 notify);

/**
 * function to get the queue size for the given file descriptor
 * @param fd the open GPIO device file descriptor to query
 * @return the current queue size as reported by the driver
 */
static __u32 get_queue_size(int fd);

int main(int argc, char *argv[]){

	int fd;
	__u32 data;
	struct sigaction sact;
	sigset_t block_mask;
	sigset_t suspend_mask;
	sigset_t old_mask;

	/* set up signal handlers */
	sact.sa_handler = sig_handle;
	sigemptyset(&sact.sa_mask);
	sact.sa_flags = 0;
	sigaction(SIGIO, &sact, NULL);
	sigaction(SIGTERM, &sact, NULL);
	sigaction(SIGINT, &sact, NULL);

	/* Open all devices */
	if ((fd = open(DEVICE, O_RDWR)) <= 0) {
		fprintf(stderr, "Error on open %s\n", DEVICE);
		exit(EXIT_FAILURE);
	}

	/* allow sigsuspend to be interrupted by any signal */
	sigemptyset(&suspend_mask);

	/* 
	 * note that it is advantageous to mask all requested signals at
	 * this point until all configuration is completed at which point
	 * they will be unmasked by sigsuspend(). This also makes sense for
	 * the implementation here, as the signals will be blocked again after
	 * sigsuspend() returns during the DATAREADQ calls, preventing race
	 * conditions. The call to sigprocmask() below does this.
	 */
	sigemptyset(&block_mask);
	sigaddset(&block_mask, SIGIO);

	sigprocmask(SIG_BLOCK, &block_mask, &old_mask);

	/* enable fasync notification on device */
	set_fasync(fd, SIGIO);

	/* Inform the driver which flags each fd should be notified on */
	set_notify_flags(fd, NOTIFY_MASK);

	printf("Waiting for signals\n");
	fflush(stdout);

	while (run) {
		sigsuspend(&suspend_mask);
		if (caught) {
			while (get_queue_size(fd) > 0) {
				ioctl(fd, DATAREADQ, &data);
				printf("Queued value 0x%X\n", data);
				caught = 0;
			}
		}
	}

	printf("Terminating...\n");
	close(fd);

	exit(EXIT_SUCCESS);
}

static void set_fasync(int fd, int sig)
{
	int flags;

	flags = fcntl(fd, F_GETFL);
	flags |= FASYNC;
	/* set owner... tells who to send the signal to */
	/* 
	 * note that in a threaded application, getpid() should be replaced
	 * with getid()
	 */
	fcntl(fd, F_SETOWN, getpid());
	fcntl(fd, F_SETSIG, sig);
	/* Now that configuration is complete, set FASYNC mode */
	fcntl(fd, F_SETFL, flags);
}

static int set_notify_flags(int fd, __u32 notify)
{
	return ioctl(fd, SETNOTIFY, &notify);
}

static __u32 get_queue_size(int fd)
{
	__u32 data;

	ioctl(fd, GETQUEUESIZE, &data);
	return data;
}

The following code is gpio_char.h

#ifndef GPIO_CHAR_H_
#define GPIO_CHAR_H_

/* Userspace version of kernel header file */

#include <linux/ioctl.h>

#define RTDM_CLASS_GPIO 0x80 

/* standard read/write functions */
#define DDRREAD         _IOR(RTDM_CLASS_GPIO,0,char)
#define DDRWRITE        _IOW(RTDM_CLASS_GPIO,0,char)
#define DATAREAD        _IOR(RTDM_CLASS_GPIO,1,char)
#define DATAWRITE       _IOW(RTDM_CLASS_GPIO,1,char)
#define INDEXREAD       _IOR(RTDM_CLASS_GPIO,2,char)
#define INDEXWRITE      _IOW(RTDM_CLASS_GPIO,2,char)

/* nolock functions */
#define DDRREAD_NL	_IOR(RTDM_CLASS_GPIO, 3, char)
#define DDRWRITE_NL 	_IOW(RTDM_CLASS_GPIO, 3, char)
#define DATAREAD_NL	_IOR(RTDM_CLASS_GPIO, 4, char)
#define DATAWRITE_NL	_IOW(RTDM_CLASS_GPIO, 4, char)
#define INDEXREAD_NL	_IOR(RTDM_CLASS_GPIO, 5, char)
#define INDEXWRITE_NL	_IOW(RTDM_CLASS_GPIO, 5, char)

/* lock/unlock */
#define GPIOLOCK	_IO( RTDM_CLASS_GPIO, 6)
#define GPIOUNLOCK	_IO( RTDM_CLASS_GPIO, 7)

#define DATAREADQ       _IOR(RTDM_CLASS_GPIO, 8, char)
#define GETNOTIFY       _IOR(RTDM_CLASS_GPIO, 9, char)
#define SETNOTIFY       _IOW(RTDM_CLASS_GPIO, 10, char)
#define GETQUEUESIZE    _IOR(RTDM_CLASS_GPIO, 11, char)

#endif /*GPIO_CHAR_H_*/