Linux驱动学习(1) 创建设备方法

参考书籍:《Linux设备驱动程序》

(一)编写驱动程序有三个要素,主次设备号,设备文件(又叫设备结点),设备操作方法。怎样在驱动中将三者联系起来成为理解驱动的重要途径,我们通过创建设备号的动态静态方式,创建设备文件的自动手动方式来理解驱动。

(二)主次设备号
1.设备号是通过两个个整数来描述驱动对应设备,其中主设备号对应驱动程序,次设备号对应设备序号。也就是多个设备可能公用一个驱动,也就是公用一个主设备号。次设备号相同不表明他们之间有关系,它主要是用来区分驱动相同的设备。
2.内核中通过dev_t类型来保存,在不同的内核版本上它的实现可能不同,我们也不需要深究,只需要它保存了主设备号和此设备号即可,要通过这个变量获取设备号方法为: MAJOR(dev_t);//获取主设备号

MINOR(dev_t);//获取次设备号
MKDEV(int major,int minor);//获取次设备号变量

3.创建设备号有两种办法,动态和静态,静态分配有设备号已被占用的风险。
①静态:

register_chrdev_region(dev_t first,unsigned int count,char *name);

或者

register_chrdev(int major,char *name,struct *file_operations);//major不为0是静态

②动态

register_chrdev(int major,char *name,struct *file_operations);//major为0是动态

或者

alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);

4.创建好后可以在/proc/devices下看到设备名和设备编号
5.注意不管静态还是动态在移除驱动时都需要删除设备号

void unregister_chrdev_region(dev_t first,unsigned int count);

(三)设备文件
1.设备文件是具体设备在文件系统中的映射,使我们可以像对文件进行操作一样操作设备。应用程序通过操作设备文件然后经过驱动的代理进而操作具体设备。linux内核通过三个结构体变量实现上述操作。
(1)struct file,为了使用户空间的文件操作能够在内核空间中被保存,内核用一种和用户空间文件File不同的结构体file来保存文件参数,例如文件打开权限以及偏移量等等。
(2)struct file_operations,驱动程序为了使文件操作映射到设备操作,需要定义一些方法映射,这些方法就被保存在这个结构体里,例如open,read,write等等。
(3)struct inode,内核中会保存文件系统中每个文件的信息,这些信息就保存在inode结构体中,例如文件属性,是文件还是文件夹等等。
2.创建设备文件有两种办法,手动创建和自动创建。
(1)手动创建:
通过在shell中调用mknod命令,例如:

mknod /proc/testdev c 231 0

(2)自动创建:

class_create(struct module *owner, const char *name);
device_create(struct class *class, struct device *parent,dev_t devt, const char *fmt, ...);

3.注意设备文件也要驱动卸载时进行删除
命令行方式:

rm -fr /dev/testdev

内核:

device_destroy(struct class *class,dev_t devnum);
class_destroy(struct class *class);

(四)设备
1.内核为了保存设备的上述属性,它用另外一种结构体来保存,而且不同的结构体表示不同类型的设备,例如用struct cdev表示字符设备,对字符设备初始化如下:

cdev_init(struct cdev *dev,struct file_operations *fops);
cdev_add(struct cdev *dev,dev_t devnum,unsigned int count);

2.同样,此结构体在驱动卸载时也应该删除:

cdev_del(cdev *dev);

(五)代码
1.静态手动:

include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<asm/io.h>
MODULE_LICENSE("GPL");

#define DEVICE_NAME "led"
#define LED_MAJOR 231

static int __init led_init(void);
static void __exit led_exit(void);
static int led_open(struct inode *inode,struct file *file);
static long led_ioctl(struct file *file,unsigned int cmd,unsigned long arg);

static struct file_operations led_fops={
    .owner=THIS_MODULE,
    .open=led_open,
    .unlocked_ioctl=led_ioctl,
};

static int led_open(struct inode *inode,struct file *file)
{
    printk("open success");
    return 0;
}

static long led_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    printk("led_ioctl cmd=[%d]",cmd);
}

static int __init led_init(void)
{
    int ret=0;
    ret=register_chrdev(LED_MAJOR,DEVICE_NAME,&led_fops);
    if(ret<0)
    {
        printk("led cannot reg magor num :[%d] ret=[%d]",LED_MAJOR,ret);
        return ret;
    }
    return ret;
}

static void __exit led_exit(void)
{
    unregister_chrdev(LED_MAJOR,DEVICE_NAME);
}

module_init(led_init);
module_exit(led_exit);

2.动态创建设备号(register_chrdev(int major,char *name,struct *file_operations);//major为0是动态),不包括创建设备文件。

#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<asm/io.h>
#include<linux/cdev.h>

MODULE_LICENSE("GPL");

#define DEVICE_NAME "test_dev1"

static unsigned int major_dev;

static int __init test_init(void);
static void __exit test_exit(void);
static int test_open(struct inode *inode,struct file *file);
static long test_ioctl(struct file *file,unsigned int cmd,unsigned long arg);


static int alloc_dev_num;
static dev_t num_dev;
struct cdev dev_c;
struct class *cdev_class;

static struct file_operations led_fops={
    .owner=THIS_MODULE,
    .open=test_open,
    .unlocked_ioctl=test_ioctl,
    //.ioctl=led_ioctl;
};


static int test_open(struct inode *inode,struct file *file)
{
    printk("test__open");
    return 0;
}

static long test_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    printk("test_ioctl cmd=[%d]",cmd);
    return 0;
}

static int __init test_init(void)
{
    int ret=0;
    ret=register_chrdev(0,DEVICE_NAME,&led_fops);//成功是返回值即为设备号
    if(ret<0)
    {
        printk("led cannot reg magor num :[%d] ret=[%d]",ret,ret);
        return ret;
    }
    printk("led reg magor num success [%d]",ret);
	major_dev=ret;
    return ret;
}

static void __exit test_exit(void)
{
    unregister_chrdev(major_dev,DEVICE_NAME);
    printk("led unreg magor num :[%d] success",major_dev);
}

module_init(test_init);
module_exit(test_exit);

执行insmod有如下打印:
在这里插入图片描述
3.动态创建设备号,并且自动生成设备文件。

#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<asm/io.h>
#include<linux/cdev.h>

MODULE_LICENSE("GPL");

#define DEVICE_NAME "test_dev1"


static int __init test_init(void);
static void __exit test_exit(void);
static int test_open(struct inode *inode,struct file *file);
static long test_ioctl(struct file *file,unsigned int cmd,unsigned long arg);


static int alloc_dev_num;
static dev_t num_dev;
struct cdev dev_c;
struct class *cdev_class;

static struct file_operations led_fops={
    .owner=THIS_MODULE,
    .open=test_open,
    .unlocked_ioctl=test_ioctl,
    //.ioctl=led_ioctl;
};


static int test_open(struct inode *inode,struct file *file)
{
    printk("test__open");
    return 0;
}

static long test_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    printk("test_ioctl cmd=[%d]",cmd);
    return 0;
}

static int __init test_init(void)
{
    int ret;
    ret=alloc_chrdev_region(&num_dev,0,1,DEVICE_NAME);
    alloc_dev_num=MAJOR(num_dev);
    printk("alloc dev num=[%d] ret=[%d]\n",alloc_dev_num,ret);
    cdev_init(&dev_c,&led_fops);
    printk("cdev_init!");
    ret=cdev_add(&dev_c,num_dev,1);
    printk("cdev_add ret=[%d]",ret);
    cdev_class=class_create(THIS_MODULE,DEVICE_NAME);
    if(IS_ERR(cdev_class))
    {
        printk("class_create error!");
    }
    printk("class_create success!");
    device_create(cdev_class,NULL,num_dev,0,DEVICE_NAME);
    return ret;
}

static void __exit test_exit(void)
{
    printk("test_exit");
    device_destroy(cdev_class,num_dev);
    class_destroy(cdev_class);
    cdev_del(&dev_c);
    unregister_chrdev_region(num_dev,1);
}

module_init(test_init);
module_exit(test_exit);

insmod后有如下输出
在这里插入图片描述
并且设备文件如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值