文章目录
前言
本文介绍应用层数据如何传到内核层
一、应用层数据如何和驱动层数据进行交互
(1)copy_from_user,用来将数据从用户空间复制到内核空间
copy_from_user函数的返回值定义,和常规有点不同。返回值如果成功复制则返回0,如果 不成功复制则返回尚未成功复制剩下的字节数。
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
retvalue = copy_from_user(databuf, buf, cnt);
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == LEDON) {
led_switch(LEDON); /* 打开LED灯 */
} else if(ledstat == LEDOFF) {
led_switch(LEDOFF); /* 关闭LED灯 */
}
return 0;
}
1234567891011121314151617181920212223242526272829
(2)copy_to_user,用来将数据从用户空间复制到内核空间
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
/* 向用户空间发送数据 */
memcpy(readbuf, kerneldata, sizeof(kerneldata));
retvalue = copy_to_user(buf, readbuf, cnt);
if(retvalue == 0){
printk("kernel senddata ok!\r\n");
}else{
printk("kernel senddata failed!\r\n");
}
//printk("chrdevbase read!\r\n");
return 0;
}
123456789101112131415161718192021222324
二、驱动中如何操控硬件
1.和裸机操控硬件的相同点
(1)硬件物理原理不变
(2)硬件操作接口(寄存器)不变,操作硬件实际上就是操作硬件设计的寄存器
(3)硬件操作代码大体不变
2.和裸机操控硬件的不同点
(1)寄存器地址不同。原来是直接用物理地址,直接通过指针指向设计时规定的物理地址。现在需要用该物理地址在内核虚拟地址空间相对应的虚拟地址。寄存器的物理地址是CPU设计时决定的,从datasheet中查找到的。
(2)编程方法不同。裸机中习惯直接用函数指针操作寄存器地址,而kernel中习惯用封装好的io读写函数来操作寄存器,以实现最大程度可移植性。
3.物理地址和虚拟地址
提到物理地址和虚拟地址就不得提到内核中的虚拟地址映射。
内核中有2套虚拟地址映射方法:动态和静态
(1)静态映射方法的特点:
内核移植时以代码的形式硬编码,如果要更改必须改源代码后重新编译内核
在内核启动时建立静态映射表,到内核关机时销毁,中间一直有效
对于移植好的内核,你用不用他都在那里
(2)动态映射方法的特点:
驱动程序根据需要随时动态的建立映射、使用、销毁映射
映射是短期临时的