Yume 練功地

偷偷練

Linux Driver Book 筆記

Linux Driver Book 筆記

Ch 2 Module

modprobe : 只會 search 已安裝於 /lib/modules

Symbol

EXPORT_SYMBOL(name) & EXPORT_SYMBOL_GPL(name)

當時行程

1
2
3
4
5
6
// <linux/sched.h>
struct task_struct{};
// <asm/current.h>
struct task_struct *current;
current->comm // 執行檔 Base Name (abc.o -> abc)
current->pid

module 參數

1
2
3
4
5
static char* whom = "world";
module_param(whom, charp, S_IRUGO);

// <linux/stat.h>
// S_IRUGO
Kernel Type C Type
bool int
invboll 相反邏輯
charp cahr *
int int
long long
short short
uint uint
ulong ulong
ushort ushort
1
insmod x.ko whom="mom"

User-Space VS Kernel-Space Driver

See P38

Module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <linux/init.h>
module_init(init_function);
module_exit(cleanup_function);

// 初始用,初始化完畢後會被回收(存放在特殊的 ELF section)
__init
__initdata

__exit
__exitdata

// hotplug,若 kernel 設定為不支援 hotplug 的話,同 __init
__devinit
__devinitdata

CH 3 Character Device

Major & Minor Number

1
2
3
ls -l /dev
crw-rw-rw- 1 root wheel 18, 3 6 10 18:44 cu.Yume-WirelessiAP-1
brw-r----- 1 root operator 1, 0 6 10 18:44 disk0

cu.Yume-WirelessiAP-1

  • Major: 18
  • Minor: 3
  • Character device

disk0

  • Major: 1
  • Minor: 0
  • Block device
1
2
3
4
5
6
7
8
9
//<linux/types.h>
// 32 bit
// 前 12 bit Major
// 後 20 bit Minor
dev_t

int MAJOR(dev_t dev);
int MINOR(dev_t dev);
dev_t MKDEV(int majotr, int minor);

裝置編號的配置與釋放

1
2
3
//<linux/fs.h>
int register_chrdev_region(dev_t first, unsigned int count, char *name);
void unregister_chrdev_region(dev_t first, unsigned int count);
可以觀看各個驅動程式的 Major
1
2
3
4
cat /proc/devices

Character Devices:
1 mem
rc.local
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/sh
module="scull"
device="scull"
mode="644"

/sbin/insmod ./$module.ko $* || exit 1

rm -f /dev/${device}[0-3]

major=$(awk "\$2 = = \$module\" {print \$1}" /proc/devices)

mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3

group="staff"
grep -q '^staff:' /etc/group || group="wheel"
chgrp $group /dev/${device}[0-3]
chmod $mod /dev/${device}[0-3]

重要資料結構

<linux/fs.h>

struct file_operations

Kernel System Call When NULL 用途
struct module *owner; 通常是 THIS_MODULE
- - - -
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); read() -EINVAL
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); write() -EINVAL
- - - -
int (*open) (struct inode *, struct file *); open() 可 NULL kernel 不會發現檔案被打開
int (*release) (struct inode *, struct file *); 可 NULL kernel 不會發現檔案被 release
- - - -
loff_t (*llseek) (struct file *, loff_t, int);
- - - -
int (*mmap) (struct file *, struct vm_area_struct *); mmap() -ENODEV
unsigned int (*poll) (struct file *, struct poll_table_struct *); poll() epoll() select() 可 NULL
1
2
3
4
5
6
7
8
9
10
11
12
struct file {
// 讀寫權限
fmode_t f_mode;
// loff_t -> long long 64bit
// 目前讀取位置
loff_t f_pos;
// 特性: 如 O_NONBLOCK (nonblocking operation)
// <linux/fcntl.h>
unsigned int f_flags;
const struct file_operations *f_op;
void *private_data;
}
1
2
3
4
5
6
7
8
9
10
11
struct inode {
// Major & Minort
dev_t i_rdev;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;

struct cdev *i_cdev;
char *i_link;
};
}

註冊字元裝置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//<linux/cdev.h>
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};

struct cdev *my_cdev = cdev_alloc();
mycdev->ops = &my_fops;

// 初始化 cdev
struct cdev *cdev_alloc(void);
// 將 cdev 嵌入到自訂 struct
void cdev_init(struct cdev *, const struct file_operations *);
// cdev 加入核心
int cdev_add(struct cdev *, dev_t, unsigned);
// 註銷 cdev
void cdev_del(struct cdev *);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct scull_dev {
struct scull_qset *data;
int quantum;
int qset;
unsigned long size;
unsigned int access_key;
struct semaphore sem;
struct cdev cdev;
}

static void scull_setup_cdev(struct scull_dev *dev, int index) {
int err, devno = MKDEV(scull_major, scull_minor + index);

cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add(&dev->cdev, devno, 1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// <linux/kernel.h>

container_of(pointer, container_type, container_field);

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

int scull_open(struct inode *inode, struct file *filp) {
struct scull_dev *dev;

dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; // 奇淫技巧:將找到 scull_dev 放到 file 私有資料區
// ...
}