Skip to content

Commit

Permalink
initialize kernel_driver repo.
Browse files Browse the repository at this point in the history
  • Loading branch information
ybin committed Mar 15, 2013
1 parent cf4d5a0 commit 4ca98dc
Show file tree
Hide file tree
Showing 9 changed files with 1,159 additions and 0 deletions.
36 changes: 36 additions & 0 deletions THIS_MODULE.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

// THIS_MODULE [@include/linux/export.h]
extern struct module __this_module;
#define THIS_MODULE (&__this_module)

// __this_module [@scripts/mod/modpost.c]
static void add_header(struct buffer *b, struct module *mod)
{
buf_printf(b, "#include <linux/module.h>\n");
buf_printf(b, "#include <linux/vermagic.h>\n");
buf_printf(b, "#include <linux/compiler.h>\n");
buf_printf(b, "\n");
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
buf_printf(b, "\n");
buf_printf(b, "struct module __this_module\n");
buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
buf_printf(b, "\t.name = KBUILD_MODNAME,\n");
if (mod->has_init)
buf_printf(b, "\t.init = init_module,\n");
if (mod->has_cleanup)
buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"
"\t.exit = cleanup_module,\n"
"#endif\n");
buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n");
buf_printf(b, "};\n");
}

// kernel用struct module列表来代表各种module
// 由此创建了 xxx.mod.c 文件, 但是module name还是没有找到,

// KBUILD_MODNAME 创建方式如下: [@Makefile]
// -DMACRO=DEFN 以字符串“DEFN”定义 MACRO 宏
gcc -DKBUILD_MODNAME=demo_module ......



50 changes: 50 additions & 0 deletions linux_err_type.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* include/linux/err.h
* ERR_PTR, PTR_ERR, IS_ERR etc.
*/

// kernel 中关于这些error宏的解释

// 最大错误码为 4K - 1,也就是说 -MAX_ERRNO到-1,每一个都表示一种错误
#define MAX_ERRNO 4095

// ERR_PTR宏可以这样理解: change "error number" to "pointer"
// 参数long error是错误码,强制类型转换为(void *),便成了内存地址指针,如:
// error == -4095,其对应的补码表示为 0xFFFFF001
// 强制类型转换为(void *),0xFFFFF001便成了一个地址
static inline void * __must_check ERR_PTR(long error)
{
return (void *) error;
}

// PTR_ERR宏理解为:change "pointer" to "error number"
// 跟ERR_PTR宏正好相反
static inline long __must_check PTR_ERR(const void *ptr)
{
return (long) ptr;
}

// IS_ERR宏的原理是这样的:
// 内核分配的内存都是以page为单位的,在32位系统上,一个page为4K大小,
// 但是最后一个page是禁止使用的,看高端内存可知,这最后一个page是作为
// fixed_map使用的,像驱动程序等是无法分配到这个page的,所以如果分配的
// 内存指针跑到最后一个page地址范围(0xFFFFF000 ~ 0xFFFFFFFF)了,那么
// 这个一定是发生错误了。
static inline long __must_check IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}

#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
// 其中(unsighed long)-MAX_ERRNO的十六进制表示为:0xFFFFF001,恰好比最后一个
// page的地址(0xFFFFF000)大 1.
// 可以联合这些宏来使用:
// 如果IS_ERR宏返回true,就调用PTR_ERR宏得到错误码。
// 这就是PTR_RET宏的实现原理:
static inline int __must_check PTR_RET(const void *ptr)
{
if (IS_ERR(ptr))
return PTR_ERR(ptr);
else
return 0;
}
196 changes: 196 additions & 0 deletions linux_open_operation.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
*
* Linux open operation: from userspace to driver.
*
*/

// Part One
// 0. system call entrance [@fs/open.c]
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
long ret;

if (force_o_largefile())
flags |= O_LARGEFILE;
//** this is the main operation
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}

// 1. do_sys_open [@fs/open.c]
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);

if (!IS_ERR(tmp)) {
fd = get_unused_fd_flags(flags);
if (fd >= 0) {
//** this is the main operation
struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f->f_path.dentry);
fd_install(fd, f);
}
}
putname(tmp);
}
return fd;
}

// 2. do_filp_open
struct file *do_filp_open(int dfd, const char *pathname,
int open_flag, int mode, int acc_mode)
{
struct file *filp;
struct nameidata nd;
// ......
filp = get_empty_filp();
if (filp == NULL)
goto exit_parent;
nd.intent.open.file = filp;
// ......
filp = nameidata_to_filp(nd);
// ......
}

// 3. nameidata_to_filp
struct file *nameidata_to_filp(struct nameidata *nd)
{
const struct cred *cred = current_cred();
struct file *filp;

/* Pick up the filp from the open intent */
filp = nd->intent.open.file;
/* Has the filesystem initialised the file for us? */
if (filp->f_path.dentry == NULL)
// this is the main operation
filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp, NULL, cred);
else
path_put(&nd->path);
return filp;
}

// 4. __dentry_open
static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
struct inode *inode;
int error;

f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
inode = dentry->d_inode;
// ......
f->f_mapping = inode->i_mapping;
f->f_path.dentry = dentry;
f->f_path.mnt = mnt;
f->f_pos = 0;
//** this is the main operation
f->f_op = fops_get(inode->i_fop);
file_move(f, &inode->i_sb->s_files);
// ......
//** this is the main operation
if (!open && f->f_op)
open = f->f_op->open;
if (open) {
error = open(inode, f);
}
}

//********** 现在我们知道: struct file中的 f->f_op就是struct inode中的inode->i_fop,阶段性胜利! **************//
// Part Two
// 下面来看inode->i_fop又是如何关联到driver中的内容的

// 0. init_special_inode , i_rdev 表示设备号 [@fs/inode.c]
// 手动执行命令mknode时会调用到mknode函数(比如ext3_mknode()),然后调用到init_special_inode()函数
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = rdev;
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
" inode %s:%lu\n", mode, inode->i_sb->s_id,
inode->i_ino);
}

// def_chr_fops [@fs/char_dev.c]
const struct file_operations def_chr_fops = {
.open = chrdev_open,
};

// chrdev_open
/*
* Called every time a character special file is opened
*/
static int chrdev_open(struct inode *inode, struct file *filp)
{
struct cdev *p;
struct cdev *new = NULL;
int ret = 0;

spin_lock(&cdev_lock);
p = inode->i_cdev;
if (!p) {
struct kobject *kobj;
int idx;
spin_unlock(&cdev_lock);
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
if (!kobj)
return -ENXIO;
// new即是我们的driver结构对应的struct cdev结构
new = container_of(kobj, struct cdev, kobj);
spin_lock(&cdev_lock);
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p = inode->i_cdev;
// 第一次打开时,p==NULL
if (!p) {
inode->i_cdev = p = new;
list_add(&inode->i_devices, &p->list);
new = NULL;
} else if (!cdev_get(p))
ret = -ENXIO;
} else if (!cdev_get(p))
ret = -ENXIO;
spin_unlock(&cdev_lock);
cdev_put(new);
if (ret)
return ret;

ret = -ENXIO;
// 现在将我们的driver结构对应的struct cdev结构中的fops赋值给filp->f_op
filp->f_op = fops_get(p->ops);
if (!filp->f_op)
goto out_cdev_put;

// 如果我们实现open()的话,就调用它,注意open的参数哦,尤其是那个filp.
if (filp->f_op->open) {
ret = filp->f_op->open(inode,filp);
if (ret)
goto out_cdev_put;
}

return 0;

out_cdev_put:
cdev_put(p);
return ret;
}

// 通过两部分上下结合,我们就知道:驱动中的file operations是如何赋值到struct file的f_op中去了。
23 changes: 23 additions & 0 deletions module_init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

// 0. module_init macro [@include/linux/init.h]
#define module_init(x) __initcall(x);

// 1. __initcall macro [@include/linux/init.h]
#define __initcall(fn) device_initcall(fn)

// 2. device_initcall macro [@include/linux/init.h]
#define device_initcall(fn) __define_initcall("6",fn,6)

// 3. __define_initcall macro [@include/linux/init.h]
// __attribute__((__section__("section-name"))) 表示
// 把符号放置到"section-name"段
// __used 表示该符号必须有定义, 没音定义的话编译器就会报错,
// __unused 表示符号定义可有可无, 没有定义的话编译器不会报错
// 具体到这里的解释是(以vivi_init函数为例):
// static initcall_t __initcall_vivi_init6 = vivi_init;
// 符号 __initcall_vivi_init6 必须要有定义, 否则编译器报错,
// 而且该符号要放置到 .initcall6.init 段中.
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn

Loading

0 comments on commit 4ca98dc

Please sign in to comment.