Software Implementation - bogics/rpi_gpio_driver GitHub Wiki

Literature:

 

The Linux Device model

The Linux Device model is built around the concept of busses, devices and drivers.
All devices in the system are connected to a bus of some kind.
When a device in the system is found to match a driver, they are bound together.
Binding a device to a driver involves calling the driver’s probe() function passing a pointer to the device as a parameter. From this point on, it’s the responsibility of the driver to get the device properly initialized and register it with any appropriate subsystems.

  • Low-level infrastructures:
    Kobjects (struct kobject), Ksets (struct kset) and Subsystems (struct subsystem)
  • Higher levels of the Linux device model:
    Buses (struct bus_type), Devices (struct device) and Drivers (struct device_driver)
  • Top-level representation of the Linux device model, platform example:
    struct bus_type platform_bus_type , struct platform_device and struct platform_driver

 

Platform Driver registration

test_gpio is implemented as platform driver.

#ifdef CONFIG_OF
static struct of_device_id test_gpio_dt_match[] = {
	{ .compatible = "test_gpio", },
	{ },
};
MODULE_DEVICE_TABLE(of, test_gpio_dt_match);
#endif

static struct platform_driver test_gpio_driver = {
	.driver = {
		.name = "test_gpio",
		.owner = THIS_MODULE,
		.of_match_table = test_gpio_dt_match,
	},
	.probe = test_gpio_probe,
	.remove = test_gpio_remove,
};

module_platform_driver(test_gpio_driver);

As every loadable device driver is basically kernel module, moving platform driver into the kernel module is done with module_init() and module_exit() macros, which register corresponding init() and exit() functions.
When module is loaded/unloaded, init() and exit() are called which further invoke to the platform_driver_register() and platform_driver_unregister() macros respectively.
This can be shortened by using the module_platform_driver() macro.
It takes the previously instantiated struct platform_driver as an argument and makes all the magic under the hood.
As result, platform driver is registered/unregistered when the module is loaded/unloaded.

At a minimum, the probe() and remove() callbacks must be supplied; the other callbacks have to do with power management and should be provided if they are relevant.
The kernel then calls the driver's probe() function once for each device.

Accordingly, the following is present in runtime:

# ls -la /sys/bus/platform/drivers/test_gpio
total 0
drwxr-xr-x    2 root     root             0 Jan  1 05:54 .
drwxr-xr-x   24 root     root             0 Jan  1 01:12 ..
lrwxrwxrwx    1 root     root             0 Jan  1 05:54 20200000.test_gpio -> ../../../../devices/platform/soc/20200000.test_gpio
--w-------    1 root     root          4096 Jan  1 05:54 bind
lrwxrwxrwx    1 root     root             0 Jan  1 05:54 module -> ../../../../module/test_gpio
--w-------    1 root     root          4096 Jan  1 05:54 uevent
--w-------    1 root     root          4096 Jan  1 05:54 unbind

# ls -la /sys/module/test_gpio
total 0
drwxr-xr-x    7 root     root             0 Jan  1 05:54 .
drwxr-xr-x   53 root     root             0 Jan  1 00:00 ..
-r--r--r--    1 root     root          4096 Jan  1 05:54 coresize
drwxr-xr-x    2 root     root             0 Jan  1 05:54 drivers
drwxr-xr-x    2 root     root             0 Jan  1 05:54 holders
-r--r--r--    1 root     root          4096 Jan  1 05:54 initsize
-r--r--r--    1 root     root          4096 Jan  1 05:54 initstate
drwxr-xr-x    2 root     root             0 Jan  1 05:54 notes
drwxr-xr-x    2 root     root             0 Jan  1 05:54 parameters
-r--r--r--    1 root     root          4096 Jan  1 05:54 refcnt
drwxr-xr-x    2 root     root             0 Jan  1 05:54 sections
-r--r--r--    1 root     root          4096 Jan  1 05:54 srcversion
-r--r--r--    1 root     root          4096 Jan  1 05:54 taint
--w-------    1 root     root          4096 Jan  1 05:54 uevent

 

Binding Device with Driver

Gpio device is represented with structure:

struct test_gpio_dev {
	struct miscdevice miscdev;
	void __iomem *regs;
	struct device_attribute **dev_attr;
	char **sysfiles;
};

Device hardware is described in the device tree test_gpio-overlay.dts overlay.

Driver binding is performed automatically by the driver core, invoking driver probe() after finding a match between device and driver, comparing compatible = "test_gpio"; from device tree with { .compatible = "test_gpio", } from struct of_device_id.

static int test_gpio_probe(struct platform_device *pdev)
{

  • Verify that the probe was called on the relevant device:
    const struct of_device_id *match;
    match = of_match_device(test_gpio_dt_match, &pdev->dev);

  • Allocate gpio device struct test_gpio_dev memory:
    struct test_gpio_dev *gpioDev;
    gpioDev = devm_kzalloc(&pdev->dev, sizeof(struct test_gpio_dev), GFP_KERNEL);
    Allocations with automatic freeing when the corresponding device or module is removed.

  • Get the device memory range from the device tree:
    struct resource *regs;
    regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - This is physical address!!

  • map device's physical memory into the kernel virtual address space:
    Regulary, it should be done with the devm_ioremap_resource(), but it is already called from pinctrl-bcm2835 driver! devm_ioremap_resource() internally calls request_mem_region().

request_mem_region() tells the kernel that driver is going to use this range of I/O addresses, which will prevent other drivers to make any overlapping call to the same region through the request_mem_region() call.
This mechanism does not do any kind of mapping, it's a pure reservation mechanism, which relies on the fact that all kernel device drivers must be nice, and they must call request_mem_region(), check the return value, and behave properly in case of error.

As this is just an example driver, we can brake this rule and go with the low level devm_ioremap():
gpioDev->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));

  • Implementation of character driver using Misc Framework:

MISC subsistem is a thin layer above the characted driver API. It is intended for devices that really do not fit in any of the existing frameworks (input, network, video, audio, etc). Another advantage is that devices are integrated in the Device Model (device files appearing in devtmpfs, which is not the case with raw character devices).
At the end of the probe() routine, when the device is fully ready to work, the miscdevice structure is initialized for each found device:
• To get an automatically assigned minor number.
• To specify a name for the device file in devtmpfs. We propose to use devm_kasprintf(&pdev->dev, GFP_KERNEL, "test_gpio-%x", res->start). devm_kasprintf() allocates a buffer and runs ksprintf() to fill its contents.
• To pass the file operations structure prevously defined.

static const struct file_operations test_gpio_fops = {
	.owner      = THIS_MODULE,
	.write      = test_gpio_write,
	.read       = test_gpio_read
};

gpioDev->miscdev.fops = &test_gpio_fops;
gpioDev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "test_gpio-%x", regs->start);
gpioDev->miscdev.minor = MISC_DYNAMIC_MINOR;
err = misc_register(&gpioDev->miscdev);  

Accordingly, the following is present in runtime:

# ls -la /sys/class/misc/test_gpio-20200000*
lrwxrwxrwx    1 root     root             0 Jan  1 05:54 /sys/class/misc/test_gpio-20200000 -> ../../devices/virtual/misc/test_gpio-20200000

# ls -la /sys/class/misc/test_gpio-20200000/
total 0
drwxr-xr-x    3 root     root             0 Jan  1 05:54 .
drwxr-xr-x   12 root     root             0 Jan  1 01:11 ..
-r--r--r--    1 root     root          4096 Jan  1 05:54 dev
drwxr-xr-x    2 root     root             0 Jan  1 05:54 power
lrwxrwxrwx    1 root     root             0 Jan  1 05:54 subsystem -> ../../../../class/misc
-rw-r--r--    1 root     root          4096 Jan  1 05:54 uevent

# ls -la /sys/devices/virtual/misc/test_gpio-20200000
total 0
drwxr-xr-x    3 root     root             0 Jan  1 05:54 .
drwxr-xr-x   12 root     root             0 Jan  1 01:11 ..
-r--r--r--    1 root     root          4096 Jan  1 05:54 dev
drwxr-xr-x    2 root     root             0 Jan  1 05:54 power
lrwxrwxrwx    1 root     root             0 Jan  1 05:54 subsystem -> ../../../../class/misc
-rw-r--r--    1 root     root          4096 Jan  1 05:54 uevent

# ls -la /dev/test_gpio-20200000 
crw-------    1 root     root       10,  57 Jan  1 05:54 /dev/test_gpio-20200000
  • Create sysfs entries :
    Module can optionally take an argument. The argument "gpio" is array of integers which represent GPIO pins for which sysfs entries will be created, e.g:
# insmod test_gpio.ko gpio="17,26"

# ls -la /sys/devices/platform/soc/20200000.test_gpio/testgpio*
-rw-r--r--    1 root     root          4096 Jan  1 00:05 /sys/devices/platform/soc/20200000.test_gpio/testgpio17
-rw-r--r--    1 root     root          4096 Jan  1 00:05 /sys/devices/platform/soc/20200000.test_gpio/testgpio26

Implementation:

#define NUM_GPIOS 54

/* Module parameters */
static int gpio[NUM_GPIOS];
static int gpio_argc = 0;
module_param_array(gpio, int, &gpio_argc, 0644);

To create a single sysfs entry, the most optimal is to use DEVICE_ATTR macro:
static DEVICE_ATTR(testgpio, S_IWUSR | S_IRUGO, test_gpio_show, test_gpio_store);
But, as there is a need to create multiple entries, dig deeper into the DEVICE_ATTR definition and do the following:

	if (gpio_argc > 0) {
		// dinamically allocated array of device_attribute structs
		gpioDev->dev_attr = devm_kzalloc(&pdev->dev, gpio_argc * sizeof(struct device_attribute), GFP_ATOMIC);
		// dinamically allocated array of sysfs entry names
		gpioDev->sysfiles = devm_kzalloc(&pdev->dev, gpio_argc * sizeof(gpioDev->sysfiles), GFP_ATOMIC);
		for (i = 0; i < gpio_argc; i++) {
			snprintf(name, sizeof(name), "testgpio%d", gpio[i]);
			gpioDev->sysfiles[i] = devm_kzalloc(&pdev->dev, strlen(name)+1, GFP_ATOMIC);
			strcpy(gpioDev->sysfiles[i], name);

			gpioDev->dev_attr[i] = devm_kzalloc(&pdev->dev, sizeof(struct device_attribute), GFP_ATOMIC);
			gpioDev->dev_attr[i]->attr.name = gpioDev->sysfiles[i];
			gpioDev->dev_attr[i]->attr.mode = VERIFY_OCTAL_PERMISSIONS(S_IWUSR | S_IRUGO);
			gpioDev->dev_attr[i]->show	= test_gpio_show;
			gpioDev->dev_attr[i]->store	= test_gpio_store;
			device_create_file(&pdev->dev, gpioDev->dev_attr[i]);
		}
	}
  • Link gpio device with platform device:
    platform_set_drvdata(pdev, gpioDev);

  • Get IRQ number index:
    irq = platform_get_irq(pdev, 0);

  • Register interrupt handler:
    request_irq(irq, test_gpio_interrupt, IRQF_SHARED, dev_name(&pdev->dev), gpioDev);

  • Allocate an interrupt line for a managed device: devm_request_irq(&pdev->dev, irq, test_gpio_interrupt, IRQF_SHARED, "test_gpio_int", gpioDev);

}

   

static int test_gpio_remove(struct platform_device *pdev)
{

  • Remove sysfs entries:
for (i = 0; i < gpio_argc; i++)
	device_remove_file(&pdev->dev, gpioDev->dev_attr[i]);
  • Unregister Misc Device:
    misc_deregister(&gpioDev->miscdev);

  • Automatically deallocated with devm_ prefix
    No need to take care about deallocation of devm_kzalloc, devm_ioremap, devm_kasprintf

}

 

Accessing driver from userspace

Access to the character device /dev/test_gpio-20200000 via read() and write() system calls which are mapped to the test_gpio_read() and test_gpio_write() file operations.

static ssize_t test_gpio_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
static ssize_t test_gpio_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{

  • retrieve the test_gpio_dev structure:

The first thing to do is to retrieve the struct test_gpio_dev from the struct miscdevice, accessible through the private_data field of the struct file.
At the time we registered our misc device, we didn’t keep any pointer to the struct test_gpio_dev.
However, as the struct miscdevice is accessible through file->private_data, and is a member of the struct test_gpio_dev, we can use a magic container_of() macro to compute the address of the parent structure:

struct test_gpio_dev *gpioDev = container_of(file->private_data, struct test_gpio_dev, miscdev);
void __iomem *regs;, member of the struct test_gpio_dev, contains already mapped GPIO registers to the kernel virtual space, used to perform register read/write operations.

  • transfer data from/to userspace
    Get direction and value of input/output pins, format in a buffer and copy to user space:
    For exchanging data with user space, copy_to_user() and copy_from_user() to transfer buffer. For single value put_user() and get_user() could be used

}

Access to the sysfs entries via read() and write() system calls which are mapped to the test_gpio_show() and test_gpio_store() sysfs device attribute callbacks.

static ssize_t test_gpio_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t test_gpio_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{

  • retrieve the test_gpio_dev structure:
    struct test_gpio_dev *gpioDev = dev_get_drvdata(dev);

  • transfer data from/to userspace
    Direct copy from/to buff is allowed!!
    What difference is comparing to the read()/writ()e file operations of character device?? Is the buf from kernel space while transfer to the user space happens somewhere under the hood?

}