设备号的分配
静态分配:
动态分配:
/**
* 功能:动态申请设备号
* dev:存放返回的设备号的结构体
* firstminor:指定次设备号
* count:连续编号范围
* name:编号相关联的设备名称. (/proc/devices)
**/
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
/**
* 功能:释放设备号
**/
void unregist_chrdev_region(dev_t first,unsigned int count);
案例 chardevice.文件:
#include<linux/init.h>
#include<linux/module.h>
static int major = 0;
static int chardevice_init(void){
dev_t dev;
if(major != 0){//静态分配
dev = MKDEV(major,0);//构建一个设备号
//向内核注册设备号
register_chrdev_region(dev,1,"tarena");
}else{
//动态申请注册设备号
alloc_chrdev_region(&dev,100,1,"test");
major = MAJOR(dev);//获取主设备号 相当于dev>>20
unsigned int minor = MINOR(dev);//获取次设备号 相当于把高20位清0
printk("major = %d ! \n",major);
}
printk("init ! \n");
return 0;
}
static void chardevice_exit(void){
dev_t dev;
dev = MKDEV(major,0);
//释放
unregist_chrdev_region(dev,1);
}
module_init(chardevice_init);
module_exit(chardevice_exit);
MODULE_LICENSE("GPL");
然后编写chardevice:
obj -m += chardevice.o
KDIR = /opt/kernel #指定内核源码路径
all :
make -C $(KDIR) SUBDIRS = $(PWD) modules
clean:
make -C $(KDIR) SUBDIRS = $(PWD) clean
编译、拷贝到开发板、安装模块;
静态注册、动态注册设备好哪个好?
静态注册优点:简单;驱动加载前已经知道设备好,可以提前创建设备文件
静态注册缺点:容易引起设备号冲突;保留的可用主设备号资源有限;
动态注册优点:简单;便于驱动推广;
动态注册缺点:驱动程序被加载前设备号还没有设备,也就不能为设备提前创造设备文件;
cdev操作步骤与相关方法
1. 分配一个cdev,有两种方法:
1),cdev_alloc
2),struct cdev led_cdev
2. 初始化cdev
cdev_init(*cdev,*file_operations)
3. 添加cdev到内核
cdev_add(...);
4. 从内核中删除cdev
cdev_del(...)
cdev案例:
#include<linux/init.h>
#include<linux/module.h>
static int led_open(struct inode *inode, struct file *file)
{
printlk("enter led_open! \n");
return 0;
}
static int led_close(struct inode *inode, struct file *file)
{
printlk("enter led_close! \n");
return 0;
}
static ssize_t led_write (struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
printlk("enter led_write! \n");
return 0;
}
static ssize_t led_read(struct file *file,char __user *buf, size_t count, loff_t *offset)
{
printlk("enter led_read! \n");
return 0;
}
static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd,unsigned long val)
{
printlk("enter led_ioctl! \n");
}
//需要实现的函数
static struct file_operations led_fops = {
.owner = THIS_MOUDLE,
.open = led_open,
.release = led_close,
.write = led_write,
.read = led_read,
.ioctl = led_ioctl,
};
static int major = 0;
/*1,分配一个cdev */
static struct cdev led_cdev;
static int chardevice_init(void){
dev_t dev;
if(major != 0){//静态分配
dev = MKDEV(major,0);//构建一个设备号
//向内核注册设备号
register_chrdev_region(dev,1,"tarena");
}else{
//动态申请注册设备号
alloc_chrdev_region(&dev,100,1,"test");
major = MAJOR(dev);//获取主设备号 相当于dev>>20
unsigned int minor = MINOR(dev);//获取次设备号 相当于把高20位清0
printk("major = %d ! \n",major);
}
//2, 初始化cdev
cdev_init(&led_cdev,&led_fops);
//3,把cdev添加到内核里面去
cdev_add(&led_cdev,dev,1);
return 0;
}
static void chardevice_exit(void){
dev_t dev;
//4,按照对内核产生的影响,逆序销毁cdev
cdev_del(&led_cdev);
dev = MKDEV(major,0);
//释放
unregist_chrdev_region(dev,1);
}
module_init(chardevice_init);
module_exit(chardevice_exit);
MODULE_LICENSE("GPL");
Makefile:
obj -m += chardevice.o
KDIR = /opt/kernel #指定内核源码路径
all :
make -C $(KDIR) SUBDIRS = $(PWD) modules
clean:
make -C $(KDIR) SUBDIRS = $(PWD) clean
测试程序:在用户空间写一个test.c main,然后调用
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int main(void){
int fd;
fd = open("/dev/abc",o_RDWR);
if(fd<0){
printlf("open /dev/abc failed");
return -1;
}
char buf[1024] = {0};
int count = 100;
read(fd,buf,count);//读取100个字节到buf去
sleep(1);
write(fd,buf,count);
sleep(1);
ioctl(fd,12,34);
sleep(1);
close(fd);
return 0;
}
将.ko和test.o复制到开发板去,然后安装模块;
然后手工创建一个设备节点文件:
mknod /dev/abc c 250 #c代表字符设备 250主设备号(250对应模块动态分配的设备号)
然后执行 ./test
file_operations结构体中经常需要实现的函数
file_operations{
open
release
read
write
ioctrl
mmap
}
我们可以手工添加设备文件:
mknod /dev/abc c250 100
c:字符设备文件
250:主设备文件
100:次设备文件
cdev 参考这里写链接内容
内核空间与用户空间数据交换的问题
copy_to_user 从内核空间向用户空间拷贝
copy_from_user 从用户空间向内核空间拷贝
/**
*To:用户空间函数 (可以是数组)
*From:内核空间函数(可以是数组)
*sizeof(from):内核空间要传递的数组的长度
*count:
**/
unsigned long copy_to_user(void *to, const void __user *from, usigned long count);
unsigned long copy_from_user(void __user *to, const void *from, usigned long count);
put_user 往用户空间放数据
get_user 从用户空间取数据
后两个只能完成简单数据的拷贝,如字节、半字、字
前者可以完成批量数据拷贝
以上函数比memcpy多了一个权限校验。
LED案例操作
write(fd,1) //亮灯
write(fd,0) //亮灯
read(fd,buf) //1 亮 0灭
#include<linux/init.h>
#include<linux/module.h>
static int led_open(struct inode *inode, struct file *file)
{
printlk("enter led_open! \n");
return 0;
}
static int led_close(struct inode *inode, struct file *file)
{
printlk("enter led_close! \n");
return 0;
}
//假如在用户空间写入:write(fd,buf,1) buf中的值1亮 0 灭
static ssize_t led_write (struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
int cmd;
copy_from_user(&cmd,buf,sizeof(cmd));//将用户空间的数据拷贝到内核空间
if(cmd == 1){
gpio_set_value(S5PV210_GPC1(3),1);//点亮
led_status = 1;
}else{
gpio_set_value(S5PV210_GPC1(3),-);
led_status = 0;
}
return count;
}
//read(fd,buf,cnt)
static ssize_t led_read(struct file *file,char __user *buf, size_t count, loff_t *offset)
{
//把灯的状态返回给用户空间
copy_to_user(buf,&led_status,sizeof(led_status));
return count;
}
static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd,unsigned long val)
{
printlk("enter led_ioctl! \n");
}
//需要实现的函数
static struct file_operations led_fops = {
.owner = THIS_MOUDLE,
.open = led_open,
.release = led_close,
.write = led_write,
.read = led_read,
.ioctl = led_ioctl,
};
static int major = 0;
/*1,分配一个cdev */
static struct cdev led_cdev;
static int led_status;//记录灯的状态
static int chardevice_init(void){
dev_t dev;
if(major != 0){//静态分配
dev = MKDEV(major,0);//构建一个设备号
//向内核注册设备号
register_chrdev_region(dev,1,"tarena");
}else{
//动态申请注册设备号
alloc_chrdev_region(&dev,100,1,"test");
major = MAJOR(dev);//获取主设备号 相当于dev>>20
unsigned int minor = MINOR(dev);//获取次设备号 相当于把高20位清0
printk("major = %d ! \n",major);
}
//2, 初始化cdev
cdev_init(&led_cdev,&led_fops);
//3,把cdev添加到内核里面去
cdev_add(&led_cdev,dev,1);
//申请GPIO管脚
gpio_request(S5PV210_GPC1(3),"LED1");//从原图中找管脚名称GPC1_3
//配置为输出
gpio_dirction_output(S5PV210_GPC1(3),0);
led_status = 0;
return 0;
}
static void chardevice_exit(void){
dev_t dev;
//4,按照对内核产生的影响,逆序销毁cdev
cdev_del(&led_cdev);
dev = MKDEV(major,0);
//释放
unregist_chrdev_region(dev,1);
}
module_init(chardevice_init);
module_exit(chardevice_exit);
MODULE_LICENSE("GPL");
Makefile文件略
test.c文件
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int main(void){
int fd;
char buf[1024] = {0};
int count = 100;
fd = open("/dev/abc",o_RDWR);
if(fd<0){
printlf("open /dev/abc failed");
return -1;
}
buf[0] =1 ;
write(fd,buf,4);
sleep(3);
read(fd,&(buf[1]),4);
if(buf[0] ==1 ){//读到状态为1
printlf("LED ON \n");
}else{
printlf("LED OFF \n");
}
close(fd);
return 0;
}