Linux Device Driver Development 笔记

Linux Device Driver Development的笔记, 和LDD3有点区别, 这本比较偏应用. 看完了, 慢慢补笔记. 可惜没讲Block Device.

 

Linux Driver Development

Intro & Basis

内核配置和编译

配置: make ARCH=<arch> menuconfig/<defconfig>
编译: make -j<cores-1> ARCH=<arch> CROSS_COMPILE=<gcc_prefix>
lint: script/gen_compile_commands.py
编译模块: make modules -j<cores-1> ARCH=<arch> CROSS_COMPILE=<gcc_prefix> && make INSTALL_MOD_PATH=out modules_install

Hello World

helloworld.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int __init helloworld_init(void){
printk("Hello World: init");
};
static void __exit helloworld_exit(void){
printk("Hello World: exit");
}
module_init(helloworld_init);
module_exit(helloworld_exit);
MODULE_AUTHOR("IgaYuka <chino@igayuka.moe>")
MODULE_DESCRIPTION("Hello, world!");
MODULE_LICENSE("GPL")

makefile
1
2
3
4
5
obj-m := helloworld.o
KERNELDIR ?= ../torvalds/linux
all default: modules
modules help clean:
$(MAKE) -c $(KERNELDIR) M=$(shell pwd) $@

take a look at include/uapi/asm-generic/errno-base.h
声明: EXPORT_SYMBOL()
参数: module_param(name, type, perm);

Kernel Facilities and Helper Functions

Useful Macros

container_of(pointer, container_type, container_field);: 通过成员指针取得结构体指针

Linked lists

双向链表. #include <linux/list.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct list_head {struct list_head *next, *prev;};   
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
#define LIST_HEAD_INIT(name) {&name, &name}
void INIT_LIST_HEAD(struct list_head *list)
void list_add(struct list_head *new, struct list_head *head)
void list_del(struct list_head *entry)
void list_replace(struct list_head *old, struct list_head* new)
void list_swap(struct list_head *entry1, struct list_head* entry2)
void list_move(struct list_head *list, struct list_head *head)
#define list_for_each_entry(pos, head, member) for(...)
```
__other__: use list_ to trigger lint.
__note__: frequently used with container of. `INIT_LIST_HEAD`, `list_add`, `list_del`, `list_for_each_entry` are important function.
### Wait queue
等待链 `#include <linux/wait.h>`
经常用于iowait, 被唤醒且条件为真时继续.
``` c++
struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
// Static declaration:
#define DECLARE_WAIT_QUEUE_HEAD(name) \
struct wait_queue_head name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
// Dynamic declaration:
wait_queue_head_t my_wait_queue;
init_waitqueue_head(&my_wait_queue);
// Blocking: block the current task (process) in the wait queue if CONDITION is false
int wait_event_interruptible(wait_queue_head_t q, CONDITION)
// Unblocking:
void wake_up_interruptible(wait_queue_head_t *q);

Delay and timer

Standard Timer

#include <linux/timer.h>
标准定时器用内核时间单位jiffy定义时间. 32位系统默认使用32位jiffies, 可通过jiffies_64访问64位jiffies. 64位系统默认使用64位jiffies.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct timer_list {
struct list_head entry;
unsigned long expires;
struct tvec_t_base_s *base;
void (*function)(unsigned long);
unsigned long data;
);
// Setting up the timer:
void setup_timer( struct timer_list *timer, void (*function)(unsigned long), unsigned long data);
// Setting the expiration time:
int mod_timer( struct timer_list *timer, unsigned long expires);
// Releasing the timer:
void del_timer(struct timer_list *timer);
int del_timer_sync(struct timer_list *timer);

note: expires经常使用jiffies + msecs_to_jiffies(<ms>)这种形式.
del_timer_sync用于等待定时器停止.
mod_timer 可以在回调中重新激活自身的Timer.
回调函数的unsigned long实际类型为struct timer_list*

High Resolution timers

#include <linux/hrtimer.h>, CONFIG_HIGH_RES_TIMERS=y
高分辨率时钟.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct hrtimer {
struct timerqueue_node node;
ktime_t _softexpires;
enum hrtimer_restart (*function)(struct hrtimer *);
struct hrtimer_clock_base *base;
uint8_t state;
uint8_t is_rel;
uint8_t is_soft;
uint8_t is_hard;
};
enum hrtimer_mode{}; // linux/hrtimer.h:39
/* Initializing the hrtimer */
void hrtimer_init( struct hrtimer *time, clockid_t which_clock, enum hrtimer_mode mode);
/* Starting hrtimer */
int hrtimer_start( struct hrtimer *timer, ktime_t time, const enum hrtimer_mode mode);
/* hrtimer cancellation */
int hrtimer_cancel( struct hrtimer *timer);
int hrtimer_try_to_cancel(struct hrtimer *timer);
/* check hrtimer running */
int hrtimer_callback_running(struct hrtimer *timer);

attention: HRT API是GPL协议的, 需要声明模块协议为GPL. 想规避的话还是自己写定时器驱动好一点.
note: absolute time会随系统时间变化而变化.
回调函数实际返回类型是enum hrtimer_restart, 只有两个值HRTIMER_NORESTART, HRTIMER_RESTART.

Delay

延时, 依靠了上面的两个定时器.
ndelay, udelay, mdelay.
一般用udelay. ndelay, mdelay一般精度不够.
udelay一般用于10us以下的延时. 循环延时.
usleep_range一般用于10us~20us的延时. 依靠hrt延时
msleep一般用于10ms以上的延时. 依靠标准定时器延时.

Kernel locking machanism

Mutex

互斥锁. #include <linux/mutex.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
struct mutex {
atomic_long_t owner;
spinlock_t wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
struct list_head wait_list;
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};
// Statically Declare
#define DEFINE_MUTEX(mutexname) \
struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
// Dynamically Declare
struct mutex my_mutex;
mutex_init(&my_mutex);
#define mutex_init(mutex) \
do { \
static struct lock_class_key __key; \
__mutex_init((mutex), #mutex, &__key); \
} while (0)
// Lock
void mutex_lock(struct mutex *lock);
int mutex_lock_interruptible(struct mutex *lock);
int mutex_lock_killable(struct mutex *lock);
// Unlock
void mutex_unlock(struct mutex *lock);
// check
int mutex_is_locked(struct mutex *lock);
// try
int mutex_trylock(struct mutex *lock);

note: mutex_lock, mutex_trylock在等待时不中断程序运行, 相当于自旋锁.
mutex_lock_interruptable在等待时可以被炒作系统中断
mutex_lock_killable只在收到信号时中断.
mutex_unlock不能在中断中使用.

Spinlock

自旋锁. #include <linux/spinlock.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spinlock_t my_spinlock;
spin_lock_init(my_spinlock);
// lock
#define spin_lock(lock) _spin_lock(lock)
// unlock
#define spin_unlock(lock) _spin_unlock(lock)
// lock during irq
#define spin_lock_irqsave(lock, flags) \
do { \
raw_spin_lock_irqsave(spinlock_check(lock), flags); \
} while (0)
// unlock during irq
#define spin_lock_irqsave(lock, flags) \
do { \
raw_spin_lock_irqsave(spinlock_check(lock), flags); \
} while (0)

note: do{...}while(0)可以使宏不管怎么用总是正确的(主要是简化的if).
暂时没搞明白为什么要#define和__always_inline混用.
自旋锁保持CPU占用.
ps: 搞错了(x, __always_inline定义原型函数.

versus

各有优点.
互斥锁保护关键资源, 自旋锁保护IRQ关键部分.
互斥锁可以进入sleep状态, 自旋锁保持CPU占用.
不可以长时间持有自旋锁. 但是可以长时间持有互斥锁.

Softirq

软中断. 很少直接使用. 一般只有网络设备和块设备直接使用softirq.
仅用于非常快速的处理.

Kernel Task Management

Tasklets

tasklets经常用于bottom-half, 依靠softirqs进行工作.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
// Dynamically Declare
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
// Statically Declare
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
// enable a tasklet
void tasklet_enable(struct tasklet_struct *);
// disable a tasklet
void tasklet_disable(struct tasklet_struct *);
void tasklet_disable_nosync(struct tasklet_struct *);
// scheduling
void tasklet_schedule(struct tasklet_struct *t);
void tasklet_hi_schedule(struct tasklet_struct *t);
// stopping
void tasklet_kill(struct tasklet_struct *t);

note: tasklet_disable等待tasklet终止执行. tasklet_disable_nosync不等待tasklet终止执行.
tasklet_hi_schedule创建的任务优先级较高.

work queues

任务队列. #include <linux/workqueue.h>
使用系统预定义的队列struct workqueue_struct *system_wq __read_mostly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
struct delayed_work {
struct work_struct work;
struct timer_list timer;

/* target workqueue and CPU ->timer uses to queue ->work */
struct workqueue_struct *wq;
int cpu;
};
// ties the work on the current CPU
int schedule_work(struct work_struct *work);
// same but delayed function
static inline bool schedule_delayed_work(struct delayed_work *dwork,
unsigned long delay)
// actually schedules the work on a given CPU
int schedule_work_on(int cpu, struct work_struct *work);
// same but delayed function
int scheduled_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay)
// cancle work
extern bool cancel_work_sync(struct work_struct *work);
extern bool cancel_delayed_work(struct delayed_work *dwork);
extern bool cancel_delayed_work_sync(struct delayed_work *dwork);
// flush work
extern bool flush_work(struct work_struct *work);

自建队列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* declare work and work queue */
struct workqueue_struct *myqueue;
struct work_struct thework;
// Define the worker function (the handler):
void dowork(void *data) { /* Code goes here */ };
// Initialize our work queue and embed our work into:
myqueue = create_singlethread_workqueue( "mywork" );
INIT_WORK( &thework, dowork, <data-pointer> );
// Scheduling work
queue_work(myqueue, &thework);
queue_dalayed_work(myqueue, &thework, <delay>);
// Wait on all pending work on the given work queue:
void flush_workqueue(struct workqueue_struct *wq)
// Cleanup
int cancel_work_sync(struct work_struct *work);
int cancel_delayed_work_sync(struct delayed_work *dwork);
// destory
extern void destroy_workqueue(struct workqueue_struct *wq);

自建队列与系统预定义队列操作的差异:
|预定义队列|自建队列|ps|
|:——-:|:——:|:-:|
schedule_work(w)|queue_work(keventd_wq,w)|on current CPU
schedule_delayed_work(w,d)|queue_delayed_work(keventd_wq,w,d)|on any CPU
schedule_delayed_work_on(cpu,w,d)|queue_delayed_work(keventd_wq,w,d)|on a given CPU
flush_scheduled_work()|flush_workqueue(keventd_wq)|
note: delay使用的时间单位为jiffy

Kernel interruption mechanism

interrupt basis

#include <linux/interrupt.h>
take a look at linux/interrupt.h:38 / IRQF_*

1
2
3
4
5
6
// Registering an interrupt handler
typedef irqreturn_t (*irq_handler_t)(int, void *);
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev)
// Unregist
void free_irq(unsigned int irq, void *dev)

Character Devices

字符设备, 在/dev目录下建立文件, 通过文件读写与驱动交互.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#define MINORBITS       20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};

struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
int (*flock) (struct file *, int, struct file_lock *);
[...]
};

struct inode {
struct pipe_inode_info *i_pipe; /* Set and used if this is a
*linux kernel pipe */
struct block_device *i_bdev; /* Set and used if this is a
* a block device */
struct cdev *i_cdev; /* Set and used if this is a
* character device */
[...]
}

struct file {
struct path f_path; /* Path to the file */
struct inode *f_inode; /* inode associated to this file */
const struct file_operations *f_op;/* operations that can be
* performed on this file
*/
loff_t f_pos; /* Position of the cursor in
* this file */
/* needed for tty driver, and maybe others */
void *private_data; /* private data that driver can set
* in order to share some data between file
* operations. This can point to any data
* structure.
*/
[...]
}

// Static device number allocation
int register_chrdev_region(dev_t first, unsigned int count, char *name);
// Dynamic device number allocation
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
// create class using class name
#define class_create(owner, name) \
({ static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
// init character device
void cdev_init(struct cdev *, const struct file_operations *);
// make the device live for the users to access
int cdev_add(struct cdev *, dev_t, unsigned);
// create a device node
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

appendix: struct file_operations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iopoll)(struct kiocb *kiocb, bool spin);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, u
nsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t
, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t,
unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags);
int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;

file_operation 结构是一个字符驱动如何建立这个连接.
struct module *owner, 指向拥有该文件的模块指针.
FLAGS: FOPS

Platform Devices

driver / device

当Driver和Device同时在总线注册时会调用Driver的Probe函数. Device可以由设备树给出, 也可以由ACPI给出(热插拔).

OF Style / ACPI style

OF匹配表一般用于设备树固定与板子上的设备. ACPI匹配表一般用于热插拔设备.

Device Tree Basis

暂时没想好写什么(x

I2C Client

1
2
3
4
5
6
7
8
9
struct i2c_driver {
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
struct device_driver driver;
const struct i2c_device_id *id_table;
};

note: OF表一个probe就够了. ACPI表一般提供remove用于清理.
client一般由I2C驱动程序给出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
intirq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};

设备独有数据

1
2
3
4
/* set the data */
void i2c_set_clientdata(struct i2c_client *client, void *data);
/* get the data */
void *i2c_get_clientdata(const struct i2c_client *client);

i2c驱动声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct i2c_device_id {
char name[I2C_NAME_SIZE];
kernel_ulong_t driver_data; /* Data private to the driver */
};
static struct i2c_device_id foo_idtable[] = {
{ "foo", my_id_for_foo },
{ "bar", my_id_for_bar },
{ }
};
MODULE_DEVICE_TABLE(i2c, foo_idtable);
static struct i2c_driver foo_driver = {
.driver = {
.name = "foo",
},
.id_table = foo_idtable,
.probe = foo_probe,
.remove = foo_remove,
}
module_i2c_driver(foo_driver);

note: MODULE_DEVICE_TABLE最后一项必须为空标识匹配表结束.

通信

1
2
3
4
5
6
7
8
9
10
11
/* basic */
int i2c_master_send(struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(struct i2c_client *client, char *buf, int count);
/* combine */
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags; /* Message flags */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);

SMBus(System Management Bus) 兼容性

SMBus兼容I2C设备, SMBus是由Intel开发的.

1
2
3
4
5
6
s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value);
s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value);
s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command, u8 *values);
s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command, u8 length, const u8 *values);

board configuration file

过时了.

设备树I2C节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
&i2c2 { /* Phandle of the bus node */
pcf8523: rtc@68 {
compatible = "nxp,pcf8523";
reg = <0x68>;
};
eeprom: ee24lc512@55 { /* eeprom device */
compatible = "packt,ee24lc512";
reg = <0x55>;
};
}
static const struct of_device_id foobar_of_match[] = {
{ .compatible = "packtpub,foobar-device" },
{}
};
MODULE_DEVICE_TABLE(of, foobar_of_match);
static struct i2c_driver foo_driver = {
.driver = {
.name = "foo",
.of_match_table = of_match_ptr(foobar_of_match), /* Only this line is added */
},
.probe = foo_probe,
.id_table = foo_id,
};

OF, ID表不是必须的, 但是保险起见还是两个一起写比较好(指的同型号厂商不同

SPI Device

Regmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  struct regmap_config {
const char *name;
int reg_bits;
int reg_stride;
int pad_bits;
int val_bits;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
int (*reg_read)(void *context, unsigned int reg,
unsigned int *val);
int (*reg_write)(void *context, unsigned int reg,
unsigned int val);
bool fast_io;
unsigned int max_register;
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
u8 read_flag_mask;
u8 write_flag_mask;
bool use_single_rw;
bool can_multi_write;
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges;
unsigned int num_ranges;
}

Industural IO Framework

Kernel Memory Management

Direct Memory Access

Linux Device Model

Pin Control & GPIO Subsystem & GPIO Controller Drivers

Advanced IRQ Management

MISC Device Drivers Framework

Input Device

RTC

PWM

Regulator / Power Management

FB: Framebuffer

NIC: Network Interface Card

Summary