嵌入式linux-嵌入式内核及驱动开发,字符设备驱动,驱动和应用程序的设计思想,编写字符设备驱动的步
2019-07-12 18:23 发布
生成海报
文章目录
1, 驱动和应用程序的设计思想
1.1,应用程序和驱动扮演的是什么角 {MOD}
用户态:应用程序
玩策略: 怎么去做
1, 一闪一闪
2,10s闪一次,也可以1s闪一次
3,一直亮
4,跑马灯
控制权是在应用程序(程序员)
--------------------------------------
内核态:驱动
玩机制: 能做什么
led:亮 和 灭
2,编写字符设备驱动的步骤和规范
2.1,步骤:
1,实现模块加载和卸载入口函数
module_init(chr_dev_init);
module_exit(chr_dev_exit);
2,在模块加载入口函数中
a,申请主设备号 (内核中用于区分和管理不同字符设备)
register_chrdev(dev_major, "chr_dev_test", &my_fops);
b,创建设备节点文件 (为用户提供一个可操作到文件接口--open())
struct class *class_create(THIS_MODULE, "chr_cls");
struct device *device_create(devcls, NULL, MKDEV(dev_major, 0), NULL, "chr2");
c, 硬件的初始化
1,地址的映射
gpx2conf = ioremap(GPX2_CON, GPX2_SIZE);
2,中断到申请
3,实现硬件的寄存器的初始化
// 需要配置gpio功能为输出
*gpx2conf &= ~(0xf<<28);
*gpx2conf |= (0x1<<28);
e,实现file_operations
const struct file_operations my_fops = {
.open = chr_drv_open,
.read = chr_drv_read,
.write = chr_drv_write,
.release = chr_drv_close,
};
2.2,规范:
1,面向对象编程思想
用一个结构体来表示一个对象
//设计一个类型,描述一个设备的信息
struct led_desc{
unsigned int dev_major; //设备号
struct class *cls;
struct device *dev; //创建设备文件
void *reg_virt_base;
};
struct led_desc *led_dev;//表示一个全局的设备对象
// 0(在init中第一步做), 实例化全局的设备对象--分配空间
// GFP_KERNEL 如果当前内存不够用到时候,该函数会一直阻塞(休眠)
// #include
led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
if(led_dev == NULL)
{
printk(KERN_ERR "malloc error
");
return -ENOMEM;
}
led_dev->dev_major = 250;
2,做出错处理
在某个位置出错了,要将之前申请到资源进行释放
led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
led_dev->dev_major = register_chrdev(0, "led_dev_test", &my_fops);
if(led_dev->dev_major < 0)
{
printk(KERN_ERR "register_chrdev error
");
ret = -ENODEV;
goto err_0;
}
err_0:
kfree(led_dev);
return ret;
3,操作寄存器地址的方式 readl/writel():
3.1,传统的方式
volatile unsigned long *gpxcon;
*gpxcon &= ~(0xf<<28);
3.2,内核提供的方式
readl/writel();
u32 readl(const volatile void __iomem *addr) //从地址中读取地址空间的值
void writel(unsigned long value , const volatile void __iomem *addr) // 将value的值写入到addr地址
例子1:
// gpio的输出功能的配置
u32 value = readl(led_dev->reg_virt_base);
value &= ~(0xf<<28);
value |= (0x1<<28);
writel(value, led_dev->reg_virt_bas);
例子2:
*gpx2dat |= (1<<7);
替换成:
writel( readl(led_dev->reg_virt_base + 4) | (1<<7), led_dev->reg_virt_base + 4 );
4,例—LED灯闪烁
4.1,驱动代码 led_drv.c
#include
#include
#include
#include
#include
#include
#include
#include
struct led_desc{
unsigned int dev_major;
struct class * cls;
struct device * dev;
void * reg_virte_base;
} ;
#define GPX2_CON 0x11000C40
#define GPX2_SIZE 8
struct led_desc * led_dev;
static int kernel_val = 555 ;
ssize_t led_drv_read ( struct file * filep, char __user * buf, size_t count, loff_t * fpos)
{
int ret;
printk ( "---------%s-------------
" , __FUNCTION__) ;
ret = copy_to_user ( buf, & kernel_val, count) ;
if ( ret > 0 )
{
printk ( "copy_to_user error
" ) ;
return - EFAULT;
}
return 0 ;
}
ssize_t led_drv_write ( struct file * filep, const char __user * buf, size_t count, loff_t * fops)
{
int ret;
int value;
printk ( "---------%s-------------
" , __FUNCTION__) ;
ret = copy_from_user ( & value, buf, count) ;
if ( ret > 0 )
{
printk ( "copy_from_user error
" ) ;
return - EFAULT;
}
if ( value)
{
writel ( ( readl ( led_dev-> reg_virte_base + 4 ) | ( 0x1 << 7 ) ) , led_dev-> reg_virte_base + 4 ) ;
}
else
{
writel ( ( readl ( led_dev-> reg_virte_base + 4 ) & ~ ( 0x1 << 7 ) ) , led_dev-> reg_virte_base + 4 ) ;
}
return 0 ;
}
int led_drv_open ( struct inode * inode, struct file * filep)
{
printk ( "---------%s-------------
" , __FUNCTION__) ;
return 0 ;
}
int led_drv_close ( struct inode * inode, struct file * filep)
{
printk ( "---------%s-------------
" , __FUNCTION__) ;
return 0 ;
}
const struct file_operations my_fops = {
. open = led_drv_open,
. read = led_drv_read,
. write = led_drv_write,
. release = led_drv_close,
} ;
static int __init led_dev_init ( void )
{
int ret;
printk ( "---------%s-------------
" , __FUNCTION__) ;
led_dev = kmalloc ( sizeof ( struct led_desc) , GFP_KERNEL) ;
if ( led_dev == NULL )
{
printk ( KERN_ERR "malloc error
" ) ;
return - ENOMEM;
}
#if 0
led_dev-> dev_major = 250 ;
ret = register_chrdev ( led_dev-> dev_major, "led_dev_test" , & my_fops) ;
if ( ret == 0 ) {
printk ( "register ok
" ) ;
}
else {
printk ( "register failed
" ) ;
ret = - EINVAL;
goto err_0;
}
#else
led_dev-> dev_major = register_chrdev ( 0 , "led_dev_test" , & my_fops) ;
if ( led_dev-> dev_major < 0 ) {
printk ( KERN_ERR "register_chrdev error
" ) ;
ret = - ENODEV;
goto err_0;
}
else {
printk ( "register ok
" ) ;
}
#endif
led_dev-> cls = class_create ( THIS_MODULE, "led_cls" ) ;
if ( IS_ERR ( led_dev-> cls) )
{
printk ( KERN_ERR "class_create error
" ) ;
ret = PTR_ERR ( led_dev-> cls) ;
goto err_1;
}
led_dev-> dev = device_create ( led_dev-> cls, NULL , MKDEV ( led_dev-> dev_major, 0 ) , NULL , "led%d" , 0 ) ;
if ( IS_ERR ( led_dev-> dev) )
{
printk ( KERN_ERR "device_create error
" ) ;
ret = PTR_ERR ( led_dev-> dev) ;
goto err_2;
}
led_dev-> reg_virte_base = ioremap ( GPX2_CON, GPX2_SIZE) ;
if ( led_dev-> reg_virte_base == NULL )
{
printk ( KERN_ERR "ioremap error
" ) ;
ret = - ENOMEM;
goto err_3;
}
#if 0
u32 value = readl ( led_dev-> reg_virte_base) ;
value = ( value & ~ ( 0xf << 28 ) ) | ( 0x1 << 28 ) ;
writel ( value, led_dev-> reg_virte_base) ;
#else
writel ( ( ( readl ( led_dev-> reg_virte_base) & ~ ( 0xf << 28 ) ) | ( 0x1 << 28 ) ) , led_dev-> reg_virte_base) ;
#endif
return 0 ;
err_3:
device_destroy ( led_dev-> cls, MKDEV ( led_dev-> dev_major, 0 ) ) ;
err_2:
class_destroy ( led_dev-> cls) ;
err_1:
unregister_chrdev ( led_dev-> dev_major, "led_dev_test" ) ;
err_0:
kfree ( led_dev) ;
return ret;
}
static void __exit led_dev_exit ( void )
{
printk ( "---------%s-------------
" , __FUNCTION__) ;
iounmap ( led_dev-> reg_virte_base) ;
device_destroy ( led_dev-> cls, MKDEV ( led_dev-> dev_major, 0 ) ) ;
class_destroy ( led_dev-> cls) ;
unregister_chrdev ( led_dev-> dev_major, "led_dev_test" ) ;
kfree ( led_dev) ;
}
module_init
( led_dev_init) ;
module_exit ( led_dev_exit) ;
MODULE_LICENSE ( "GPL" ) ;
4.2,应用程序 led_test.c
#include
#include
#include
#include
int main ( int argc, char * argv[ ] )
{
int fd;
int value = 0 ;
fd = open ( "/dev/led0" , O_RDWR) ;
if ( fd < 0 )
{
perror ( "led_test open" ) ;
exit ( 1 ) ;
}
read ( fd, & value, 4 ) ;
printf ( "__USER__ : value = %d
" , value) ;
while ( 1 )
{
value = 1 ;
write ( fd, & value, 4 ) ;
sleep ( 1 ) ;
value = 0 ;
write ( fd, & value, 4 ) ;
sleep ( 1 ) ;
}
close ( fd) ;
return 0 ;
}
4.3,Makefile
ROOTFS_DIR = /nfs/rootfs
APP_NAME = led_test
MODULE_NAME = led_drv
CROSS_COMPILE = arm-none-linux-gnueabi-
CC = $( CROSS_COMPILE) gcc
ifeq ( $( KERNELRELEASE) ,)
KERNEL_DIR = /home/linux/linux-3.14.79
CPU_DIR = $( shell pwd )
all:
make -C $( KERNEL_DIR) M= $( CPU_DIR) modules
@
@
$( CC) $( APP_NAME) .c -o $( APP_NAME)
clean:
make -C $( KERNEL_DIR) M= $( CPU_DIR) clean
rm $( APP_NAME)
install:
sudo cp -raf *.ko $( APP_NAME) $( ROOTFS_DIR) /drv_module
else
obj-m += $( MODULE_NAME) .o
endif
4.4,串口终端信息
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮