驱动 2.3 字符设备驱动编程框架

Linux内核绝大部分代码是由c语言实现的
但是Linux内核中大量运用了面向对象的编程思想

c语言实现面向对象主要依靠struct

在内核中实现一个字符设备(例如LED 键盘 鼠标 触摸屏…)的驱动程序
其本质就是实例化一个struct cdev类型的对象 并注册到内核中去即可

struct cdev { //字符设备驱动框架的核心数据结构
	...
	const struct file_operations *ops;
	dev_t dev;
};

1.设备号

本质就是一个32bit的无符号数据
设备号=主设备号(高12bit)
主设备号 用于区分不同类型的设备
次设备号 用于区分同一类设备中的不同个体
ls /dev/fb0 -l //lcd屏
ls /dev/ttySAC* -l
//uart控制器

1.1 静态申请注册设备号

找一个未被使用的主设备号为我所用
找的方式:cat /proc/devices //查看主设备号

cat /proc/devices

Character devices:
 主设备号     被哪类硬件模块占用
  1 		mem
  5 		/dev/tty
  5 		/dev/console
  5 		/dev/ptmx
 10 		misc
 13 		input
 21 		sg
 29 		fb
 81 		video4linux
 86 		ch
 89 		i2c                  
 ...       

申请注册注销设备号内核函数

int register_chrdev_region(dev_t from,unsigned count,const char *name);//注册设备函数
            作用:向内核注册一系列的设备号
            from, 要注册的第一设备号
            count,连续注册的个数
            name, 名称
void unregister_chrdev_region(dev_t from,unsigned count);          //注销设备函数
            作用: 注销一系列的设备号
            from, 要注销的起始设备好号
            count,连续注销个数

cat /proc/devices
vi led_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>

MODULE_LICENSE("GPL");

dev_t dev;   //num
unsigned int major = 200; //main num
unsigned int minor = 0; //ci   num
int __init led_drv_init(void){
    //dev = major<<20|minor;
    dev = MKDEV(major,minor);
    register_chrdev_region(dev,1,"myleds");
    return 0;
}

void __exit led_drv_exit(void){
    unregister_chrdev_region(dev,1);
}
module_init(led_drv_init);
module_exit(led_drv_exit);

vi Makefile

obj-m += led_drv.o
KERNEL = ../../../linux/kernel

all:
        make -C $(KERNEL) M=$(PWD) modules
clean:
        make -C $(KERNEL) M=$(PWD) modules

make 在开发板上执行
insmod led_drv.ko
cat /proc/devices 200 myleds …
rmmod led_drv
cat /proc/devices

3.1.2 动态申请注册设备号

由内核分配一个主设备号为我所用

动态分配设备号函数

int alloc_chrdev_region(dev_t *dev,unsigned minor,unsigned count,char *name);
          dev 传出参数
          ××××××××××××××××××××××××××××××××××××

2.操作函数集合

Linux中实现一个字符设备驱动,实则就是实例化一个struct cdev对象并注册到内核中去
××××××××××××××××××××××

3.内核中提供的关于cdev操作的api

void cdev_init(struct cdev *cdev,const struct file_operations *fops)
{
//将实现的操作函数集合和设备关联起来
}
//向内核注册cdev
//p,指向要注册的cdev
dev,设备号
count,要连续注册的个数
int cdev_add()
{
//将注册得到的设备号与该设备(cdev)绑定
}
void cdev_del(struct cdev *p) //指向的cdev从内核中注销掉

实验步骤:
vi led_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
MODULE_LICENSE("GPL");
dev_t dev;   //num
unsigned int major = 0; //main num
unsigned int minor = 5; //ci   num
struct cdev led_cdev;
int led_open(struct inode *inode,struct file *filp){
    printk("<2>" "OK %s\n",__func__);
    return 0;
}
int led_release(struct inode *inode,struct file *filp){
    printk("<2>" "OK %s\n",__func__);
    return 0;
}
struct file_operations led_fops={
    .owner = THIS_MODULE,
    .open  = led_open,
    .release = led_release,
};
int __init led_drv_init(void){
    if(major){
        //dev = major<<20|minor;
        dev = MKDEV(major,minor);
        register_chrdev_region(dev,1,"myleds");
    }
    else{
        alloc_chrdev_region(&dev,minor,1,"myleds");
        printk("<2>" "major=%d minor=%d\n",MAJOR(dev),MINOR(dev));
    }
    cdev_init(&led_cdev,&led_fops);
    cdev_add(&led_cdev,dev,1);
    return 0;
}
void __exit led_drv_exit(void){
    cdev_del(&led_cdev);
    unregister_chrdev_region(dev,1);
}
module_init(led_drv_init);
module_exit(led_drv_exit);

vi Makefile

obj-m += led_drv.o
KERNEL = ../../../linux/kernel

all:
        make -C $(KERNEL) M=$(PWD) modules
clean:
        make -C $(KERNEL) M=$(PWD) modules

make
vi test.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(void){
    int fd = 0;
    fd = open("/dev/myleds",O_RDWR);
    if(fd<0){
        perror("open failed");
        return -1; 
    }   
    printf("open myleds successed!\n");
    sleep(10);
    close(fd);
    return 0;
}

arm-cortex_a9-linux-gnueabi-gcc test.c -o test
cp test …/…/rootfs/
到开发板执行命令
insmod led_drv.ko
mknod /dev/myleds c 244 5
./test
结论:

没有增加新的系统调用号 就能调用内核中led_open led_release
通过操作设备文件/dev/myleds实现的
体现了:linux下一切皆文件

实现原理:

open("/dev/myleds", O_RDWR)
      5----->R7
      swi
sys_open(...){
       /dev/myleds ---->  c  244 5------> led_cdev
       ------->led_cdev.ops // &led_fops
       ------>led_cdev.ops->open(....) //led_fops.open(....) //led_open(....)
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值