class="markdown_views prism-github-gist">
嵌入式Linux驱动 GPIO操作改进 包括从驱动到测试程序
说明
这篇博客记录的是上一次编写GPIO驱动的改进版本。这里改进了两个地方:
- 面向对象的编程思想 结构体来表示一个对象
- 做出错判断
字符的驱动编写小结
字符设备驱动编写步骤
这里出现的代码是从后面的程序拷贝出来的。
字符设备驱动编写步骤:
- 实现加载和卸载的入口函数
- module_init(chr_io_init);
- module_exit(chr_io_exit);
- 模块加载入口函数中:
- 申请主设备号(用于区分和管理不同的字符设备)
- register_chrdev(DEV_MAJOR , “io”,&file_ops);
- 创建设备节点文件(为用户提供一个可操作的文件接口)
- struct class* class_create(THIS_MODULE, “io_cls”);
- struct device* device_create(devcls, NULL, dev_num, NULL, “io”);
- 硬件的初始化
- 地址的映射
ioremap(GPIOL2_CON, LEN);
- 中断的申请
- 实现硬件的寄存器初始化
- 实现file_operations(文件接口的实现)
操作寄存器的方式总结
- 操作寄存器地址的方式
- 举例:volatile unsigned long *gpxcon;
- gpxcon &=~(1<<10);
- static inline u32 readl(const volatile void __iomem *addr)
//从寄存器读值
static inline void writel(unsigned int b, volatile void __iomem *addr)
//向寄存器写值
- 举例(这里的代码是会在后面出现的)
u32 value = readl(led_dev->reg_virt_base);
value &=~(1<<0);
value |=1<<0;
writel(value, led_dev->reg_virt_base);
gpiol20_dat = gpiol20_cof+1;
writel(read(led_dev->reg_virt_base+4) |(1<<0), led_dev->reg_virt_base+4);
代码
#include
#include
#include
#include
#include
#include
#include
#include
#define GPIOL2_CON 0x11000000+0x0100
#define LEN 8
struct led_desc{
unsigned int dev_major;
struct class *cls;
struct device *dev;
void* reg_virt_base;
};
static struct led_desc *led_dev;
ssize_t chr_drv_read (struct file *filp, char __user *buffer, size_t count, loff_t *fpos)
{
u32 value=0;
int ret;
printk("-------%s-------
",__FUNCTION__);
value = readl(led_dev->reg_virt_base+4);
value &=1<<0;
ret = copy_to_user(buffer, &value, count);
return 0 ;
}
ssize_t chr_drv_write (struct file *flip, const char __user *buffer, size_t count, loff_t *fpos)
{
int ret;
int value;
static int num=0;
printk("write:%d ",num++);
ret = copy_from_user(&value, buffer, count);
if(value == 1)
{
printk("vaule:%d
",value);
writel(readl(led_dev->reg_virt_base+4) |(1<<0), led_dev->reg_virt_base+4);
}
else
{
printk("vaule:%d
",value);
writel(readl(led_dev->reg_virt_base+4) & ~(1<<0), led_dev->reg_virt_base+4);
}
return 0;
}
int chr_drv_open (struct inode *inode, struct file *filp)
{
printk("-------%s-------
",__FUNCTION__);
return 0;
}
int chr_drv_release (struct inode *inode, struct file *filp)
{
printk("-------%s-------
",__FUNCTION__);
return 0;
}
static struct file_operations file_ops={
.owner = THIS_MODULE,
.open = chr_drv_open,
.read = chr_drv_read,
.write = chr_drv_write,
.release = chr_drv_release,
};
static int __init chr_io_init(void)
{
int ret;
u32 value;
printk("-----------%s--------
",__FUNCTION__);
led_dev = kmalloc(sizeof(struct led_desc), GFP_KERNEL);
if(led_dev == NULL)
{
printk(KERN_ERR "malloc error!
");
return -ENOMEM;
}
led_dev->dev_major = register_chrdev(0 , "led",&file_ops);
if(led_dev->dev_major < 0)
{
printk(KERN_ERR "register_chrdev error!
");
ret = -ENODEV;
goto err_0;
}
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_virt_base = ioremap(GPIOL2_CON, LEN);
if(IS_ERR(led_dev->reg_virt_base))
{
printk(KERN_ERR "ioremap error!
");
ret = -ENOMEM;
goto err_3;
}
value = readl(led_dev->reg_virt_base);
value &=~(1<<0);
value |=1<<0;
writel(value, led_dev->reg_virt_base);
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, "led0");
err_0:
kfree(led_dev);
return ret;
}
static void __exit chr_io_exit(void)
{
printk("-----------%s--------
",__FUNCTION__);
iounmap(led_dev->reg_virt_base);
unregister_chrdev(led_dev->dev_major, "led0");
device_destroy(led_dev->cls, MKDEV(led_dev->dev_major,0));
class_destroy(led_dev->cls);
}
MODULE_LICENSE("GPL");
module_init(chr_io_init);
module_exit(chr_io_exit);