Pi IoT In Python Using Linux Drivers - GPIO Using Ioct
Written by Mike James & Harry Fairhead   
Monday, 29 July 2024
Article Index
Pi IoT In Python Using Linux Drivers - GPIO Using Ioct
The struct module
GPIO Output
The complete program

The GPIO driver in Linux used to be GPIO sysfs, but no longer. Now it's the GPIO character driver. Find out how to do low-level GPIO control using ioct, even if you are using a Pi 5.

This content comes from the second edition of our book and includes how to make it work on the Pi 5 :

Raspberry Pi IoT In PythonUsing Linux Drivers
Second Edition

By Harry Fairhead & Mike James

DriverPython2e360

Buy from Amazon.

Contents

  1.  Choosing A Pi For IoT
  2.  Getting Started With Python
  3.   Drivers: A First Program
  4.  The GPIO Character Driver 
  5.  GPIO Using Ioct ***NEW!!
  6.  GPIO Events
  7.  The Device Tree
       Extract: The DHT22
  8.  Some Electronics
  9.  Pulse Width Modulation
       Extract: PWM *
  10. SPI Devices
  11. I2C Basics
       Extract: I2C *
  12. The I2C Linux Driver
  13. Advanced I2C
  14. Sensor Drivers
  15. 1-Wire Bus
       Extract 1-Wire And The DS18B20 *
  16. Going Further With Drivers
  17. Appendix I

*From the first edition waiting for update.

 <ASIN:B0CT46R6LF>

GPIO Using I/O Control

The GPIOD library is fairly easy to use, but so is the underlying ioctl, input/output control, interface. The main reason for wanting to use it is to find out how it actually works and perhaps gain access to features that the Python bindings don’t expose. Overall this approach is a little slower than bindings, but it is worth seeing that even the GPIO driver behaves like a character file.

Using ioctl In Python

The ioctl call is a Linux function that allows you to interact and configure character-based device drivers such as gpiod. The call varies according to the device it is being used with.

Its general form is:

ioctl(fd, request, pstruct)

where fd is a Linux file descriptor for the device being used and request is a variable that specifies the request or operation to be performed. The third parameter is usually a pointer to a struct or an area of memory. Some ioctl calls have additional parameters.

The request number was never formalized and driver writers tended to invent their own, but there was an impromptu move to apply an organization and many request numbers follow a regular pattern. The most recent scheme uses two direction bits (00: none, 01: write, 10: read, 11: read/write) followed by 14 size bits (giving the size of the argument), followed by an 8-bit type (collecting the ioctls in groups for a common purpose or a common driver), and an 8-bit serial number.

You can mostly ignore all of this - if you know the request number, you can simply use it in the ioctl call.

So far the description of the ioctl call has been in terms of how it is expressed in C, but Python has a module that lets you make an ioctl call and it is based on the underlying C ioctl call.

The fcntl module provides a fctnl object which has an ioctl method. You can use the fcntl object to make fcntl calls to interact with files but in this case it is the ioctl method that we need to use.

fcntl.ioctl(fd, request, arg=0, mutate_flag=True) 

You can see that this is similar to the Linux ioctl call. The first parameter can be a file descriptor or it can be an object that provides a file descriptor, for example a Python file object. The request parameter is a 32-bit integer set to the request code. The arg parameter is the most complex and the one that you will usually struggle to get right the first time you use ioctl for any particular task. It can be a simple integer, but in most cases it is an object that can supply a buffer of bytes – a bytes object or a bytearray. This is Python’s stand-in for a C struct and when the call is made the bytes are copied into a temporary C struct and the result of the call is, by default, copied back to the Python object if this is possible, i.e. the object is writable. The final parameter mutate_flag can be set to false to stop the copying of the result back to the Python object.

In this chapter we are going to use the struct module to create a bytearray, but another way to do the same job, the ctypes module, is described in Chapters 13 and 15. While this is arguably easier to use, it is not quite as efficient so you should know how to use both.

As already mentioned, the biggest problem with using the ioctl call is in working out how the C struct can be represented in Python. This is often easier than it initially looks because of the struct module which provides a way of converting a C struct definition into an equivalent Python object. The basic idea is that a struct is just a sequence of bytes that have been initialized correctly to hold the type of data they are supposed to hold. For example, consider this in C:

struct gpiochip_info {
	char name[32];
	char label[32];
	__u32 lines;
};

Even if you don’t know C, you can probably work out that the struct consists of a name of 32 characters, i.e. 32 bytes, a label of 32 characters, i.e. another 32 bytes and an unsigned 32-bit integer, i.e. 4 bytes. This struct is equivalent to a buffer of 32+32+4, i.e. 68 bytes. The only problem is that when you pass it to the ioctl call the first 32 bytes have to contain bytes that represent a C string, followed by another 32 bytes of string data and finally 4 bytes which represent an unsigned integer value.



Last Updated ( Tuesday, 30 July 2024 )