原创 字符驱动总结

2009-6-1 09:34 4704 5 6 分类: MCU/ 嵌入式
ziphttp://blog.chinaunix.net/u1/34474/showart.php?id=404280 基本抄自
点击看大图
点击看大图
点击看大图


点击看大图点击看大图








主设备号和此设备号


主编号标识设备相连的驱动,次编号被内核用来决定引用哪个设备。


在内核中,
dev_t
类型(
<linux/types.h>中定义)用来持有设备编号。对于
2.6.0 内核,
dev_t
32 位的量,
12
位用作主编号, 20
位用作次编号.


应当利用在
<linux/kdev_t.h>中的一套宏定义.
为获得一个 dev_t
的主或者次编号, 使用:



 (dev_t)-->主设备号、次设备号



 MAJOR(dev_t
dev)
 MINOR(dev_t dev)



 主设备号、次设备号-->(dev_t)



 MKDEV(int major,int
minor) 



在建立一个字符驱动时你的驱动需要做的第一件事是获取一个或多个设备编号来使用。并且应当在不再使用它们时释放它。




int
register_chrdev_region
(dev_t
first
,
unsigned
int
count,
char
*name);  
//
指定设备编号

int
alloc_chrdev_region
(dev_t
*dev,
unsigned
int
firstminor
,
unsigned
int
count,
char
*name);  
//
动态生成设备编号

void
unregister_chrdev_region
(dev_t
first
,
unsigned
int
count);  
   //
释放设备编号



安排主编号最好的方式,
我们认为, 是缺省使用动态分配,
而留给自己在加载时或者甚至在编译时指定主编号的选项权.


以下是在scull.c中用来获取主设备好的代码:




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
;
}



动态分配的缺点是你无法提前创建设备节点,
因为分配给你的模块的主编号会变化.
对于驱动的正常使用,
这不是问题, 因为一旦编号分配了,
你可从 /proc/devices
中读取它.


一些重要数据结构


大部分的基础性的驱动操作包括
3 个重要的内核数据结构,file_operations,
file
inode.


file_operations结构定义在
<linux/fs.h>




struct
file_operations scull_fops = {
.owner = THIS_MODULE,

.llseek = scull_llseek,
.read = scull_read,
.write
= scull_write,
.ioctl = scull_ioctl,
.open =
scull_open,
.release = scull_release,
};



struct
file,
定义于 <linux/fs.h>与用户空间程序的
FILE 指针没有任何关系。文件结构代表一个打开的文件.它由内核在
open 时创建,
并传递给在文件上操作的任何函数,
直到最后的关闭.
在文件的所有实例都关闭后,
内核释放这个数据结构.


inode
结构,是在内核内部用来表示文件的。因此,
它和代表打开文件描述符的文件结构是不同的.
可能有代表单个文件的多个打开描述符的许多文件结构,
但是它们都指向一个单个 inode
结构.


字符设备注册


    1. struct
      cdev
      分配空间(如果已经将struct
      cdev
      嵌入到自己的设备的特定结构体中,并分配了空间,这步略过!)




struct
cdev *my_cdev = cdev_alloc();


    1. 初始化struct
      cdev



void
cdev_init(struct cdev *cdev, const struct file_operations *fops)



    1. 初始化cdev.owner




cdev.owner
= THIS_MODULE;


    1. cdev设置完成,通知内核struct
      cdev
      的信息(在执行这步之前必须确定你对struct
      cdev
      的以上设置已经完成!




int
cdev_add(struct cdev *p, dev_t dev, unsigned count)



从系统中移除一个字符设备:void
cdev_del(struct cdev *p)



以下是scull中的初始化代码(之前已经为struct
scull_dev

分配了空间):




/*
 *
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_init
(&dev->cdev,
&scull_fops);
    dev
->cdev.owner
=
THIS_MODULE
;
    dev
->cdev.ops
=
&scull_fops;  //
这句可以省略,在cdev_init中已经做过
    err
=
cdev_add
(&dev->cdev,
devno
,
1
);
    
/*
Fail gracefully if need be
这步值得注意*/
    
if
(err)
        printk
(KERN_NOTICE
"Error
%d adding scull%d"
,
err
,
index
);
}



open 方法


open
方法提供给驱动来做任何的初始化来准备后续的操作.
在大部分驱动中, open
应当进行下面的工作:


●检查设备特定的错误(例如设备没准备好,
或者类似的硬件错误)
如果它第一次打开,
初始化设备
如果需要,
更新 f_op 指针.
分配并填充要放进
filp->private_data 的任何数据结构


但是,
事情的第一步常常是确定打开哪个设备.
记住 open 方法的原型是:




int
(*open)(struct inode *inode, struct file *filp);



inode
参数有我们需要的信息,以它的
i_cdev 成员的形式,
里面包含我们之前建立的 cdev
结构.




container_of(pointer,
container_type, container_field);




这个宏使用一个指向
container_field
类型的成员的指针,
它在一个
container_type
类型的结构中,
并且返回一个指针指向包含结构.

scull_open,
这个宏用来找到适当的设备结构:




struct
scull_dev *dev; /* device information */
dev =
container_of(inode->i_cdev, struct scull_dev,
cdev);
filp->private_data = dev; /* for other methods */




识别打开的设备的另外的方法是查看存储在
inode
结构的次编号.
如果你使用
register_chrdev
注册你的设备,
你必须使用这个技术.
确认使用
iminor

inode
结构中获取次编号,
并且确定它对应一个你的驱动真正准备好处理的设备.




int
scull_open(struct inode *inode, struct file *filp)
{

struct scull_dev *dev; /* device information */
dev =
container_of(inode->i_cdev, struct scull_dev, cdev);

filp->private_data = dev; /* for other methods */
/* now
trim to 0 the length of the device if open was write-only */

if ( (filp->f_flags & O_ACCMODE) = = O_WRONLY) {

scull_trim(dev); /* ignore errors */
}
return 0;
/* success */
}






release 方法


●释放 open
分配在 filp->private_data
中的任何东西
在最后的
close 关闭设备


scull
的基本形式没有硬件去关闭,
因此需要的代码是最少的:




int
scull_release(struct inode *inode, struct file *filp)
{

return 0;
}



scull模型的内存使用




以下是scull模型的结构体:




/*
 *
Representation of scull quantum sets.
 */

struct
scull_qset
{
    
void
**data;
    
struct
scull_qset
*next;
};

struct
scull_dev
{
    
struct
scull_qset
*data;
/*
Pointer to first quantum set */

    
int
quantum
;
/*
the current quantum size */

    
int
qset
;
/*
the current array size */

    
unsigned
long
size
;
/*
amount of data stored here */

    
unsigned
int
access_key
;
/*
used by sculluid and scullpriv */

    
struct
semaphore sem
;
/*
mutual exclusion semaphore */

    
struct
cdev cdev
;    
/*
Char device structure        */

};



scull驱动程序引入了两个Linux内核中用于内存管理的核心函数,它们的定义都在<linux/slab.h>:




void
*kmalloc(size_t
size
,
int
flags
);
void
kfree
(void
*ptr);




以下是scull模块中的一个释放整个数据区的函数(类似清零),将在scull以写方式打开和scull_cleanup_module中被调用:




int
scull_trim
(struct
scull_dev
*dev)
{
 
struct

scull_qset
*next,
*dptr;
    
int

qset
=
dev
->qset;
/*
量子集中量子的个数*/
    
int

i
;
    
for

(dptr
=
dev
->data;
dptr
;
dptr
=
next
)
{
/*
循环scull_set个数次,直到dptrNULL为止。*/
        
if

(dptr->data)
{
              
for

(i
=
0
;
i
<
qset
;
i
++)/*
循环一个量子集中量子的个数次*/
                   
kfree
(dptr->data[i]);/*
释放其中一个量子的空间*/

              
kfree
(dptr->data);/*
释放当前的scull_set的量子集的空间*/
              
dptr
->data
=
NULL;/*
释放一个scull_set中的void
**data
指针*/
         
}

     
next
=
dptr
->next;
/*
准备下个scull_set的指针*/
     
kfree
(dptr);/*
释放当前的scull_set*/
     
}

 
dev
->size
=
0
;
/*
当前的scull_device所存的数据为0字节*/
 
dev
->quantum
=
scull_quantum
;/*
初始化一个量子的大小*/
 
dev
->qset
=
scull_qset
;/*
初始化一个量子集中量子的个数*/
 
dev
->data
=
NULL;/*
释放当前的scull_devicestruct
scull_qset *data
指针*/
 
return

0
;
}




以下是scull模块中的一个沿链表前行得到正确scull_set指针的函数,将在readwrite方法中被调用:




/*Follow
the list*/

struct
scull_qset
*scull_follow(struct
scull_dev
*dev,
int
n
)
{
    
struct
scull_qset
*qs
=
dev
->data;
        
/*
Allocate first qset explicitly if need be */

    
if
(!
qs
)
{
        qs
=
dev
->data
=
kmalloc
(sizeof(struct
scull_qset
),
GFP_KERNEL
);
        
if
(qs
==
NULL)
            
return
NULL;
/*
Never mind */

        
memset(qs,
0
,
sizeof(struct
scull_qset
));
    
}
    
/*
Then 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
;
}



其实这个函数的实质是:如果已经存在这个scull_set,就返回这个scull_set的指针。如果不存在这个scull_set,一边沿链表scull_set分配空间一边沿链表前行,直到所需要的scull_set被分配到空间并初始化为止,就返回这个scull_set的指针。





read and write




ssize_t
read(struct file *filp, char __user *buff,
size_t count,
loff_t *offp);
ssize_t write(struct file *filp, const char
__user *buff,
size_t count, loff_t *offp);



read
write 方法的
buff 参数是用户空间指针.
因此, 它不能被内核代码直接解引用.
scull
中的读写代码需要拷贝一整段数据到或者从用户地址空间.
这个能力由下列内核函数提供,
它们拷贝一个任意的字节数组:




unsigned
long copy_to_user(void __user *to,

const void *from,
unsigned long
count);
unsigned long copy_from_user(void *to,

const void __user *from,

unsigned long count);



你可以调用 __copy_to_user
__copy_from_user 来代替.
这是有用处的, 例如,
如果你知道你已经检查了这些参数.
但是, 要小心;
事实上,
如果你不检查你传递给这些函数的用户空间指针,
那么你可能造成内核崩溃和/或安全漏洞.
而值得一提的是上两个函数与




#define
__copy_from_user
(to,from,n)
(
memcpy(to,
(void
__force
*)from,
n
),
0
)
#define
__copy_to_user
(to,from,n)    (memcpy((void
__force
*)to,
from
,
n
),
0
)



之间的关系,前者调用后者,但前者在调用前对用户空间指针进行了检查。


代码过程


1.将几乎所有模块代码都有的内容,所有模块代码都应该指定所使用的许可证,当然别忘了自己的署名,以及初始化和关闭copy以下:




/*
copy
by
压子
*/



#include
<linux/module.h>
//#include <linux/moduleparam.h> //test
moduleparam
#include <linux/init.h>
#include
<linux/slab.h>



/*

* 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 devices
*/
int scull_quantum = SCULL_QUANTUM;
int scull_qset =
SCULL_QSET;








/*

* Finally, the module stuff
*/








/*

* The cleanup function is used to handle initialization failures
as well.
* Thefore, it must be careful to work correctly even
if some of the items
* have not been initialized
*/



void
scull_cleanup_module(void)
{
int i;
dev_t devno =
MKDEV(scull_major, scull_minor);

/* Get rid of our char
dev entries */
if (scull_devices) {
for (i = 0; i <
scull_nr_devs; i++) {
scull_trim(scull_devices +
i);
cdev_del(&scull_devices.cdev);
}
kfree(scull_devices);
}

/*
cleanup_module is never called if registering failed
*/
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_init(&dev->cdev, &scull_fops);
dev->cdev.owner
= THIS_MODULE;
// dev->cdev.ops = &scull_fops;
err =
cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if
need be */
if (err)
printk(KERN_NOTICE "Error %d
adding scull%d", err, index);
}








int
scull_init_module(void)
{
int result, i;
dev_t dev =
0;

/*
* Get a range of minor numbers to work with,
asking for a dynamic
* major unless directed otherwise at load
time.
*/
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);
if (!scull_devices)
{
result = -ENOMEM;
goto fail; /* Make this more
graceful */
}
memset(scull_devices, 0, scull_nr_devs *
sizeof(struct scull_dev));

/* Initialize each
device. */
for (i = 0; i < scull_nr_devs; i++)
{
scull_devices.quantum =
scull_quantum;
scull_devices.qset =
scull_qset;
init_MUTEX(&scull_devices.sem);
scull_setup_cdev(&scull_devices,
i);
}

return 0; /* succeed */


fail:
scull_cleanup_module();
return result;
}








module_init(scull_init_module);
module_exit(scull_cleanup_module);








MODULE_AUTHOR("Duck");
MODULE_LICENSE("Dual
BSD/GPL");






2.自然在scull_setup_cdevcdev_init(&dev->cdev,
&scull_fops);
这里先写写openrelease



/*


copy
by
压子


*/


#include
<linux/module.h>


//#include
<linux/moduleparam.h> //test moduleparam


#include
<linux/init.h>





#include
<linux/slab.h> /* kmalloc() */





/*


*
Empty out the scull device; must be called with the device


*
semaphore held.


*/


int
scull_trim(struct scull_dev *dev)


{


struct
scull_qset *next, *dptr;


int
qset = dev->qset; /* "dev" is not-null */


int
i;





for
(dptr = dev->data; dptr; dptr = next) { /* all the list items
*/


if
(dptr->data) {


for
(i = 0; i < qset; i++)


kfree(dptr->data);


kfree(dptr->data);


dptr->data
= NULL;


}


next
= dptr->next;


kfree(dptr);


}


dev->size
= 0;


dev->quantum
= scull_quantum;


dev->qset
= scull_qset;


dev->data
= NULL;


return
0;


}


/*


*
Open and close


*/





int
scull_open(struct inode *inode, struct file *filp)


{


struct
scull_dev *dev; /* device information */





dev
= container_of(inode->i_cdev, struct scull_dev, cdev);


filp->private_data
= dev; /* for other methods */





/*
now trim to 0 the length of the device if open was write-only */


if
( (filp->f_flags & O_ACCMODE) == O_WRONLY) {


if
(down_interruptible(&dev->sem))


return
-ERESTARTSYS;


scull_trim(dev);
/* ignore errors */


up(&dev->sem);


}


return
0; /* success */


}





int
scull_release(struct inode *inode, struct file *filp)


{


return
0;


}





struct
file_operations scull_fops = {


.owner
= THIS_MODULE,


.read
= scull_read,


.write
= scull_write,


.open
= scull_open,


.release
= scull_release,


};





/*


*
Finally, the module stuff


*/





/*


*
The cleanup function is used to handle initialization failures as
well.


*
Thefore, it must be careful to work correctly even if some of the
items


*
have not been initialized


*/


void
scull_cleanup_module(void)


{


int
i;


dev_t
devno = MKDEV(scull_major, scull_minor);





/*
Get rid of our char dev entries */


if
(scull_devices) {


for
(i = 0; i < scull_nr_devs; i++) {


scull_trim(scull_devices
+ i);


cdev_del(&scull_devices.cdev);


}


kfree(scull_devices);


}





/*
cleanup_module is never called if registering failed */


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_init(&dev->cdev,
&scull_fops);


dev->cdev.owner
= THIS_MODULE;


// dev->cdev.ops
= &scull_fops;


err
= cdev_add (&dev->cdev, devno, 1);


/*
Fail gracefully if need be */


if
(err)


printk(KERN_NOTICE
"Error %d adding scull%d", err, index);


}





int
scull_init_module(void)


{


int
result, i;


dev_t
dev = 0;





/*


*
Get a range of minor numbers to work with, asking for a dynamic


*
major unless directed otherwise at load time.


*/


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);


if
(!scull_devices) {


result
= -ENOMEM;


goto
fail; /* Make this more graceful */


}


memset(scull_devices,
0, scull_nr_devs * sizeof(struct scull_dev));





/*
Initialize each device. */


for
(i = 0; i < scull_nr_devs; i++) {


scull_devices.quantum
= scull_quantum;


scull_devices.qset
= scull_qset;


init_MUTEX(&scull_devices.sem);


scull_setup_cdev(&scull_devices,
i);


}





return
0; /* succeed */





fail:


scull_cleanup_module();


return
result;


}





module_init(scull_init_module);


module_exit(scull_cleanup_module);





MODULE_AUTHOR("Duck");


MODULE_LICENSE("Dual
BSD/GPL");






3.完整版



/*


copy
by
压子


*/


#include
<linux/module.h>


#include
<linux/moduleparam.h>


#include
<linux/init.h>





#include
<linux/kernel.h> /* printk() */


#include
<linux/slab.h> /* kmalloc() */


#include
<linux/fs.h> /* everything... */


#include
<linux/errno.h> /* error codes */


#include
<linux/types.h> /* size_t */


#include
<linux/fcntl.h> /* O_ACCMODE */


#include
<linux/cdev.h>


#include
<asm/system.h> /* cli(), *_flags */


#include
<asm/uaccess.h> /* copy_*_user */


#include
"scull.h" /* local definitions */





/*


*
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 devices */


int
scull_quantum = SCULL_QUANTUM;


int
scull_qset = SCULL_QSET;





module_param(scull_major,
int, S_IRUGO);


module_param(scull_minor,
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 the scull device; must be called with the device


*
semaphore held.


*/


int
scull_trim(struct scull_dev *dev)


{


struct
scull_qset *next, *dptr;


int
qset = dev->qset; /* "dev" is not-null */


int
i;





for
(dptr = dev->data; dptr; dptr = next) { /* all the list items
*/


if
(dptr->data) {


for
(i = 0; i < qset; i++)


kfree(dptr->data);


kfree(dptr->data);


dptr->data
= NULL;


}


next
= dptr->next;


kfree(dptr);


}


dev->size
= 0;


dev->quantum
= scull_quantum;


dev->qset
= scull_qset;


dev->data
= NULL;


return
0;


}


/*


*
Open and close


*/





int
scull_open(struct inode *inode, struct file *filp)


{


struct
scull_dev *dev; /* device information */





dev
= container_of(inode->i_cdev, struct scull_dev, cdev);


filp->private_data
= dev; /* for other methods */





/*
now trim to 0 the length of the device if open was write-only */


if
( (filp->f_flags & O_ACCMODE) == O_WRONLY) {


if
(down_interruptible(&dev->sem))


return
-ERESTARTSYS;


scull_trim(dev);
/* ignore errors */


up(&dev->sem);


}


return
0; /* success */


}





int
scull_release(struct inode *inode, struct file *filp)


{


return
0;


}


/*


*
Follow the list


*/


struct
scull_qset *scull_follow(struct scull_dev *dev, int n)


{


struct
scull_qset *qs = dev->data;





/*
Allocate first qset explicitly if need be */


if
(! qs) {


qs
= dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);


if
(qs == NULL)


return
NULL; /* Never mind */


memset(qs,
0, sizeof(struct scull_qset));


}





/*
Then 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;


}





/*


*
Data management: 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; /* how many bytes in the listitem */


int
item, s_pos, q_pos, rest;


ssize_t
retval = 0;





if
(down_interruptible(&dev->sem))


return
-ERESTARTSYS;


if
(*f_pos >= dev->size)


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;


rest
= (long)*f_pos % itemsize;


s_pos
= rest / quantum; 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[s_pos])


goto
out; /* don't fill holes */





/*
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
= -EFAULT;


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; /* value used in "goto out" statements
*/





if
(down_interruptible(&dev->sem))


return
-ERESTARTSYS;





/*
find listitem, qset index and offset in the quantum */


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 */


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;


}


/*
write only up to the end of this quantum */


if
(count > quantum - q_pos)


count
= quantum - q_pos;





if
(copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {


retval
= -EFAULT;


goto
out;


}


*f_pos
+= count;


retval
= count;





/*
update the size */


if
(dev->size < *f_pos)


dev->size
= *f_pos;





out:


up(&dev->sem);


return
retval;


}





struct
file_operations scull_fops = {


.owner
= THIS_MODULE,


.read
= scull_read,


.write
= scull_write,


.open
= scull_open,


.release
= scull_release,


};





/*


*
Finally, the module stuff


*/





/*


*
The cleanup function is used to handle initialization failures as
well.


*
Thefore, it must be careful to work correctly even if some of the
items


*
have not been initialized


*/


void
scull_cleanup_module(void)


{


int
i;


dev_t
devno = MKDEV(scull_major, scull_minor);





/*
Get rid of our char dev entries */


if
(scull_devices) {


for
(i = 0; i < scull_nr_devs; i++) {


scull_trim(scull_devices
+ i);


cdev_del(&scull_devices.cdev);


}


kfree(scull_devices);


}





/*
cleanup_module is never called if registering failed */


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_init(&dev->cdev,
&scull_fops);


dev->cdev.owner
= THIS_MODULE;


// dev->cdev.ops
= &scull_fops;


err
= cdev_add (&dev->cdev, devno, 1);


/*
Fail gracefully if need be */


if
(err)


printk(KERN_NOTICE
"Error %d adding scull%d", err, index);


}





int
scull_init_module(void)


{


int
result, i;


dev_t
dev = 0;





/*


*
Get a range of minor numbers to work with, asking for a dynamic


*
major unless directed otherwise at load time.


*/


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);


if
(!scull_devices) {


result
= -ENOMEM;


goto
fail; /* Make this more graceful */


}


memset(scull_devices,
0, scull_nr_devs * sizeof(struct scull_dev));





/*
Initialize each device. */


for
(i = 0; i < scull_nr_devs; i++) {


scull_devices.quantum
= scull_quantum;


scull_devices.qset
= scull_qset;


init_MUTEX(&scull_devices.sem);


scull_setup_cdev(&scull_devices,
i);


}





return
0; /* succeed */





fail:


scull_cleanup_module();


return
result;


}





module_init(scull_init_module);


module_exit(scull_cleanup_module);





MODULE_AUTHOR("Duck");


MODULE_LICENSE("Dual
BSD/GPL");









运行


yannzi@pc:~/linuxdriver2.6/字符驱动03$
make


make
-C /lib/modules/2.6.28-12-generic/build
M=/home/yannzi/linuxdriver2.6/
字符驱动03
modules


make[1]:
正在进入目录 `/usr/src/linux-headers-2.6.28-12-generic'


CC
[M] /home/yannzi/linuxdriver2.6/
字符驱动03/scull.o


Building
modules, stage 2.


MODPOST
1 modules


CC
/home/yannzi/linuxdriver2.6/
字符驱动03/scull.mod.o


LD
[M] /home/yannzi/linuxdriver2.6/
字符驱动03/scull.ko


make[1]:正在离开目录
`/usr/src/linux-headers-2.6.28-12-generic'


yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo insmod scull.ko


[sudo]
password for yannzi:


yannzi@pc:~/linuxdriver2.6/字符驱动03$
cat /proc/devices



Character
devices:


1
mem


4
/dev/vc/0


4
tty


4
ttyS


5
/dev/tty


5
/dev/console


5
/dev/ptmx


6
lp


7
vcs


10
misc


13
input


14
sound


21
sg


29
fb


99
ppdev


108
ppp


116
alsa


128
ptm


136
pts


180
usb


189
usb_device


216
rfcomm


250
scull


251
hidraw


252
usb_endpoint


253
usbmon


254
rtc





Block
devices:


1
ramdisk


2
fd


259
blkext


7
loop


8
sd


9
md


11
sr


65
sd


66
sd


67
sd


68
sd


69
sd


70
sd


71
sd


128
sd


129
sd


130
sd


131
sd


132
sd


133
sd


134
sd


135
sd


252
device-mapper


253
pktcdvd


254
mdp



yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull0 c 250 0


yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull1 c 250 1


yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull2 c 250 2


yannzi@pc:~/linuxdriver2.6/字符驱动03$
sudo mknod -m 666 scull3 c 250 3


yannzi@pc:~/linuxdriver2.6/字符驱动03$
ls -l > /dev/scull0


yannzi@pc:~/linuxdriver2.6/字符驱动03$
ls -l /dev/scull0


crwxrwxrwx
1 root staff 250, 0 2009-05-31 22:13 /dev/scull0


yannzi@pc:~/linuxdriver2.6/字符驱动03$
cat /dev/scull0


总用量 596


-rw-r--r--
1 yannzi yannzi 487 2009-05-31 21:53 Makefile


-rw-r--r--
1 yannzi yannzi 487 2009-05-31 21:53 Makefile~


-rw-r--r--
1 yannzi yannzi 0 2009-05-31 21:53 Module.markers


-rw-r--r--
1 yannzi yannzi 59 2009-05-31 21:53 modules.order


-rw-r--r--
1 yannzi yannzi 0 2009-05-31 21:53 Module.symvers


-rw-r--r--
1 yannzi yannzi 1599 2009-05-31 22:15 path


drwxr-xr-x
2 yannzi yannzi 4096 2009-05-31 21:49 scull


-rw-r--r--
1 yannzi yannzi 7984 2009-05-31 21:43 scull.c


-rw-r--r--
1 yannzi yannzi 5415 2009-05-31 21:40 scull.c~


-rw-r--r--
1 yannzi yannzi 5153 2005-02-01 04:31 scull.h


-rw-r--r--
1 yannzi yannzi 16185 2009-05-31 21:53 scull.ko


-rwxr-xr-x
1 yannzi yannzi 896 2009-05-31 22:12 scull_load.sh


-rw-r--r--
1 yannzi yannzi 1708 2009-05-31 22:12 scull_load.sh~


-rw-r--r--
1 yannzi yannzi 1203 2009-05-31 21:53 scull.mod.c


-rw-r--r--
1 yannzi yannzi 11068 2009-05-31 21:53 scull.mod.o


-rw-r--r--
1 yannzi yannzi 5740 2009-05-31 21:53 scull.o


drwxr-xr-x
2 yannzi yannzi 4096 2007-10-25 09:53 scull-test


-rw-r--r--
1 yannzi yannzi 692 2009-05-31 22:14 test_load


-rw-r--r--
1 yannzi yannzi 0 2009-05-31 22:14 test_load~


-rw-r--r--
1 yannzi yannzi 500952 2009-05-31 21:59
字符驱动.eio






PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户201423 2009-6-4 20:39

好厉害,加油!
相关推荐阅读
yannzi 2009-08-14 22:45
C++课程设计指导书之二 媒體庫管理系統
一、課程設計目的l        通過設計、實現媒體庫管理系統,熟悉面向對象的設計思想;l        進一步熟悉、掌握STL中有關類型及其使用方法,泛型編程的風格。二、系統設計要求在這個課程設計中...
yannzi 2009-08-10 16:17
C++课程设计指导书之一 学生考勤管理系统,已添加自己的程序
https://static.assets-stash.eet-china.com/album/old-resources/2009/5/15/cc35849a-1226-4015-829a-f5fe...
yannzi 2009-08-10 11:54
痛苦并快乐着
今天图书馆下了一堆资料,可能是猛过头了,所以用死了两台电脑,还有一台电脑根本开不了机。。。帮同学影印了九章奖状,有机会可以剥削人家一把,影印太费时了。在宿舍,还有工人忙着修宿舍,噪音很吵,就快开始看资...
yannzi 2009-08-09 22:21
实用软件介绍 (Q-Dir、UE编辑器、Beyond Compare)
Q-Dir 3.93http://www.onlinedown.net/soft/66200.htmUltraEdit-32 (文本编辑器)http://www.orsoon.com/Soft/780...
yannzi 2009-08-09 22:09
实习第8天
      昨天也没什么好写的,就这样跳过了,都是自己在看一些有关嵌入式的视频。      原本今天也没什么好写的,倒数第二天了,发现有些东西还是挺人性化的,比如不用现在就把电脑还了之类的,要不然还真...
yannzi 2009-08-09 22:08
实习第6天
       这天吃了早餐来到公司,过了半个钟,就退还了两本Windows相关的编程书,放弃MFC相关的编程了,然后再借了两本与嵌入式有关的书。       就这样开始一天的读书之旅。看来要开始准备辞...
EE直播间
更多
我要评论
1
5
关闭 站长推荐上一条 /3 下一条