---
# System prepended metadata

title: Character Device Driver
tags: [device driver, rp3]

---

# Character Device Driver
###### tags: `device driver` `rp3`
* major and minor
* Allocating and registering a character device
* device file operations
* Writing file operations
    * Exchanging data between kernel space and user space
        * get_user
        * put_user
        * copy_from_user
        * copy_to_user
    * The open method
    * The release method
    * The write method
    * The read method
    * The llseek method
    * The poll method
    * The ioctl method
* Remove a Characteer Device Driver()
    *  class_destroy(gpio_class);  // 取消class
    * cdev_del(gpio_cdev);  // 移除cdev
    * unregister_chrdev_region(driverno, ARRAY_SIZE(leds));  // 解登錄driverno
## Expected Achievemet Goal
e.g: 
* 完成一個+1的character device driver
* 控制LED的character device driver
* user space status  diagmram 的character device driver
* 一個可以執行中斷.pooling 的character device driver

## 概念解析
字符設備是3大類設備(字符設備、塊設備和網絡設備)中的一類,其驅動程序完成的主要工作是初始化添加和刪除cdev結構體,申請和釋放設備號,以及填充`file_operations`結構體中的操作函數,來實現file_operations結構體中的`read()`、`write()`和`ioctl()`等函數是驅動設計的主體工作。
而註冊character deivce driver主要分成兩類
#### (a) Static Regist-[lxr code](https://elixir.bootlin.com/linux/latest/source/fs/char_dev.c#L235)
```clike=
int register_chrdev_region(dev_t first, \
                           unsigned int count, \
                           char *name);
```
#### (b) Dynamic Regist-[lxr code](https://elixir.bootlin.com/linux/latest/source/fs/char_dev.c#L235)-RECOMMEND
* flow of character device driver dynamic register
    * Suggestion let the Kernel do this Job
```clike=
int alloc_chrdev_region(dev_t *dev, \
                        unsigned int firstminor, \
                        unsigned int count, \
                        char *name);
```
## Major && Minor

you may have a minor and a major, and need to build a dev_t . 
The macro you should use is `MKDEV(int major, int minor)`
## 

## Allocating and registering a character device
1. Reserve a major and a range of minors with `alloc_chrdev_region()`.
2. Create a class for your devices with `class_create()`, visible in `/sys/class/` .
3. Set up a struct file_operation (to be given to cdev_init ), and for each device you need to create, call `cdev_init()` and `cdev_add()` to register the device.
4. Then, create a `device_create()` for each device with a proper name. It will result in your device being created in the `/dev` directory:

### alloc_chrdev_region()
register a range of char device numbers
```c=
int alloc_chrdev_region(dev_t *dev, \
                        unsigned int firstminor, \
                        unsigned int count, \
                        char *name);
```
* dev: output parameter for first assigned number
* baseminor: first of the requested range of minor numbers
* count: the number of minor numbers required (how many device)
* name: the name of the associated device or driver
### `cdev_init`
```c=
/* visible /sys/class 
 @cdev: the structure to initialize
 @fops: the file_operations for this device
 
 Initializes @cdev, remembering @fops, making it ready to add to the
 system with cdev_add().
*/
void cdev_init (  struct cdev * cdev,const struct file_operations * fops);
```
### `cdev_add()`
```c=
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
```
### `device_create()`
```c=
device_create(struct class *cls, struct device *parent, dev_t devt,
	      void *drvdata, const char *fmt, ...);
```
### `class_create()`
```c=

```

## File Operations of Character drivers
Linux一切皆文件，那麼作為一個設備文件，它的操作方法介面封裝在struct file_operations，當我們寫一個驅動的時候，一定要實現相應的介面，這樣才能使這個驅動可用，Linux的內核中大量使用"註冊+回調"機制進行驅動程式的編寫，所謂註冊回調，簡單的理解，就是當我們open一個設備文件的時候，其實是通過VFS找到相應的inode，並執行此前創建這個設備文件時註冊在inode中的open函數，其他函數也是如此，所以，為了讓我們寫的驅動能夠正常的被應用程式操作，首先要做的就是實現相應的方法，然後再創建相應的設備文件。



字元裝置,所以對該檔案的操作通過open,write,ioctl等函式,所以要把這個函式和底層的操作函式對應起來,這就要用到file_operation這個結構體,來宣告:
```cpp=
static const struct file_operations dynamic_fops = {
    .owner      = THIS_MODULE,
    .open       = dev_open,
    .release    = dev_release,
    .unlocked_ioctl = dev_ioctl,
};
```
* open
* read
* write
* release
* The llseek method
* The poll method
* The ioctl method
* #### lock(Kernel 2.6)
    * unlocked_ioctl
    * compatible_ioctl
### Dummy Node
#### [290312.423090] dummy_char major number = 240
#### [290312.424638] dummy char module loaded



## Tracing Code
在Linux Kernel中character device driver 使用cdev 這個結構(structure)進行並且登記系統呼叫handler(fops)。

我們可以從hello world device diver來延伸
#### Module Intialization
* applied device number (申請設備號)
* register `cdev`
* 
* alloc_chrdev_region
* device_create
```clike=
device_create(struct class *cls,  \ 
              struct device *parent,  \
              dev_t devt,  \
              void *drvdata, \
              const char *fmt, ...);
```

* e.g:
    ```clike=
    device_create(dynamic_class, NULL, MKDEV(dev_major, 0), NULL,"dyn_ch_dev");
    ```

#### Module Exit
* release device number (釋放設備號)
* unregister `cdev`
* major number: 每一個Device file都有自己一個獨一無二的major number。
* minor number: 驅動程式自己管理的編號，kernel完全不過問，如果驅動程式同時控制多個裝置的話，需要用到minor number來區分不同的裝置。
    * e.g: chdev1, chdev2, chdev3

* Cdev structure
    * 需要註冊



## LAB
* `echo`
* `cat`



## Code Function
主要分成三個部份

### Intialize

* #### alloc_chrdev_region
```clike=
alloc_chrdev_region()
```
* #### cdev_init
* #### cdev add
    * a pointer point to
    * a parameter with "dev_t"
    * Unsigned
```clike=
int cdev_add(struct cdev *, dev_t, unsigned);
```
* #### cdev initialization
    * (a) struct cdev *
    * (b) const struct file_operations *
### Exit module exit(Unload Module)
 
* 使用 marco `MKDEV`合成
```clike=
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))
```
* 使用 marco `MAJOR`獲得主設備號(major device number)
```clike=
#define MAJOR(dev)      ((unsigned int) ((dev) >> MINORBITS))
```


* delete the device driver -> chdev_del
* unregister chr devregion->unregister_chrdev_region
```clike=
dev_t chrdev = MKDEV(chrdev_major, 
cdev_del(&chrdev_cdev);
unregister_chrdev_region(chrdev, 
```


## Reference
* [ch0 && ch1 device driver](https://embetronicx.com/tutorials/linux/device-drivers/cdev-structure-and-file-operations-of-character-drivers/)
    * echo
    * head
    * cat
    * head

* [most_cdev.c](https://elixir.bootlin.com/linux/latest/source/drivers/most/most_cdev.c#L501)
* [Dummy Device Driver](https://embetronicx.com/tutorials/linux/device-drivers/cdev-structure-and-file-operations-of-character-drivers/)
* [Linux Kernel Character device driver](https://linux-kernel-labs.github.io/master/labs/device_drivers.html)
* [用樹莓派選寫驅動程式](https://jasonblog.github.io/note/raspberry_pi/yong_raspberry_pi_xie_qu_dong_cheng_shi_--_fan_li_.html)
* [字元裝置驅動程式](https://www.itread01.com/content/1548639364.html)
* [Pi GPIO Kenel Module](https://github.com/frobino/kernel-modules-raspi)