# Writing an I2C Kernel Device Driver

In this post I am going to write Linux device driver for Beaglebone Black to interface with Nunchuk Joystick and MCP9808 Temperature sensor using I2C protocol.

- Part 1. Writing Kernel Driver for MCP9808 Temperature Sensor.
- Part 2. Writing Kernel Driver for Nunchuk Joystick and using Input Subsystem

# I2C Primer

I2C is a slow two-wire protocol which was developed by Philips. This protocol provides a bus for connecting multiple types of devices with infrequent or low bandwidth communication needs. 

In Linux terminology, there are two chips: adapter chip and client chip. We need a driver for I2C adapter and another driver for I2C client/ device. 

I2C Bus core
I2C Controller Drivers
I2C Device Drivers/ Client Drivers.

# Implementation

## Device Registration in Device Tree

For this project, I am connecting MCP9808 sensor to the I2C-1 bus. The device tree structure looks like this.

```
&i2c1 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1_pins>;
    status = "okay";
    clock-frequency = <100000>;

	temp: mcp@18 {
		compatible = "microchip,mcp9808";
		reg = <0x18>;
	};
};
```

## Driver Structure

The first building block of the I2C driver for the sensor looks like this.

```
// SPDX-License-Identifier: GPL-2.0
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>


static const struct of_device_id mcp9808_of_match[] = {
    { .compatible = "microchip,mcp9808" },
    { },
};

MODULE_DEVICE_TABLE(of, mcp9808_of_match);

static struct i2c_driver mcp9808_i2c_driver = {
    .driver = {
        .name = "mcp9808_i2c",
        .owner = THIS_MODULE,
        .of_match_table = mcp9808_of_match,
    },
    .probe = mcp9808_i2c_probe,
    .remove = mcp9808_i2c_remove,
};

// A macro to define default init() and exit() functions.
module_i2c_driver(mcp9808_i2c_driver);

MODULE_AUTHOR("John Doe <john.doe@gmail.com");
MODULE_DESCRIPTION("Basic I2C Temperature driver");
MODULE_LICENSE("GPL");
```

This structure is typically used in other kinds of device drivers. Here we define the name of the driver, its compatibility with the device tree structure, the `probing` and the `remove` function called during initialisation and unregistration of the device.

### `probe()`

We can specify the steps to initialise the device in this function. In order to initialise and register MCP9808 device, we are going to execute these steps:

1. Create a device node each device.
2. Initialise some registers.
3. Setup device-specific data. 

```
static int mcp9808_i2c_probe(struct i2c_client *client,
                              const struct i2c_device_id *id)
{   
    pr_info("mcp9808_i2c_probe is called.\n");

    int error;
    u8 inp[2];
    error = mcp9808_read_registers(client, 0x06, inp, sizeof(inp));
    if (error == 0)
    {
        pr_info("MCP9808 Manufacture ID = %x\n", inp[0] << 8 | inp[1]);
    }

    struct mcp9808_dev *mcp_dev;
	mcp_dev = devm_kzalloc(&client->dev, sizeof(*mcp_dev), GFP_KERNEL);
	if (!mcp_dev)
		return -ENOMEM;
        
    mcp_dev->client = client;

    pr_info("Set private data serial\n");
	i2c_set_clientdata(client, mcp_dev);

    return 0;

}
```

### `remove()`

This function is called when the driver is being unregistered.

```
static int mcp9808_i2c_remove(struct i2c_client *client)
{
    pr_info("mcp9808_i2c_remove is called.\n");
    return 0;
}
```

## Extra Client Data

The user/ client should be able to read the sensor value from the user space. In order to do that, we have to implement a function to read the sensor value to send the value back to users. To simplify implementation, I am going to use Misc subsystem API to register the device and expose `read()` and `ioctl()` operation.

The device registration using Misc subsystem API can be done in the `probe()` function.

```

static const struct file_operations fops = {
	.owner = THIS_MODULE,
	.read  = mcp9808_read
};

static int mcp9808_i2c_probe(struct i2c_client *client,
                              const struct i2c_device_id *id)
{   
    ...
    char *name;
    name = devm_kasprintf(&client->dev, GFP_KERNEL, "mcp9808-%x", (inp[0] << 8 | inp[1]));

    mcp_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
    mcp_dev->miscdev.name = name;
    mcp_dev->miscdev.fops = &fops;
    mcp_dev->miscdev.parent = &client->dev;

    ...
    int ret;
    ret = misc_register(&mcp_dev->miscdev);
	pr_info("Call misc_register %d n", ret);

}

static int mcp9808_i2c_remove(struct i2c_client *client)
{
    pr_info("mcp9808_i2c_remove is called.\n");

    // Unregister the device
    struct mcp9808_dev *sdev = i2c_get_clientdata(client);
	dev_info(sdev->miscdev.this_device, "Unregistering device\n");
	misc_deregister(&sdev->miscdev);

    return 0;
}

```

### Receiving Data using I2C Communication

It is time to implement the `read()` function. Inside this function, we will read the register to get the temperature value. The following I2C APIs can be used to write and read registers.

```
int i2c_master_send()
int i2c_master_recv()

// A helper function
int i2c_transfer()
```

```
static int mcp9808_read_registers(struct i2c_client *client,
                                  unsigned char reg_addr,
                                  u8* inp, size_t size)
{
    int error;
    struct i2c_msg msg[2];

    msg[0].addr = client->addr;
    msg[0].flags = 0;
    msg[0].len = 1;
    msg[0].buf = &reg_addr;

    msg[1].addr = client->addr;
    msg[1].flags = I2C_M_RD;
    msg[1].len = size;
    msg[1].buf = inp;

    error = i2c_transfer(client->adapter, msg, 2);
    pr_info("i2c_transfer error: %d\n", error);
    if (error >= 0) 
    {
        return error;
    }

    return error;
}

ssize_t mcp9808_read(struct file *f, char __user *buf, size_t sz, loff_t *off)
{
    struct mcp9808_dev *sdev = container_of(f->private_data, struct mcp9808_dev, miscdev);
    if (sdev == NULL) 
    {
        pr_info("Empty sdev\n");
        return -1;
    }

    if (sdev->client == NULL)
    {
        pr_info("client is NULL\n");
        return -1;
    }

	int error;
    u8 inp[2];
    error = mcp9808_read_registers(sdev->client, 0x05, inp, sizeof(inp));
    if (error >= 0)
    {
        int temperature = ((inp[0] & 0x0f) << 4) + (inp[1] >> 4);
        pr_info("Temperature = %d.%d oC\n", temperature, inp[1] & 0x0f);
    }

	int nbytes = 2 - copy_to_user(buf, inp, 2);
	*off = nbytes;

	pr_info("Reading function, nbytes=%d, pos=%d, sz=%d\n", nbytes, (int) *off, sz);

	return 0;
}

```

# Testing 

Compile the kernel device driver and load it on the device

```
# insmod temperature.ko

# ls /dev/mcp9808-54
/dev/mcp9808-54

# cat /dev/mcp9808-54
[ 6139.115536] i2c_transfer error: 2
[ 6139.118895] Temperature = 25.7 oC
[ 6139.122228] Reading function, nbytes=2, pos=2, sz=4096

# cat /dev/mcp9808-54 
[ 6139.754094] i2c_transfer error: 2
[ 6139.757453] Temperature = 24.14 oC
[ 6139.760871] Reading function, nbytes=2, pos=2, sz=4096

```
