hello_world-3.0之简单字符设备
1.helloworldmem.c
#include<linux/module.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#define HELLOWORLDMEM_SIZE 0x1000
#define HELLOWORLDMEM_MAJOR 250
static int helloworldmem_major = HELLOWORLDMEM_MAJOR;
struct helloworldmem_dev {
struct cdev cdev;
unsigned char mem[HELLOWORLDMEM_SIZE];
};
struct helloworldmem_dev dev;
static const struct file_operations helloworldmem_fops = {
.owner = THIS_MODULE,
};
static void helloworldmem_setup_cdev()
{
int err,devno = MKDEV(helloworldmem_major,0);
cdev_init(&dev.cdev,&helloworldmem_fops);
dev.cdev.owner = THIS_MODULE;
err = cdev_add(&dev.cdev,devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding helloworldmem",err);
}
int __init helloworldmem_init(void)
{
int result;
dev_t devno = MKDEV(helloworldmem_major,0);
if(helloworldmem_major)
result = register_chrdev_region(devno,1,"helloworldmem");
else
{
result = alloc_chrdev_region(&devno,0,1,"helloworldmem");
helloworldmem_major = MAJOR(devno);
}
if(result < 0)
return result;
helloworldmem_setup_cdev();
return 0;
}
void __exit helloworldmem_exit(void)
{
cdev_del(&dev.cdev);
unregister_chrdev_region(MKDEV(helloworldmem_major,0),1);
}
module_init(helloworldmem_init);
module_exit(helloworldmem_exit);
MODULE_LICENSE("GPL");
2.分析以及写一个最简单的字符设备的步骤
2.1 首先是定义一个自己的结构helloworldmem_dev
这个结构呢,内嵌一个cdev;其实,我们想想前面的设备驱动模型,那个是设备中内嵌一个kobject,这里内嵌一个cdev,是想加入到字符驱动核心系统中,让他成为字符
驱动,这样编写驱动来讲更方便,而且,可以应用字符核心系统中提供的函数接口,轻易的实现在内核中创建并且注册。
2.2 关于设备号dev_t 以及注册字符设备驱动
系统中每一个设备在/dev/下都有一个设备号。其中设备的主设备号是12位,次设备号是20位
可以通过MAJOR(dev_t dev)来获取主设备号。
其实,设备号的获得有两种方法,一种是静态的:写死主设备号,然后通过MKDEV(major,mirror)来制作一个设备号,然后通过register_chrdev_region(devno,1,"helloworldmem")来注册一个字符设备驱动,
一种是动态的,这种方法比较常见:alloc_chrdev_region(&devno,0,1,"helloworldmem");这样一步将动态的分配了字符设备号,而且还注册了字符设备驱动
221 /**
222 * alloc_chrdev_region() - register a range of char device numbers
223 * @dev: output parameter for first assigned number
224 * @baseminor: first of the requested range of minor numbers
225 * @count: the number of minor numbers required
226 * @name: the name of the associated device or driver
227 *
228 * Allocates a range of char device numbers. The major number will be
229 * chosen dynamically, and returned (along with the first minor number)
230 * in @dev. Returns zero or a negative error code.
231 */
232 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
233 const char *name)
234 {
235 struct char_device_struct *cd;
236 cd = __register_chrdev_region(0, baseminor, count, name);
237 if (IS_ERR(cd))
238 return PTR_ERR(cd);
239 *dev = MKDEV(cd->major, cd->baseminor);
240 return 0;
241 }
这个是注册一系列的连续的次设备和一个主设备的设备,dev_t *dev,将要传给的那个设备号,basemirror 开始的次设备号,count,次设备号数量,name,名字
其中已经包含了register_chrdev_region了。
2.3 初始化并注册字符设备
cdev_init(&dev.cdev,&helloworld_fops);这一步是初始化字符设备,dev.cdev.owner貌似这个是必须的,但是不清楚原因,添加上就是
cdev_add(&dev.cdev,devno,1)这一步正式的注册设备到系统中
2.4 卸载字符设备与字符设备驱动
cdev_del(&dev.cdev) 这个是卸载字符设备
unregister_chrdev_region(MKDEV(helloworld_major,0),1)
这个是卸载字符设备驱动
总结:我是根据前面简单设备模型中的内容来理解字符设备驱动,首先,我们定义了一个设备,那么他需要有设备的注册和设备驱动的注册,以及bus,那么
字符设备的bus,我们不必讨论了,这个是字符设备核心系统干的事,那么字符设备的设备注册,分为两步:一,字符设备初始化:cdev_init;二。字符设备注册到
系统中cdev_add;那么字符设备驱动的注册呢,其实就一个register_chrdev_region,这样一个设备就注册到字符设备核心子系统中了。其他的事是子系统操心的。这也就是
为什么linux中出现很多的子系统,这样很方便我们开发驱动了。
相对应的卸载设备是cdev_del,卸载驱动是unregister_chrdev_region.了
对于#include<linux/fs.h>,需要他是因为alloc_chrdev_region 和unregister_chrdev_region都在这里声明了,我本来以为需要他是因为fs_operations哈哈