@ TOCカーネルドライバー実装コード)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/gpio.h>
#include <mach/platform.h>
#include <linux/uaccess.h>
static dev_t file_dev_num;//设备号
static struct cdev file_cdev;
/*B.1硬件资源配置*/
struct led_source {
//定义一个LED灯信息的结构体,包含名称和GPIO口两个成员变量;
char *name;
int gpio;
};
static struct led_source led_info[] = {
{
.name = "LED1", //LED灯名字,可以随便取;
.gpio = PAD_GPIO_C +12 //PAD_GPIO_C,不同芯片厂家,名称不同,根据芯片厂家提供的Linux内核中的宏编写;
},
{
.name = "LED2",
.gpio = PAD_GPIO_C +11
},
{
.name = "LED3",
.gpio = PAD_GPIO_C +7
},
{
.name = "LED4",
.gpio = PAD_GPIO_B +26
}
};
/*D.2内核空间组用户空间提供的读操作接口函数*/
ssize_t mychar_dev_read(struct file * file,char __user * buf,\
size_t count,loff_t * ppos){
int kbuf[4];
int i =0;
int ret_copy = 0 ;
//读取当前GPIO的数值保存到kbuf数组中,
//通过copy_to_user()函数将数据 从内核空间传递到用户空间
for(;i < ARRAY_SIZE(led_info);i++){
kbuf[i] = gpio_get_value(led_info[i].gpio);
}
ret_copy = copy_to_user(buf,kbuf,sizeof(int)*(i+1));
return ret_copy;
}
/*D.3.1当用户空间向内核空间写操作接口写入开灯命令时,执行如下函数*/
void led_on(struct file * file){
int i = 0;
char file_name[64];//保存打开的文件名
strcpy(file_name,file->f_path.dentry->d_iname);
//将文件名的最后一个字符做为灯的编号;
i = simple_strtoul(&file_name[strlen(file_name)-1],NULL,0);
//设置GPIO为低电平 ,即:开灯
gpio_set_value(led_info[i].gpio,0);
}
/*d.3.2当用户空间向内核空间写操作接口写入关灯命令时,执行如下函数*/
void led_off(struct file * file){
int i = 0;
char file_name[64];
strcpy(file_name,file->f_path.dentry->d_iname);
i = simple_strtoul(&file_name[strlen(file_name)-1],NULL,0);
//设置GPIO为高电平 ,即:关灯
gpio_set_value(led_info[i].gpio,1);
}
/*D.3内核空间组用户空间提供的写操作接口函数*/
ssize_t mychar_dev_write(struct file * file ,const char __user * buf,\
size_t count, loff_t *ppos)
{
int kbuf;
int ret_copy = 0;
ret_copy = copy_from_user(&kbuf,buf,4);
switch(kbuf){
case 0:
led_on(file);//执行开灯操作
break;
case 1:
led_off(file);//执行关灯操作
break;
default :
break;
}
return ret_copy;
}
/*D.1给字符设备接口赋值,即:提供读写操作接口
static struct file_operations mychar_dev_fops = {
.owner = THIS_MODULE,
.read = mychar_dev_read,
.write = mychar_dev_write,
};
/*A.0驱动模块加载时执行的函数*/
static int mychar_dev_init(void){
//B.2初始化GPIO
int i =0 ;
int ret_chrdev;
for(;i<ARRAY_SIZE(led_info);i++){
gpio_request(led_info[i].gpio,led_info[i].name);//申请GPIO
gpio_direction_output(led_info[i].gpio,1);//配置GPIO为输出口,高电平
}
//C.1 申请字符设备号,初始化、注册字符设备
//申请了一个主设备号和4个次设备号,注册和释放时也要注册和释放4个;
ret_chrdev = alloc_chrdev_region(&file_dev_num,0,4,"myfile_cdev");
cdev_init(&file_cdev,&mychar_dev_fops);
cdev_add(&file_cdev,file_dev_num,4);
return 0;
}
/*A.0驱动模块卸载时执行的函数*/
static void mychar_dev_exit(void){
//B.3释放GPIO资源
int i = 0;
for(;i<ARRAY_SIZE(led_info);i++){
gpio_set_value(led_info[i].gpio,1);设置GPIO为高电平
gpio_free(led_info[i].gpio);//释放GPIO资源
}
//C.2释放字符设备号,包含4个次设备号,删除字符设备
unregister_chrdev_region(file_dev_num,4);
cdev_del(&file_cdev);
printk("模块卸载完成\n");
}
module_init(mychar_dev_init);//A.0定义驱动模块加载时执行的函数
module_exit(mychar_dev_exit);//A.0定义驱动模块卸载时执行的函数
MODULE_LICENSE("GPL");//A.0定义驱动模块支持GPL协义