一、设备驱动分类
设备驱动分为三大类:字符设备、块设备、网络设备
1.字符设备
该设备对数据的处理按照字节流的形式进行的,支持顺序访问(是有时间的概念),也可以支持随机访问
典型的字符设备:串口、键盘、触摸屏、摄像头、I2C、SPI、声卡、帧缓冲设备
顺序访问的设备:串口、键盘、触摸屏
随机访问的设备:帧缓冲设备
2.块设备
该设备的处理按照若干个块来进行的。一个块的固定大小512字节、4096字节。这类设备支持随机访问,这种以块和随机访问能够提高数据存储效率。
块设备往往是面向于存储类的设备: nand flash、SD卡、U盘、eMMC、硬盘
3.网络设备
网络设备是比较特殊的,在/dev没有设备文件,它就是专门针对网络设备的一类驱动,其主要作用是进行网络的数据收发。
网络类设备:有线网卡、无线WiFi网卡(RT3070)、无线GPRS网卡、无线4G网卡
应用程序:socket套接字
二、设备号
主设备号:区分某一类的设备。
ttySAC这是串口设备,主设备号为204;
mmcblk0这是电子硬盘属于块设备,主设备号为179.
次设备号:用于区分同一类设备的不同个体或不同分区。
串口设备:串口0串口3,则使用次设备号6467进行区分。
电子硬盘设备:分区1分区7,则使用次设备号17进行区分。
设备号 = 主设备号 + 次设备号
三、code
基本流程:
申请设备号
1)静态注册
MKDEV
register_chrdev_region
2)动态注册
alloc_chrdev_region
定义一个字符设备,struct cdev
定义file_operations,初始化打开、关闭、读、写等函数接口。
cdev初始化
.cdev_init
将cdev加入到内核
.cdev_add
创建设备文件
.手动创建,使用mknod去/dev目录进行创建
mknod [设备名称] [设备类型] [主设备号] [次设备号]
.自动创建
1)class_create
2)device_create
led_drv.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
static dev_t myled_num; //设备号
static struct cdev led_cdev; //设备
static struct class *led_class; //设备类
static struct device *led_device; //设备文件信息
int myled_release (struct inode *inode, struct file *file);
int myled_open (struct inode *inode, struct file *file);
ssize_t myled_read (struct file *, char __user *, size_t, loff_t *);
ssize_t myled_write (struct file *, const char __user *, size_t, loff_t *);
// long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
static struct file_operations led_ops = {
.owner = THIS_MODULE,
.open = myled_open,
.release = myled_release,
.write = myled_write,
.read = myled_read,
// .unlocked_ioctl = ,
};
int myled_release(struct inode *inode, struct file *file)
{
printk("<6>""myled_release\n");
return 0;
}
int myled_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO"myled_open\n");
return 0;
}
ssize_t myled_read (struct file *file, char __user *buf, size_t len, loff_t *loff)
{
int ret;
char kbuf[] = "hello world";
ret = copy_to_user(buf,kbuf,sizeof kbuf);//返回未拷贝成功的字节数
ret = len - ret;
printk(KERN_INFO"myled_read\n");
return ret;
}
ssize_t myled_write (struct file *file, const char __user *buf, size_t len, loff_t *loff)
{
int ret;
char kbuf[2] = {0};
if(len > sizeof(kbuf))
len = sizeof(kbuf);
ret = copy_from_user(kbuf,buf,len); //返回未拷贝成功的字节数
ret = len - ret;//得到成功拷贝的字节数
printk(KERN_INFO"kbuf[0] = %d kbuf[1] = %d\n",*kbuf,kbuf[1]);
printk(KERN_INFO"myled_write\n");
return ret;
}
static int __init myled_init(void)
{
int ret;
#ifdef _STATIC_REGISTCHAR_DEV
// 1)静态注册
myled_num = MKDEV(240,0);
ret = register_chrdev_region(myled_num,1,"myled");//cat /proc/devices
if(ret < 0)
{
printk("<7>""register_chrdev_region\n");
goto err_register_chrdev_region;
}
//mknod /dev/myled c 243 0
#endif
#if 1
// 2)动态注册
ret = alloc_chrdev_region(&myled_num,0,1,"myled");
if(ret < 0)
{
printk("<7>""err_alloc_chrdev_region\n");
goto err_alloc_chrdev_region;
}
printk("<6>""major = %d minor = %d\n",MAJOR(myled_num),MINOR(myled_num));
//mknod /dev/myled c 243 0
#endif
//设备初始化
cdev_init(&led_cdev,&led_ops);
//将设备加载到内核
ret = cdev_add(&led_cdev,myled_num,1);
if(ret < 0)
{
printk("<7>""err_cdev_add\n");
goto err_cdev_add;
}
//创建设备类-myled类 /sys/misc
led_class = class_create(THIS_MODULE,"myled");
if(IS_ERR(led_class))
{
printk("<7>""err_class_crete\n");
goto err_class_crete;
}
//添加设备信息 设备号 设备文件名
led_device = device_create(led_class,NULL,myled_num,NULL,"myled");
if(IS_ERR(led_device))
{
printk("<7>""err_device_create\n");
goto err_device_create;
}
//ls /dev/myled
//ls /sys/class/myled
//cat /sys/class/myled/myled/uevent
//cat /sys/devices/virtual/myled/myled/uevent
printk("<6>""myled_init\n");
return 0;
// err_register_chrdev_region: //静态注册设备出错
// return ret;
err_device_create:
class_destroy(led_class);
err_class_crete:
cdev_del(&led_cdev);
err_cdev_add:
unregister_chrdev_region(myled_num,1);
return ret;
err_alloc_chrdev_region:
return ret;
}
static void __exit myled_exit(void)
{
//从设备类中销毁设备信息
device_destroy(led_class,myled_num);
//销毁设备类
class_destroy(led_class);
//从内核中删除字符设备
cdev_del(&led_cdev);
//设备号的注销
unregister_chrdev_region(myled_num,1);
printk("<6>""myled_exit\n");
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
/*
申请设备号
1)静态注册
MKDEV
register_chrdev_region
2)动态注册
alloc_chrdev_region
定义一个字符设备,struct cdev
定义file_operations,初始化打开、关闭、读、写等函数接口。
cdev初始化
.cdev_init
将cdev加入到内核
.cdev_add
创建设备文件
.手动创建,使用mknod去/dev目录进行创建
mknod [设备名称] [设备类型] [主设备号] [次设备号]
.自动创建
1)class_create
2)device_create
*/
test.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#define DEVICE_DIR "/dev/myled"
int main(int argc, char const *argv[])
{
char buf[2] = {7,1};
char rbuf[10];
int len;
int fd_led = open(DEVICE_DIR,O_RDWR);
if(fd_led < 0)
{
perror("open /dev/myled");
return -1;
}
len = write(fd_led,buf,2);
printf("write = %d len = %d\n",*buf,len);
bzero(rbuf,10);
len = read(fd_led,rbuf,10);
printf("read = %s len = %d\n",rbuf,len);
sleep(1);
close(fd_led);
return 0;
}
Makefile
obj-m += led_drv.o
KERNEL_DIR :=/kernel
CROSS_COMPILE :=linux/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)
default:
$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modules
$(MAKE) clean
arm-linux-gcc test.c -o test
clean:
rm *.o *.order .*.cmd *.mod.c *.symvers *.tmp_versions -rf