驱动程序之_1_字符设备_1_基本框架和Led实例
Linux设备驱动分三种,包括字符设备驱动、块设备驱动和网络设备驱动
其中本文讲的字符设备(如LCD、触摸屏等)只能按字节流先后顺序访问设备内存,不能随机访问
字符设备的基本框架比较简单
加载驱动时,调用入口函数
卸载驱动时,调用出口函数
应用程序打开驱动设备时,调用open函数
应用程序读写驱动设备时,调用read、write函数
……
open、read、write等函数是在linux内核中的统一接口,使用open打开设备会返回一个文件标识符,通过文件标识符使用read、write等函数会调用到设备自己的read、write函数,从而操作到硬件,这些设备自己的函数都被存放在一个文件结构体中
编写字符设备驱动的基本流程:
1、定义、设置文件结构体
2、编写入口函数,注册结构体
3、编写出口函数,销毁结构体
4、在设备打开时,初始化设备硬件
5、编写read、write等功能函数
6、包含一些头文件(参考其他驱动程序即可)
如:
入口函数
static int __init LedsInit(void)
{
}
出口函数
static void __exit LedsExit(void)
{
}
入口、出口函数需要修饰:
module_init(LedsInit);
module_exit(LedsExit);
结构体定义
static struct file_operations g_tLedsFops =
{
.owner = THIS_MODULE,
.open = LedsOpen,
.read = LedsRead,
.write = LedsWrite,
};
open、read、write函数声明如下
static int LedsOpen(struct inode *ino, struct file *filep);
static ssize_t LedsWrite(struct file *fd, const char __user *buf, size_t cnt, loff_t * lof);
ssize_t LedsRead(struct file *file, char __user *buf, size_t size, loff_t * ppos);
注册结构体所用函数原型
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
可以人为创建设备,分配主设备号,然后在major中输入相应值,但一般来说都会使用linux内核所自带的mdev机制,自动分配设备号,创建设备,具体操作如下:
定义class、class_device指针变量
static struct class *g_ptLedsClass;
static struct class_device *g_ptLedsClassDevice;
创建class和class_device所用函数原型
extern struct class *class_create(struct module *owner, const char *name);
extern struct class_device *class_device_create(struct class *cls,
struct class_device *parent,
dev_t devt,
struct device *device,
const char *fmt, ...)
__attribute__((format(printf,5,6)));
销毁class和class_device所用函数原型
extern void class_unregister(struct class *);
extern void class_device_destroy(struct class *cls, dev_t devt);
使用mdev机制需要包含GPL协议
MODULE_LICENSE("GPL");
附上一个关于led的驱动程序和测试用的应用程序,代码仅起示例作用
驱动程序
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/device.h>
static volatile unsigned long *g_dwGpfCon,*g_dwGpfDat;
static struct class *g_ptLedsClass;
static struct class_device *g_ptLedsClassDevice;
static int LedOpen(struct inode *ino, struct file *filep)
{
*g_dwGpfCon&= ~(3<<8 | 3<<10 | 3<<12);
*g_dwGpfCon |= (1<<8 | 1<<10 | 1<<12);
return 0;
}
static ssize_t LedWrite(struct file *fd, const char __user *buf, size_t cnt, loff_t * lof)
{
int iVal;
int iStat;
int iNum;
int iError;
iError = copy_from_user(&iVal,buf,cnt);
iStat = iVal % 10;
iNum= iVal / 10;
printk("iStat : %d , iNum : %d \r\n",iStat,iNum);
if(iStat)
{
if(iNum != 7)
*g_dwGpfDat &= ~(1<<iNum);
else
*g_dwGpfDat &= ~((1<<(iNum-3)) | (1<<(iNum-2)) | (1<<(iNum-1)));
}
else
{
if(iNum != 7)
*g_dwGpfDat |= (1<<iNum);
else
*g_dwGpfDat |= ((1<<(iNum-3)) | (1<<(iNum-2)) | (1<<(iNum-1)));
}
return 0;
}
static struct file_operations g_tLedFops =
{
.owner = THIS_MODULE,
.open = LedOpen,
.write = LedWrite,
};
int g_iMajor;
static int LedInit(void)
{
g_iMajor = register_chrdev(0,"Leds",&g_tLedFops);
g_ptLedsClass = class_create(THIS_MODULE,"leds_class");
g_ptLedsClassDevice = class_device_create(g_ptLedsClass,0,MKDEV(g_iMajor,0),0,"Leds");
g_dwGpfCon = ioremap(0x56000050,16);
g_dwGpfDat = g_dwGpfCon + 1;
return 0;
}
static void LedExit(void)
{
*g_dwGpfDat |= ((1<<4) | (1<<5) | (1<<6));
iounmap(g_dwGpfCon);
class_device_destroy(g_ptLedsClass,MKDEV(g_iMajor,0));
class_unregister(g_ptLedsClass);
unregister_chrdev(g_iMajor,"Leds");
}
module_init(LedInit);
module_exit(LedExit);
MODULE_LICENSE("GPL");
应用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argv,char* argc[])
{
int iFd;
int iVal = 0;
int iError;
if(argv < 3)
{
printf("usage : ./filename <on|off> <1|2|3|all>");
return -1;
}
if(!strcmp(argc[1],"on"))
{
iVal = 1;
}
else if(!strcmp(argc[1],"off"))
{
iVal = 0;
}
else
{
printf("usage : ./filename <on|off> <1|2|3|all>");
return -1;
}
if(!strcmp(argc[2],"1"))
{
iVal += 40;
}
else if(!strcmp(argc[2],"2"))
{
iVal += 50;
}
else if(!strcmp(argc[2],"3"))
{
iVal += 60;
}
else if(!strcmp(argc[2],"all"))
{
iVal += 70;
}
else
{
printf("usage : ./filename <on|off> <1|2|3|all>\r\n");
return -1;
}
iFd = open("/dev/Leds",O_RDWR);
if(iFd < 0)
{
printf("open error\r\n");
return -1;
}
iError = write(iFd,&iVal,sizeof(iVal));
return 0;
}