---
author: Sanchayan Maity
title: Adding sysfs support to a driver
tags: device-driver, sysfs
---

<p style='text-align: justify;'>A month or so back I had given an example of how to add support for a device using the platform bus framework, which as such mainly showed how exactly platform framework worked and what it was used for. In the driver I wrote, I provided access to the required values using the ioctl calls. The ioctl() calls are not a recommended way of doing this. These days drivers provide the data and control to the user space through the **sysfs** interface. As the **Linux Kernel Development** book mentions "**The sysfs file system is currently the place for implementing functionality previously reserved for ioctl() calls on device nodes or the procfs filesystem**". This post will show how to make possible what we tried to achieve in the </p>

[Write a platform device ADC driver using wm97xx codec](https://sanchayanmaity.gitlab.io/2013-12-13-how-to-write-a-platform-devicedriver-adc-driver-using-wm97xx-codec/)

<p style='text-align: justify;'>article in a much easier way using the sysfs interface. Please refer that article before reading further.</p>

<p style='text-align: justify;'>For learning about the sysfs interface and what it is, refer to the **The Linux Device Model** chapter of the **Linux Device Drivers** book or the **Devices and Modules** chapter of the **Linux Kernel Development** book by **Robert Love**.</p>

<p style='text-align: justify;'>Some other documentation can be found at the below links:</p>

1. [http://lxr.free-electrons.com/source/Documentation/driver-model/device.txt?v=3.12;a=arm](http://lxr.free-electrons.com/source/Documentation/driver-model/device.txt?v=3.12;a=arm)

2. [http://lxr.free-electrons.com/source/Documentation/filesystems/sysfs.txt](http://lxr.free-electrons.com/source/Documentation/filesystems/sysfs.txt)

<p style='text-align: justify;'>The core driver file to which we will make the changes is on the below link:</p>

[http://lxr.free-electrons.com/source/drivers/input/touchscreen/wm97xx-core.c](http://lxr.free-electrons.com/source/drivers/input/touchscreen/wm97xx-core.c)

<p style='text-align: justify;'>The below change was made to the **probe** function of the core driver file</p>

```c
if ( device_create_file(wm->dev, &dev_attr_adc_channel1) != 0 )
{
    printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel1\n");
}

if ( device_create_file(wm->dev, &dev_attr_adc_channel2) != 0 )
{
    printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel2\n");
}

if ( device_create_file(wm->dev, &dev_attr_adc_channel3) != 0 )
{
    printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel3\n");
}

if ( device_create_file(wm->dev, &dev_attr_adc_channel4) != 0 )
{
    printk(KERN_ALERT "Sysfs Attribute Creation failed for ADC Channel4\n");
}
```

<p style='text-align: justify;'>The following was added to the core driver file to access the ADC data via sysfs attributes.</p>

```c
static ssize_t adc_channel1_show(struct device *child, struct device_attribute *attr, char *buf)
{
    struct wm97xx *wm = dev_get_drvdata(child);

    return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID1));
}

static ssize_t adc_channel2_show(struct device *child, struct device_attribute *attr, char *buf)
{
    struct wm97xx *wm = dev_get_drvdata(child);

    return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID2));
}

static ssize_t adc_channel3_show(struct device *child, struct device_attribute *attr, char *buf)
{
    struct wm97xx *wm = dev_get_drvdata(child);

    return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID3));
}

static ssize_t adc_channel4_show(struct device *child, struct device_attribute *attr, char *buf)
{
    struct wm97xx *wm = dev_get_drvdata(child);

    return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, WM97XX_AUX_ID4));
}

static DEVICE_ATTR(adc_channel1, 0644, adc_channel1_show, NULL);
static DEVICE_ATTR(adc_channel2, 0644, adc_channel2_show, NULL);
static DEVICE_ATTR(adc_channel3, 0644, adc_channel3_show, NULL);
static DEVICE_ATTR(adc_channel4, 0644, adc_channel4_show, NULL);
```

<p style='text-align: justify;'>For cleanup, the attribute removal was done in the **remove** function as below.</p>

```c
device_remove_file(wm->dev, &dev_attr_adc_channel1);
device_remove_file(wm->dev, &dev_attr_adc_channel2);
device_remove_file(wm->dev, &dev_attr_adc_channel3);
device_remove_file(wm->dev, &dev_attr_adc_channel4);
```

<p style='text-align: justify;'>After doing the above changes, the device attributes which we added show up in the sysfs tree, which can be used to get the ADC values.</p>

<p style='text-align: justify;'>If you compare this to what we tried to do previously, you can see how easy it is to get the ADC values as it allows a simple command like **cat **to access the value. Of course, you could also access the value in C code as well. Things will not always be simple like this. Here we already had a core driver, which allowed us to just add the necessary changes and get what we wanted.</p>

<p style='text-align: justify;'>Check out the below file which shows how sysfs support was added for PWM. This support landed in the 3.6 version with the following commit. </p> [http://lwn.net/Articles/553755/](http://lwn.net/Articles/553755/)

[http://lxr.free-electrons.com/source/drivers/pwm/sysfs.c?v=3.12;a=arm](http://lxr.free-electrons.com/source/drivers/pwm/sysfs.c?v=3.12;a=arm)

<p style='text-align: justify;'>As such the sysfs support will be provided through the framework being used. For example, for devices like ADC, the Industrial IO framework will be used and you will have the sysfs support available through that framework.</p>

<p style='text-align: justify;'>Hopefully my earlier post and this one will give you an idea on how to add and use the sysfs interface.</p>
