经过最近这几天学习字符设备驱动,我发现学习字符设备驱动模块编写是一个剥洋葱的过程,不考虑设备文件是手动生成还是利用mdev生成,仅仅考虑注册字符设备,编写模块加载函数仅仅需要一个核心函数register_chrdev()这个函数里面又包含了很多函数,一层一层的播下去就能搞懂驱动在linux中的工作原理。我现在对字符设备理解的还不透彻,深入的学习笔记以后再写,今天主要记录根据led的驱动熟悉字符设备驱动框架这个洋葱最外层的函数。
这部分的学习主要是模仿韦东山的视频,具体问题看宋宝华的《linux设备驱动开发详解》,如果说韦东山视频中的代码算洋葱的第一层,name宋宝华的模板代码就算第二层,今天先看第一层。首先cat /proc/devices下使用的主设备号(要先挂载proc文件系统),在/dev目录下用未使用的设备号(假设为xxx)建立设备文件。其实设备文件可以让linux自动生成,这涉及到busybox中使用mdev的知识,使用mdev的方法并不难百度上的资料说的都很清楚。
头文件在博客里显示不出来,就不贴了
volatile unsigned long *gpbcon = NULL;
volatile unsigned long *gpbdat = NULL;//mini2440上的led接在gpb上
int major=xxx;
static int first_drv_open(struct inode *inode, struct file *file);
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos);
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write = first_drv_write,
};//这里的file_operations结构体对应的就是/dev下代表led的设备文件,它记录了该设备文件所使用的模块和函数。
static int first_drv_init(void)
{
register_chrdev(major, "first_drv", &first_drv_fops); // 注册设备,把主设备号为major的设备文件和first_drv_fops这个文件结构体绑定
gpbcon = (volatile unsigned long *)
ioremap(0x56000010, 16);//ioremap(baseaddress, length)物理地址映射
gpbdat = gpbcon + 1;
return 0;
}
static int first_drv_open(struct inode *inode, struct file *file)
{
printk("first_drv_open\n");
*gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2))| (0x3<<(8*2)));
*gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2))| (0x1<<(8*2)));//配置led
//*gpbcon &= ~(0x3<<0) ;
//*gpbcon |= (0x1<<0);//配置蜂鸣器
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
char val;
printk("first_drv_write\n");
copy_from_user(&val, buf, count); //因为我们最终操作硬件是在用户空间,在用户空间向内核空间写值需要copy_from_user()
if (val == 1)
{
// 点灯
*gpbdat &= ~((1<<5) | (1<<6) | (1<<7)| (1<<8));//led
//*gpbdat |= (1<<0);//蜂鸣器
}
else
{
// 灭灯
*gpbdat |= ((1<<5) | (1<<6) | (1<<7)| (1<<8));//led
//*gpbdat &= ~(1<<0);//蜂鸣器
}
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸载设备
iounmap(gpbcon);//解除物理地址映射
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
当我们编写测试程序检验驱动模块时,发现只有两个led被控制,另外两个从内核启动时就一直在像心脏一样闪,应该是linux内核中自动加载了什么模块
果然在linux配置中发现有led support这一项,里面有两个选项被选中了,led并不重要直接m掉,重新编译内核,烧写后重启发现果然那两个led不闪了
阅读(1887) | 评论(0) | 转发(0) |