概述
其实就是在上一章“LED驱动”中的file_operations中添加read系统接口,仅此而已。
基础知识
(一) copy_from_user、copy_to_user使用
函数包含于<asm/uaccess.h>头文件中,实现内核空间和用户空间之间的数据拷贝功能。
/static inline int copy_from_user(void *to, const void __user volatile *from,unsigned long n)
/*拷贝from用户空间,to内核空间,拷贝长度n字节*/
static inline int copy_to_user(void __user volatile *to, const void *from,unsigned long n)
/*拷贝to用户空间,from内核空间,拷贝长度n字节*/
驱动代码
#include <linux/fs.h> /*包含file_operation结构体*/
#include <linux/init.h> /* 包含module_init module_exit */
#include <linux/module.h> /* 包含LICENSE的宏 */
#include <linux/miscdevice.h>/*包含miscdevice结构体*/
#include <linux/io.h> /*包含ioremap等操作函数*/
#include <linux/kernel.h> /*包含printk等操作函数*/
#include <asm/uaccess.h> /*包含copy_to_user操作函数*/
/**************宏定义***************/
#define PIO_PER (*(volatile unsigned long *)(virt_addr +0x0000))/*虚拟寄存器地址*/
#define PIO_MDDR (*(volatile unsigned long *)(virt_addr +0x0054))
#define PIO_OWDR (*(volatile unsigned long *)(virt_addr +0x00A4))
#define PIO_PDSR (*(volatile unsigned long *)(virt_addr +0x003C))
#define PIO_ODR (*(volatile unsigned long *)(virt_addr +0x0014))
#define KEY1 1 << 0
#define KEY2 1 << 2
#define KEY3 1 << 4
#define KEY4 1 << 6
/**************内部变量***************/
unsigned long virt_addr;
/* 定义一个打开设备的,open函数 */
static int key_open(struct inode *inode,struct file *file)
{
return 0;
}
/* 定义一个打开设备的,read函数 */
ssize_t key_read(struct file *file, char __user *array, size_t size, loff_t *ppos)
{
unsigned int readData;
char buf[4];
int res;
readData = PIO_PDSR;
buf[0] = (char)(readData>>0)&0x01;
buf[1] = (char)(readData>>2)&0x01;
buf[2] = (char)(readData>>4)&0x01;
buf[3] = (char)(readData>>6)&0x01;
printk("%d=%d=%d=%d\r\n",buf[0],buf[1],buf[2],buf[3]);
res = copy_to_user(array,buf,4);
if(!res)
return 4;
else
return -1;
}
/*字符设备驱动程序就是为具体硬件的file_operations结构编写各个函数*/
static const struct file_operations key_ctl={
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
};
/*杂项设备,主设备号为10的字符设备,相对普通字符设备,使用更简单*/
static struct miscdevice key_miscdev = {
.minor = 255,
.name = "key_read",
.fops = &key_ctl,
};
static int __init key_init(void)
{
char res;
/*注册杂项设备驱动*/
res = misc_register(&key_miscdev);
printk(KERN_ALERT"key_init %d\n",res);
/*通过物理地址,得到寄存器的虚拟地址*/
virt_addr =(volatile unsigned long )ioremap(0Xfffff600,0x200);
/*对物理寄存器操作,IO口输入使能*/
PIO_PER |= KEY1 | KEY2 | KEY3 | KEY4;//IO使能
PIO_MDDR |= KEY1 | KEY2 | KEY3 | KEY4;//取消复用功能
PIO_ODR |= KEY1 | KEY2 | KEY3 | KEY4;//设置为输入
return res;
}
static void __exit key_exit(void)
{
/*释放杂项设备*/
misc_deregister(&key_miscdev);
/*取消虚拟地址映射*/
iounmap((unsigned long *)virt_addr);
printk(KERN_ALERT"key_exit\r\n");
}
/*驱动模块的加载和卸载入口*/
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("boyee");
MODULE_DESCRIPTION("read key");
简单makefile
KERNEL_DIR:=/home/boyee/at91/linux-at91-V3.18.0-git
CROSS=arm-linux-
obj-m +=keyRead.o
all:
make -C $(KERNEL_DIR) M=`pwd` modules
clean:
rm -rf *.o *.ko
用户测试程序
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
char *keys = "/dev/key_read";
int fd;
char buf[5];
if((fd = open(keys,O_RDWR))<0)
{
printf("key open err\r\n");
return -1;
}
while(1)
{
read(fd,buf,4);
printf("%d-%d-%d-%d\r\n",buf[0],buf[1],buf[2],buf[3]);
sleep(1);
}
close(fd);
return 0;
}
测试结果
1.编译生成xxx.ko文件和测试程序
2.上传开发板insmod驱动,testAPP运行测试