vscocam安卓版下载:字符设备驱动程序 (1) - LDD3学习整理 - 小兽的窝

来源:百度文库 编辑:中财网 时间:2024/04/28 03:27:53
字符设备驱动程序 (1) #include
#include
#include
#include     /*printk()*/
#include     /*kmalloc()*/
#include     /*everything...*/
#include     /*error codes*/
#include     /*size_t*/
#include     /*cdev struct*/
#include     /*cli(),*_flags*/
#include /*copy_ ....*/
#include "scull_t1.h" /*attention to the "" and <>*/
/*
*Our parameters which can be set at load time.
*
*/
int scull_major = SCULL_MAJOR;
int scull_minor = 0;
int scull_nr_devs = SCULL_NR_DEVS; /*number of bare scull devs*/
int scull_quantum = SCULL_QUANTUM; /*every one of quantum array has a quantum size*/
int scull_qset = SCULL_QSET;        /*the longth of quantum-set*/
module_param(scull_major,int,S_IRUGO);        /*knowledge 1*/
module_param(scull_monor,int,S_IRUGO);
module_param(scull_nr_devs,int,S_IRUGO);
module_param(scull_quantum,int,S_IRUGO);
module_param(scull_qset,int,S_IRUGO);
struct scull_dev *scull_devices; /*allocated in scull_init_module*/
/*
*Empty out (like "clean up")the scull device
*/
int scull_trim(struct scull_dev *dev) /*知识点4*/
{
struct scull_qset *next,*dptr;
int qset = dev->qset; /*amount of quantum*/
int i;
for(dptr = dev->data;dptr;dptr = next) /*循环scull_qset list 个数*/
{
if(dptr->data)
{
for(i = 0; ikfree(dptr->data[i]); /*释放dptr指向的数组的每个数组元素指向的quantum空间*/
kfree(dptr->data);/* 释放当前的scull_set的量子集的空间*/
dptr->data = NULL;/* 释放一个scull_set中的void **data指针*/
/*其实就我个人写的话下面那句指向空会漏掉*/
}
next = dptr->next; /*下一个scull_qset list开始*/
kfree(dptr); /*knowledge 2*/
}
dev->size =0; /*amount of data store here 为0*/
dev->quantum = scull_quantum; /*set the quantum'size*//*你不是清空不,怎么还来设置它的大小啊*/
dev->qset = scull_qset;         /*set the number of quantum in a array*//*同上的疑问*/
dev->data = NULL;              /*释放当前的scull_device的struct scull_qset *data指针*/
return 0;
}
/********************************************************************/
/*
*Open and close
*/
int scull_open(struct inode *inode,struct file *filp)
{
struct scull_dev *dev; /*the information of this dev*/
dev = contairner_of(inode->i_cdev,struct scull_dev, cdev); /*知识点3*/
flip->private_data = dev; /*.将获得的dev保存到filp->private_data*/
/*如果是以只读模式打开则调用scull_trim()截短设备为0*/
if((filp->f_flags&O_ACCMODE) == O_WRONLY) /*这里注意是检测打开的方式不是读写权限f_mode*/
{
if(down_interruptible(&dev->sem)) /* 对访问区域加锁*/
return -ERESTARTSYS; /*猜想应该是信号量不可用的意思吧??*/
scull_trim(dev);
up(&dev->sem);                        /* 解锁*/
}
}
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
/*由于前面定义了scull是一个全局且持久的内存区,所以他的release什么都不做*/
}
/*
*Follow the list
*/
struct scull_qset *scull_follow(struct scull_dev *dev,int n)
{
struct scull_qset *qs = dev->data;
/*Allocate frist qset explicitly if need be*/
if(! qs) /*当list为null时*/
{
qs = dev->data = kmalloc(sizeof(struct scull_qset),GFP_KERNEL);
if(qs == NULL)
return NULL; /*??*/
memset(qs,0,sizeof(struct scull_qset));
}
/*follow the list*/
while(n--)
{
if(!qs->next)
{
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs->next == NULL)
return NULL; /* Never mind */
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
continue; /*这下我傻了*/
return qs;
}
}
/*
*read and write
*/
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data; /*获得已经保存在其中的设备结构体入口指针*/
struct scull_qset *dptr;                /*the first listitem*/
int quantum = dev->quantum,qset = dev->qset;
int itemsize = quantum *qset; /*这句很怪哦 不同的部分相互赋值干吗啊??*/
int item,s_pos,q_pos,rest;
ssize_t retval = 0;
if(down_interruptible(&dev->sem))
return -ERESTARSYS;
if(*f_pos >= dev->size) /*判读off_t *f_pos参数是否超越了设备存储的范围*/
goto out;
if(*f_pos+count > dev->size)
count = dev->size - *f_pos;
/*find listitem,qset index,and offset in the quantum*//* 根据数组结构得出相应数据 */
item = (long)*f_pos/ itemsize; /*long???why???*/
rest = (long)*f_pos%itemsize;
s_pos = rest/quantum; /*no long ??why??*/
q_pos = rest%quantum;
/*follow the list up to the right position(defined elsewhere)*/
dptr = scull_follow(dev,item);
/*判断各个数值的合法性并做修正*/
if(dptr == NULL||!dptr->data||!dptr->data||!dptr->data[s_pos])
goto out;
/*read only up to the end of this quantum*/
if(count>quantum - q_pos)
count = quantum -q_pos;
/*将内核数据复制到用户区域*/
if(copy_to_user(buf, dptr->data[s_pos]+q_pos,count))/*发现自己对返回值还是不足*/
{
retval = -EFUALT;                                /*上面应该是成功返回0非成功返回非0即真*/
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos )
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum,qset = dev->qset;
int itemsize = quantum * qset;                     /*这句还是没懂*/
int item,s_pos,q_pos,rest;
ssize_t retval = -ENOMEM; /*no mem used in "goto out"*/
if(down_interruptible(&dev->sem))
return -ERESTART;
/*同read*/
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
/* follow the list up to the right position */
/*同read*/
dptr = scull_follow(dev, item);
if (dptr == NULL)
goto out;
if (!dptr->data)
{
dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
if (!dptr->data)
goto out;
memset(dptr->data, 0, qset * sizeof(char *));
}
if (!dptr->data[s_pos])
{
dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
if (!dptr->data[s_pos])
goto out;
}
if (count > quantum - q_pos)
count = quantum - q_pos;
/*注意假如内核访问用户区域,由于user域存在分页机制,
进程有可能被休眠以等待不再当前页中的数据,所以需要
copy_from_user函数是可重入的.*/
if(copy_from_user(dptr->data[s_pos]+q_pos, buf, count))
{
retval = -EFAULT; /*bad address*/
goto out;
}
*f_pos +=count;
retval = count;
/*updata the size*/
/*实际上大多数情况读写函数是不能完全读写完的,所以做更新方便读写完的操作*/
if(dev->size< *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
return retval;
}
/*set file_operations*/
struct file_operations scull_fops =
{
.owner = THIS_MODULE,
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release,
};
/*init and exit*/
void scull_cleanup_module(void)
{
int i;
dev_t devno = MKDEV(scull_major,scull_minor);/*获取设备号*/
/*get rid of dev entries*/
if(scull_devices)
{
for(i = 0; i{
scull_trim(scull_devices +i);
cdev_del(&scull_devices[i].cdev);
}
kfree(scull_devices);
}
unregister_chrdev_region(devno, scull_nr_devs);
}
/*
*set up the char_dev structure for this device
*/
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err,devno = MKDEV(scull_major,scull_minor+index);
/*初始化cdev,主要是指定其fops */
cdev_init(&dev->cdev,&scull_fops); /*注册设备结构体*//*知识点5*/
dev->cdev.owner = THIS_MODULE; /*注意这里是先注册再初始化有些元素,原因有待查*/
dev->cdev.ops = &scull_fops; /*cdev->init里面已经初始化了 可省略*/
err = cdev_add(&dev->cdev, devno,1) /*告知内核该结构体信息,设备注册到这里才算真的激活*/
if(err)
printk(KERN_NOTICE "Error %d adding scull%d",err,index); /*这个失败的可能很小书上这么提到why??*/
}
int scull_init_module(void)
{
int result,i;
dev_t dev = 0;
if (scull_major) /*选择是否自动获取设备号*/
{
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
}
else
{
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
"scull");
scull_major = MAJOR(dev);
}
if (result < 0)
{
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
/*
*allocate the devices we can't have them static,as the number
*can be specified at load time
*/
scull_devices = kmalloc(scull_nr_devs*sizeof(struct scull_dev), GFP_KERNEL);/*分配nr个scull_dev的内存*/
if(!scull_devices)
{
result = -ENOMEM; /*NO enogh memory */
goto fail;
}
memset(scull_devices,0,scull_nr_devs*sizeof(struct scull_dev)); /*kmalloc不清空*/
/*init each device*/
for(i = 0; i{
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
init_MUTEX(&scull_devices[i].sem); /*初始化旗帜变量 用于down_interruptible up(&xxx_dev->sem);*/
scull_setup_cdev(&scull_devices[i],i); /*这个一定记得放于最后*/
}
return 0; /*succeed*/
fail: /*知识点6 goto*/
scull_cleanup_module();
return result;
}
/*加载 注销*/
module_init(scull_init_module);
module_exit(scull_cleanup_module);
/*程序信息*/
MODULE_AUTHOR("小兽");
MODULE_LICENSE("Dual BSD/GPL"); /*听说不写这个会有警告,等下编译的时候试验下*/
/*knowledge 1 module_param
*我这样理解这个函数,它向当前模块传入参数
*而这些参数是经过该函数注册的,带有一定的属性
*具体属性可以跟踪moduleparam.h里面的具体函数,我想能
*理解好__attribute__这个家伙的性能应该就能理解这个函数的意义了。
*值得注意的是,注册数组之类的是用的别的类似于这个函数的函数
*
*这个知识点我犯错了,因为本身LDD3上介绍得很清楚了,由于是很久以前看书
*时候看到的,给忘记了.... *应该书它的作用是让insmod命令对那些参数可见,而这
*种使用只是在调试的时候应该
*用,至于平常视乎更多的是不允许这些参数被修改的。
*
*再次提醒自己,不要什么程序可以跑了基本能看懂就OK了,这样的路迟早走不远,更别说
*实现自己的理想。
****************************************************************************/
/*N0 2
* 关于指针释放的,说实在的这里发现自己的C基础很差,完全找不到
*指针释放的概念,在这里做个笔记 提醒下以后多注意下
*/
/*NO3
*分配并填写置于filp->private_data里的数据结构。
*将初始化过的struct scull_dev dev的指针传递到filp->private_data里,以备后用
*
*具体说法为我们不需要cdev结构体本身,但希望得到包含scull_dev结构体?
*于是将scull_dev结构体指针保存在private_data字段中,方便使用
*
*简介点说是.通过indode->i_cdev(其本身指向scull_dev的cdev)获得scull_dev的入口指针
*识别需要被打开的设备
*/
/***
*NO4
*
*scull_trim的实现相当于遍历了链表,先用dptr指针作为游标,指向每一个
*scull_qset,再通过量子集的大小dev->qset,在for循环中对dptr->data中的每一个量
*子进行释放。dptr->data是一个指针数组,所以kfree的参数为dptr->data[i]。把量
*子集释放后再释放dptr->data.接着通过next指针移动游标,再释放当前的dptr。
*完整的内存释放后再对scull_devices初始化。
*http://www.diybl.com/course/6_system/linux/Linuxjs/2008109/149527_2.html
*/
/*
*NO5 设备结构体注册详情查看LDD3 P59
*记得刚接触cdev这个家伙的时候是在跟着一本用老方法注册设备的书做完两个
*驱动之后的事了,为了接受这个结构体花了不少时间,现在都不知道为什么,
*但确实卡了我好久。呵呵 想想好笑
*这里用户自己定义的设备结构体scull_dev中嵌入了cdev注意接受这种嵌入的方法!!!!
在open函数里面
* 通过indode->i_cdev(其本身指向scull_dev的cdev)获得scull_dev的入口指针识别需要被打开的设备
* dev = container_of(inode->i_cdev, struct scull_dev, cdev);
* 将获得的dev保存到filp->private_data filp->private_data = dev
*
这样就完成了整个自己定义设备结构体的嵌入工作
*/
/*
*NO6 goto
*大一接触C的时候这个印象特深,说什么容易把程序跳乱不要乱用,前几天看一些关于
*goto 的讨论才知道,是个什么大师发表了一篇关于goto毒害的文章后才被认为是毒瘤
*看了些资料后的整体认识是:1:向后跳的话没什么大碍,也不影响可读性(短距离)
*                                    2:这里用到的错误恢复机制视乎没有别的语句能比goto
*                                     更简介的实现所需要的功能 这点蛮好的!
*                                    3:goto 源于汇编,可以说是比较底层的操作,很多破解直接
*                                     一个goto语句越过注册码算法部分 跳到检测注册码后的
*                                     程序运行部分破解就完成,所以你不能说它不强大!!!
*/
.h文件部分
#ifdef _SCULL_H_ /*这里没懂 等下收集下头文件书写知识*/
#define _SCULL_H_
/*
*macros to help debugging
*/
#undef PDEBUG /*undef it, 怕别的地方定义了 会出错,学习这个良好的习惯!!*/
#ifdef SCULL_DEBUG
/*
*下面这块被注释掉了 应该是说要使用的时候再取消注释吧 说到这里想到了
*#if 0 #endif 的强大了,通俗易懂 方便得很!!!
*在编译时如果加上gcc -DSCULL_DEBUG即define SCULL_DEBUG
*/
# ifdef __KERNEL__
/* This one if debugging is on, and kernel space */
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args)
# else
/* This one for user space */
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
# endif
# else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
# endif
#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ /*没懂*/
#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0 /*dynamic major by default*/
#endif
#ifndef SCULL_NR_DEVS
#define SCULL_NR_DEVS 4 /*scull0 through scull3*/
#endif
/*
*the bare device is a variable-length region of memory
*use a linked list of indirect blocks.
*scull_de->data points to an array pointers, each pointer refers to a memory area
*of SCULL_QUANTUM byte.
*
*The array quantum-set is SCULL_QSET long.
*
*/
#ifndef SCULL_QSET
#define SCULL_QSET 1000
#endif
#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM 4000
#endif
/*
*Representation of scull quantum sets
*/
//"scull_dev->data" points to an array of pointers, each
//* pointer refers to a memory area of SCULL_QUANTUM bytes
struct scull_qset
{
void **data;
struct scull_qset *next;
};
struct scull_dev
{
struct scull_qset *data; /*point to first quantum set*/
int quantum;                /*size of quantum*/
int qset; /*the current array size*/
unsigned long size;        /*amount of data stored here*/
unsigned int access_key; /*??*/
struct semaphore sem;    /*mutual exclusion semaphore*/
struct cdev cdev;            /*cdev.h*/
};
/*
* The different configurable parameters
*如果你写个main函数要用到这些变量直接引用这个头文件就OK啦 呵呵
*/
extern int scull_major; /* main.c */
extern int scull_nr_devs;
extern int scull_quantum;
extern int scull_qset;
/*
* Prototypes for shared functions
*以后就可以直接用咯 呵呵就是这个作用
*/
int scull_trim(struct scull_dev *dev);
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos);
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos);
/*
* Split minors in two parts
*有待补充哦 这个还没接触下面的都还没接触 以后一定补上
*/
#define TYPE(minor)    (((minor) >> 4) & 0xf)    /* high nibble */
#define NUM(minor)    ((minor) & 0xf)        /* low nibble */
/*
* Ioctl definitions
*/
/* Use 'k' as magic number */
#define SCULL_IOC_MAGIC 'k'
/* Please use a different 8-bit number in your code */
#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) reswords
/*
* S means "Set" through a ptr,
* T means "Tell" directly with the argument value
* G means "Get": reply by setting through a pointer
* Q means "Query": response is on the return value
* X means "eXchange": switch G and S atomically
* H means "sHift": switch T and Q atomically
*/
#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int)
#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int)
#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3)
#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4)
#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int)
#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)
#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)
#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8)
#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int)
#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)
#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)
/*
* The other entities only have "Tell" and "Query", because they're
* not printed in the book, and there's no need to have all six.
* (The previous stuff was only there to show different ways to do it.
*/
#define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC, 13)
#define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC, 14)
/* ... more to come */
#define SCULL_IOC_MAXNR 14
#endif /* _SCULL_H_ */
makefile 部分
#内核路径
KERNELDIR = /home/alu/mywork/systems/linux-2.6.22.6
# $为取当前变量 PWD为取当前路径
PWD := $(shell pwd)
#NO1 #N02
INSTALLDIR =/home/soso/ldd3/modules
CROSS_COMPLE =/usr/local/arm/crosstool/gcc-3.3.6-glibc-2.3.2/arm-linux/bin:
CC = $(CROSS_COMPLE)gcc
obj-m := scull.o
#NO3
#伪目标1
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
#N04
modules_install:
cp scull.ko $(INSTALLDIR)
clean:
rm -rf *.o *~core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY:modules modules_install clean
#NO5
###############################################################################
#NO1: := 和=的区别别
#:= 定义的变量如果值内容本身就是变量,他不会延伸。如果是=:
#会延伸。所以在使用时,不需要延伸的时候一定要加上: ,防止不可
#控的情况。
#何为延伸:
#a = orginal_value
#b = $(a)
#a = later_value
#最后a=later_value
#和下面
#a = orginal_value
#b := $(a)
#a = later_value
#最后a=original_value
###############################################################################
###############################################################################
#NO2
#shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的
#命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系
#统命令后的输出作为函数返回。于是,我们可以用操作系统命令以及字
#符串处理命令awk,sed等等命令来生成一个变量,如:
#contents := $(shell cat foo)
#files := $(shell echo *.c)
#注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行
#性能,如果你的Makefile中有一些比较复杂的规则,并大量使用了这个函数,
#那么对于你的系统性能是有害的。特别是Makefile的隐晦的规则可能会让你
#的shell函数执行的次数比你想像的多得多。
#################################################################################
#################################################################################
#NO3
#值是y (for built-in)或者m(for module)。如果既不是y也不是m,这个文件不会被编译
#或链接
#这里也是文件中的第一个目标文件(target ) :<>中make是如何
#工作的 提到了这点
#我的理解是当执行make时 这条语句才是核心,他决定了最后所输出的顶层
#最后发现这样理解是错误的,因为没理解好modules这个伪目标所指内容的
#性能做完module分析后再联系起来总结下。
#################################################################################
#################################################################################
#NO4 $(MAKE)
#四、嵌套执行make
#在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在
#不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有
#利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一
#个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和
#分段编译有着非常大的好处。
#例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了
#这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:
#subsystem:
#cd subdir && $(MAKE)
#其等价于:
#subsystem:
#$(MAKE) -C subdir
#定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成
#一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然
#后执行make命令。
#我们把这个Makefile叫做“总控Makefile”,总控Makefile的变量可以传递到下级的Makefile中
#(如果你显示的声明),但是不会覆盖下层的Makefile中所定义的变量,除非指定
#了“-e”参数。
#摘自<<跟我一起写 Makefile>>
#记起了以前学写驱动的时候,我们的编译过程是这样的:在内核(KERNELDIR)目录的
#drivers/char的Makefile下增加obj-m += scull.o 在那个目录下make modules就会在那个目录下生成
#scull.ko文件然后移到开发板进行加载卸载
#而在这里这个makefile相当于远程控制(KERNELDIR)/Makefile 做出相同的操作即总控makefile
#######################################################################################
#######################################################################################
#NO5 .PHONY
#这个是伪目标声明标志 具体知识请自己百度
###################################################################################
###################################################################################
#未解决知识点1:
#clean: 这个伪目标具体跟的文件不怎么知道写 我想可能要根据编译过程最后生成的
#文件书写 ,应该是看最后都生成了些什么东西,然后写上去。
#但都只是猜想,等下实践后补上
###################################################################################