文章目录
- 1, 驱动和应用程序的设计思想
- 2,编写字符设备驱动的步骤和规范
- 3,操作寄存器地址的方式 readl/writel():
- 4,例---LED灯闪烁
- 4.1,驱动代码 led_drv.c
- 4.2,应用程序 led_test.c
- 4.3,Makefile
- 4.4,串口终端信息
1, 驱动和应用程序的设计思想
1.1,应用程序和驱动扮演的是什么角色
用户态:应用程序
玩策略: 怎么去做
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\n");
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\n");
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-------------\n",__FUNCTION__);
ret = copy_to_user(buf, &kernel_val, count);
if(ret > 0)
{
printk("copy_to_user error\n");
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-------------\n",__FUNCTION__);
ret = copy_from_user(&value, buf, count);
if(ret > 0)
{
printk("copy_from_user error\n");
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-------------\n",__FUNCTION__);
return 0;
}
int led_drv_close(struct inode *inode, struct file *filep)
{
printk("---------%s-------------\n",__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-------------\n",__FUNCTION__);
led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
if(led_dev == NULL)
{
printk(KERN_ERR "malloc error\n");
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\n");
}
else{
printk("register failed\n");
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\n");
ret = -ENODEV;
goto err_0;
}
else{
printk("register ok\n");
}
#endif
led_dev->cls = class_create(THIS_MODULE, "led_cls");
if(IS_ERR(led_dev->cls))
{
printk(KERN_ERR "class_create error\n");
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\n");
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\n");
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-------------\n",__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\n",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,串口终端信息