编写驱动程序,首先要了解是什么类型的设备。linux下的设备分为三类,分别为:字符设备,块设备和网络设备。字符设备类型是根据是否以字符流为数据的交换方式,大部分设备都是字符设备,如键盘,串口等,块设备则是以块为单位进行管理的设备,如,磁盘。网络设备就是网卡等。
其次要了解应用程序和驱动程序的区别,两者的主要区别分为以下三点:
1入口函数的任务不相同,应用程序完成一个任务,驱动只完成初始化工作,比如中断
申请,寄存器设置,定时器设置。
2运行时的cpu模式不相同,驱动具有很高的权限,应用程序是在用户态下运行,而驱
动程序是在内核态下执行。
3 驱动程序不能调用C库函数,内核为驱动程序提供一些函数。如printk(KERN_NOTICE fmt, ##arg),第一个参数为打印级别,有如下的打印级别:
KERN_EMERG 用于紧急事件,一般是系统崩溃前的提示信息
KERN_ALERT 用于需要立即采取动作的场合
KERN_CRIT 临界状态,通常设计验证的硬件或软件操作失败
KERN_ERR 用于报告错误状态.设备驱动程序通常会用它报告来自硬件的问题
KERN_WARNING 就可能出现的问题提出警告.这些问题通常不会对系统造成严重破坏
KERN_NOTICE 有必要提示的正常情况.许多安全相关的情况用这个级别汇报
KERN_INFO 提示性信息.有很多驱动程序在启动时用这个级别打印相关信息
KERN_DEBUG 用于调试的信息
u_long copy_from_user(void *to, const void *from, u_long len),由用户态拷贝到内核态;
u_long copy_to_user(void * to, const void *from, u_long len),由内核态拷贝到用户态。
鉴于以上区别,驱动程序需要完成以下三点基本功能:
1:要对设备进行初始化和释放功能模块,就如上面的寄存器设置,中断的申请,向内核注
册驱动程序(register_chrdev()),卸载驱动程序(unregister_chrdev())。
2:能进行数据传输,在read(),write()函数里具体实现,数据传输工作。
3:能进行控制操作,给用户提供的ioctl()函数里可实现一些用户的选择性设置功能。
确定一个设备的执行函数集(结构体)
static struct file_operations myGPIO_fops = {
owner: THIS_MODULE,
write: myGPIO_write,
read: myGPIO_read,
ioctl: myGPIO_ioctl,
open: myGPIO_open,
release: myGPIO_release,
};
接下来是初始化工作,需要写在一个init()函数中,这个函数是独立的也是自动执行的,在这之中主要是对一些寄存器进行初始化操作。同样需要完成卸载驱动模块。
myGPIO_Major = register_chrdev(0, DRIVER_NAME, &myDriver_fops);
上面的程序完成设备号的注册,第一个参数为主设备号,一般为0,由系统来分配。
第二个参数为设备名,这需要在/dev/(/dev目录下设备名由命令 <mknod 设备名 C 主设备号 从设备号>来生成)目录下出现的设备名相符合。相反的在卸载中就取消注册
unregister_chrdev(myGPIO_Major, DRIVER_NAME);
最后将这两个模块加入到内核中,由程序段的最后两行完成。
static int __init myGPIO_init(void)
{
PRINTK("GPIO init/n");
myGPIO_Major = register_chrdev(0, DRIVER_NAME, &myGPIO_fops);
if(myGPIO_Major < 0)
{
PRINTK("register char device fail!/n");
return myGPIO_Major;
}
PRINTK("register myGPIO OK! Major = %d/n", myGPIO_Major);
#ifdef CONFIG_DEVFS_FS
devfs_myDriver_dir = devfs_mk_dir(NULL, "GPIO", NULL);
devfs_myDriver_raw = devfs_register(devfs_myDriver_dir, "raw0", DEVFS_FL_DEFAULT, myGPIO_Major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &myGPIO_fops, NULL);
PRINTK("add dev file to devfs OK!/n");
#endif
return 0;
}
static void __exit myGPIO_exit(void)
{
/* Module exit code */
PRINTK("GPIO exit/n");
/* Driver unregister */
if(myGPIO_Major > 0)
{
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_myDriver_raw);
devfs_unregister(devfs_myDriver_dir);
#endif
unregister_chrdev(myGPIO_Major, DRIVER_NAME);
}
return;
}
MODULE_AUTHOR("LiuFan");
MODULE_LICENSE("Dual BSD/GPL");
module_init(myGPIO_init);
module_exit(myGPIO_exit);
设备执行函数功能的实现将在下面完成。如结构体的函数,但并不是全都需要实现。open()函数中是执行一些设备工作前的初始化工作。rlease()则是将设备的相关寄存器恢复到原来的值。read()函数是将设备中的数据拷贝到内核,write()函数是将内核数据拷贝到对应的设备中。MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT两个宏是提供给系统对硬件资源进行控制访问的。在open()和rlease()两个函数中最基本的操作应是实现以上两个宏的操作。
static unsigned char myGPIO_Buffer[1024*1024];
/* Driver Operation Functions */
static int myGPIO_open(struct inode *inode, struct file *filp)
{
// int Minor = MINOR(inode->i_rdev);
// filp->private_data = 0;
MOD_INC_USE_COUNT;
PRINTK("myDriver open called!/n");
return 0;
}
static int myGPIO_release(struct inode *inode, struct file *filp)
{
// int Minor = MINOR(inode->i_rdev);
MOD_DEC_USE_COUNT;
PRINTK("myDriver release called!/n");
return 0;
}
static ssize_t myGPIO_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
char dat;
size_t read_size = count;
PRINTK("GPIO read called!/n");
PRINTK("/tcount=%d, pos=%d/n", count, (int)*f_pos);
/* if(*f_pos >= sizeof(myGPIO_Buffer))
{
PRINTK("[GPIO read]Buffer Overlap/n");
*f_pos = sizeof(myGPIO_Buffer);
return 0;
}
if((count + *f_pos) > sizeof(myGPIO_Buffer))
{
PRINTK("count + f_pos > sizeof buffer/n");
read_size = sizeof(myGPIO_Buffer) - *f_pos;
}*/
dat= GPFDAT;
copy_to_user(buf,&dat,1);
// *f_pos += read_size;
return read_size;
}
static ssize_t myGPIO_write(struct file *filp,const char *buf, size_t count, loff_t *f_pos)
{
char dat;
size_t fill_size = count;
PRINTK("myDriver write called!/n");
PRINTK("/tcount=%d, pos=%d/n", count, (int)*f_pos);
if(*f_pos >= sizeof(myGPIO_Buffer))
{
PRINTK("[myDriver write]Buffer Overlap/n");
*f_pos = sizeof(myGPIO_Buffer);
return 0;
}
if((count + *f_pos) > sizeof(myGPIO_Buffer))
{
PRINTK("count + f_pos > sizeof buffer/n");
fill_size = sizeof(myGPIO_Buffer) - *f_pos;
}
copy_from_user(&dat,buf,fill_size);
GPFDAT = dat;
// *f_pos += fill_size;
return fill_size;
}
控制ioctl() 函数则是提供给应用层的接口函数,功能并不是固定的,由开发者定义,一般都是对硬件的一些除过上述功能的其他操作。
static int myGPIO_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int i;
unsigned int mask=0x01;
GPFUP = 0x00;
PRINTK("myGPIO ioctl called(%d)!/n", cmd);
switch(cmd)
{
case MOD_IN:
for(i=0;i<8;i++)
{
if((mask & arg)!=0x0)
{
GPFCON &=~(3<<i*2);
}
mask =mask << 1;
}
break;
case MOD_OUT:
PRINTK("IOCTRL 0 called(0x%lx)!/n", arg);
for(i=0;i<8;i++)
{
if((mask & arg)!=0x00)
{
GPFCON &= ~(3 <<( i*2));
GPFCON |=(0x01<<(i*2));
}
mask=mask<<1;
}
break;
case MOD_EXIT_INT:
PRINTK("IOCTRL 1 called(0x%lx)!/n", arg);
GPFDAT = 0xFFFFFF00;
break;
default:
break;
}
return 0;
}
Linux下GPIO驱动的更多相关文章
-
Linux下GPIO驱动(三) ----gpio_desc()的分析
上篇最后提出的疑问是结构体gpio_chip中的成员函数set等是怎么实现的,在回答之前先介绍下gpio_desc这个结构体. 如上图所示,右上方部分为GPIO驱动对其它驱动提供的GPIO操作接口,其 ...
-
很好的linux下GPIO驱动详解文章
原文地址 http://blog.csdn.net/llxmedici/article/details/6282372 打算跟着友善之臂的<mini2440 linux移植开发指南>来做 ...
-
Linux下GPIO驱动(五) ----misc_register();
// struct miscdevice { int minor; const char *name; const struct file_operations *fops; struct list_ ...
-
Linux下GPIO驱动(一) ----一个简单的LED驱动
/******************************* * *杂项设备驱动:miscdevice *majior=10; * * *****************************/ ...
-
Linux下GPIO驱动(四) ----gpio_request();gpio_free();
//gpio_request申请gpio口 int gpio_request(unsigned gpio, const char *label) { struct gpio_desc *desc; s ...
-
Linux下GPIO驱动(二) ----s3c_gpio_cfgpin();gpio_set_value();
首先来看s3c_gpio_cfgpin(); int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) { struct s3c_gpio_ ...
-
飞思卡尔imx6开发板Linux下GPIO驱动
控制GPIO_1_28的输出: #define MY_BOMB_GPIO IMX_GPIO_NR(1, 28) 配置为输出方式: gpio_direction_output (MY_BOM ...
-
调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之二
/** ****************************************************************************** * @author 暴走的小 ...
-
调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之一
/** ****************************************************************************** * @author 暴走的小 ...
随机推荐
-
postman使用之二:数据同步和创建测试集
数据同步 启动postman 后在右上角可以登录账号,登录后就可以同步自己的api测试脚本,连上网在办公区在家都可以同步. 创建测试集 1.点击collections,点击add folder 2.c ...
-
【Python】列表各种操作
# -*- coding:utf-8 -*- #列表操作name_list = ['zhangsan','lisi','wangwu'] #append 在最后插入name_list.append(& ...
-
java向压缩文件添加文件
package org.alfresco.repo.bom.util; import java.io.File; import java.io.FileInputStream; import java ...
-
LeetCode Compare Version Numbers
原题链接在这里:https://leetcode.com/problems/compare-version-numbers/ 用string.split()方法把原有string 从小数点拆成 str ...
-
php版DES
class DES { var $key; var $iv; //偏移量 function DES( $key, $iv=0 ) { //key长度8例如:1234abcd $this->key ...
-
2017 年的 人生 hard 模式终于结束了,2018年回归初心
2017 年的 人生 hard 模式终于结束了,2018年回归初心 2017年对于我个人来讲, 毫不夸张的说 算是近十年来除了高考那一年,最最惊心动魄的一年了,没有之一. >>>开篇 ...
-
pwn-ROP(2)
通过int80系统只对静态编译有效,动态编译需要用其他方法 本题提供了一个地址输入端,输入函数地址会返回该函数的实际地址,我们用得到的实际地址-偏移地址=基地址,然后用基地址+任意函数的偏移地址就可以 ...
-
ReactNative——UI2.组件生命周期
对于习惯了iOS开发的同学,可能会对React Native中组件的生命周期很困惑.在iOS中有一个ViewDidLoad来初始化,那么在RN中,又是在哪里呢? 一.看图分析 在下图中描述了React ...
-
server.xml详解
http://www.cnblogs.com/gugnv/archive/2012/02/01/2334187.html http://blog.csdn.net/weinianjie1/articl ...
-
《Windows核心编程》读书笔记.Chapter06线程基础
原文链接在印象笔记(效果也好的多):https://app.yinxiang.com/l/AAQlNLnxTPRMAppVr5W0upchipQDDC_FHlU 概要: 现成也有两个组成部分: 现成的 ...