Linux驱动、应用调试技巧

版权声明:本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,欢迎转载,但转载请注明来自hceng blog(www.hceng.cn),并保持转载后文章内容的完整。本人保留所有版权相关权利。 https://blog.csdn.net/hceng_linux/article/details/89913985

CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2019/04/08/Linux驱动、应用调试技巧/#more

记录几个Linux驱动、应用调试技巧。

1.printk

printk都比较熟悉了,在日常中用得最多的一个。

示例:
{% codeblock lang:c %}
printk(KERN_DEBUG “Passed %s %d \n”,FUNCTION,LINE);
{% endcodeblock %}

其中KERN_DEBUG表示log的级别,参考kern_levels.h:
{% codeblock lang:c %}
#define KERN_EMERG KERN_SOH “0” /* system is unusable 紧急事件,一般是系统崩溃之前的提示消息 /
#define KERN_ALERT KERN_SOH “1” /
action must be taken immediately 必须立即采取行动 /
#define KERN_CRIT KERN_SOH “2” /
critical conditions 临界状态,通常涉及严重的硬件或者软件操作失败 /
#define KERN_ERR KERN_SOH “3” /
error conditions 报告错误状态,经常用来报告硬件错误 /
#define KERN_WARNING KERN_SOH “4” /
warning conditions 对可能出现问题的情况进行警告,通常不会对系统造成严重问题 /
#define KERN_NOTICE KERN_SOH “5” /
normal but significant condition 有必要的提示,通常用于安全相关的状况汇报 /
#define KERN_INFO KERN_SOH “6” /
informational 提示信息,驱动程序常用来打印硬件信息 /
#define KERN_DEBUG KERN_SOH “7” /
debug-level messages 用于调试信息 */
{% endcodeblock %}

一个有8个等级,从0到7,优先级依次降低。
通常通过修改/proc/sys/kernel/printk来设置printk打印。
{% codeblock lang:c %}
cat /proc/sys/kernel/printk
7 4 1 7

echo 8 > /proc/sys/kernel/printk

cat /proc/sys/kernel/printk
8 4 1 7
{% endcodeblock %}

4个值的含义依次如下:
console_loglevel:当前console的log级别,只有更高优先级的log才被允许打印到console;
default_message_loglevel:当不指定log级别时,printk默认使用的log级别;
minimum_console_loglevel:console能设定的最高log级别;
default_console_loglevel:默认的console的log级别。

另外,关于printk格式化字符串形式,参考printk-formats.txt

使用dmesg命令,可以显示之前所有的打印信息,常配合grep来查找历史纪录。

2.dump_stack

在分析驱动源码的调用关系时,常遇到分支结构、回调函数,往往要多次添加打印来追溯调用过程。
这时,可以使用内核提供的dump_stack();函数来一次性打印调用过程,将该函数加在要调试位置,当运行到该函数时,就会打印出之前的调用关系。

加入dump_stack
{% codeblock lang:c %}
static int spidevx_drv_init(void)
{
……
dump_stack();
……
}
{% endcodeblock %}

效果:
{% codeblock lang:c %}

insmod spidev.ko

CPU: 0 PID: 198 Comm: insmod Tainted: G O 4.1.18-g4b7863b4-dirty #32
Hardware name: Generic AM33XX (Flattened Device Tree)
Backtrace:
[] (dump_backtrace) from [] (show_stack+0x18/0x1c)
r7:ddfb6100 r6:c0910960 r5:bf0012cc r4:c0910960
[] (show_stack) from [] (dump_stack+0x20/0x28)
[] (dump_stack) from [] (spidevx_drv_init+0x18/0xe8 [spidev])
[] (spidevx_drv_init [spidev]) from [] (do_one_initcall+0x88/0x1e0)
r5:bf000fbc r4:c0910960
[] (do_one_initcall) from [] (do_init_module+0x60/0x1b0)
r10:bf001488 r9:00000001 r8:dddd5f40 r7:bf0014d0 r6:ddfb6040 r5:00000001
r4:bf001488
[] (do_init_module) from [] (load_module+0x1bec/0x1e54)
r6:dddd5f48 r5:00000001 r4:ddcc3f48
[] (load_module) from [] (SyS_finit_module+0x84/0x98)
r10:00000000 r9:ddcc2000 r8:c000f9c4 r7:0000017b r6:0002541e r5:00000003
r4:00000000
[] (SyS_finit_module) from [] (ret_fast_syscall+0x0/0x3c)
r6:00038d08 r5:00000000 r4:00000000
{% endcodeblock %}

可以看到调用关系为:
ret_fast_syscall->SyS_finit_module->load_module->do_init_module->do_one_initcall->spidevx_drv_init

3.strace

strace是个功能强大的Linux调试分析诊断工具,可用于跟踪程序执行时进程系统调用(system call)和所接收的信号,尤其是针对源码不可读或源码无法再编译的程序。
在Linux系统中,用户程序运行在一个沙箱(sandbox)里,用户进程不能直接访问计算机硬件设备。当进程需要访问硬件设备(如读取磁盘文件或接收网络数据等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。
strace可跟踪进程产生的系统调用,包括参数、返回值和执行所消耗的时间。
strace常用参数:
{% codeblock lang:shell %}
strace:
-p : 跟踪一个PID进程
-f: 继续子进程的跟踪
-T: 打印出每次调用所花费的时间,单位:秒
-c: 统计和报告每个系统调用所执行的时间、调用次数和出错次数等
-o : 指定保存strace输出信息的文件
{% endcodeblock %}

示例,执行:

strace -o log.txt date

查看log.txt
{% codeblock lang:c %}
……
open("/etc/localtime", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=127, …}) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=127, …}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb6f9f000
read(3, “TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0”…, 1024) = 127
_llseek(3, -6, [121], SEEK_CUR) = 0
read(3, “\nUTC0\n”, 1024) = 6
close(3) = 0
……
{% endcodeblock %}
可以清楚的看到date先打开/etc/localtime,得到文件描述符为3,再去read文件,最后close文件。

4.应用层读写寄存器

在判断某个硬件是否按期望正常工作,最简单粗暴的就是直接读取对应寄存器值来分析。
Linux内核提供了一个/dev/mem节点来访问硬件寄存器,可以通过devmemdevmem2等应用程序来读写寄存器。
一些嵌入式的BusyBox包含了devmem,一些发行版的Linux,可以通过sudo apt install devmem2等方式安装,或者手动编译源码:

{% codeblock lang:c [devmem2.c]%}
/*

  • http://sources.buildroot.net/devmem2.c
  • devmem2.c: Simple program to read/write from/to any location in memory.
  • Copyright © 2000, Jan-Derk Bakker ([email protected])
  • This software has been developed for the LART computing board
  • (http://www.lart.tudelft.nl/). The development has been sponsored by
  • the Mobile MultiMedia Communications (http://www.mmc.tudelft.nl/)
  • and Ubiquitous Communications (http://www.ubicom.tudelft.nl/)
  • projects.
  • The author can be reached at:
  • Jan-Derk Bakker
  • Information and Communication Theory Group
  • Faculty of Information Technology and Systems
  • Delft University of Technology
  • P.O. Box 5031
  • 2600 GA Delft
  • The Netherlands
  • This program is free software; you can redistribute it and/or modify
  • it under the terms of the GNU General Public License as published by
  • the Free Software Foundation; either version 2 of the License, or
  • (at your option) any later version.
  • This program is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  • GNU General Public License for more details.
  • You should have received a copy of the GNU General Public License
  • along with this program; if not, write to the Free Software
  • Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>

#define FATAL do { fprintf(stderr, “Error at line %d, file %s (%d) [%s]\n”,
LINE, FILE, errno, strerror(errno)); exit(1); } while(0)

#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)

int main(int argc, char **argv) {
int fd;
void *map_base, *virt_addr;
unsigned long read_result, writeval;
long int target;
int access_type = ‘w’;

/*
usage:  ./devmem  { address } [ type [ data ] ] 
*/
if(argc < 2) {
    fprintf(stderr, "\nUsage:\t%s { address } [ type [ data ] ]\n"
        "\taddress : memory address to act upon\n"
        "\ttype    : access operation type : [b]yte, [h]alfword, [w]ord\n"
        "\tdata    : data to be written\n\n",
        argv[0]);
    exit(1);
}
target = strtoul(argv[1], 0, 0);

if(argc > 2)
    access_type = tolower(argv[2][0]);


if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
printf("/dev/mem opened.\n"); 
fflush(stdout);

/* Map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
if(map_base == (void *) -1) FATAL;
printf("Memory mapped at address %p.\n", map_base); 
fflush(stdout);

virt_addr = map_base + (target & MAP_MASK);
switch(access_type) {
    case 'b':
        read_result = *((unsigned char *) virt_addr);
        break;
    case 'h':
        read_result = *((unsigned short *) virt_addr);
        break;
    case 'w':
        read_result = *((unsigned long *) virt_addr);
        break;
    default:
        fprintf(stderr, "Illegal data type '%c'.\n", access_type);
        exit(2);
}
printf("Value at address 0x%X (%p): 0x%X\n", target, virt_addr, read_result); 
fflush(stdout);

if(argc > 3) {
    writeval = strtoul(argv[3], 0, 0);
    switch(access_type) {
        case 'b':
            *((unsigned char *) virt_addr) = writeval;
            read_result = *((unsigned char *) virt_addr);
            break;
        case 'h':
            *((unsigned short *) virt_addr) = writeval;
            read_result = *((unsigned short *) virt_addr);
            break;
        case 'w':
            *((unsigned long *) virt_addr) = writeval;
            read_result = *((unsigned long *) virt_addr);
            break;
    }
    printf("Written 0x%X; readback 0x%X\n", writeval, read_result); 
    fflush(stdout);
}

if(munmap(map_base, MAP_SIZE) == -1) FATAL;
close(fd);
return 0;

}
{% endcodeblock %}

以操作一个LED为例,GPIO1_18的寄存器基地址为0x4804C000,数据输出寄存器偏移为0x13C

值得注意的是,这里使用的是AM335X,测试中发现不能直接操作数据输出寄存器,需要先操作GPIO控制寄存器,这里先通过GPIO子系统完成GPIO寄存器前期工作,也许换个SOC不会出现该情况,可以直接操作任意寄存器:

echo 50 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio50/direction
cat /sys/class/gpio/gpio50/value
0

可以看到,现在数据输出寄存器0x4804C13C的值为0,使用devmem2查看:

./devmem2 0x4804c13c w
/dev/mem opened.
Memory mapped at address 0xb6f86000.
Value at address 0x4804C13C (0xb6f8613c): 0x0

读取到的值和前面使用GPIO子系统的结果一致。

继续写操作测试:

./devmem2 0x4804c13c w 0x40000
/dev/mem opened.
Memory mapped at address 0xb6fcc000.
Value at address 0x4804C13C (0xb6fcc13c): 0x0
Written 0x40000; readback 0x40000

cat /sys/class/gpio/gpio50/value
1

使用devmem2操作寄存器,使用GPIO子系统查看发现确实被改变了。

使用devmem2还存在几个问题:
一是需要保证/dev/mem节点存在;
二是不能同时读取多个寄存器值;
三是必须依赖应用程序,不能直接echocat读写寄存器;

因此,编写一个新的驱动和应用程序,独立的实现读写寄存器的功能,以解决前面可能出现的情况。

驱动程序如下:
{% codeblock lang:c [ker_rw.c] %}
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>

#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/ioport.h>

#define MAX_LEN 1000

#define REG_IOC_MAGIC ‘r’
#define REG_IOC_R8 _IOWR(REG_IOC_MAGIC, 0, void *)
#define REG_IOC_R16 _IOWR(REG_IOC_MAGIC, 1, void *)
#define REG_IOC_R32 _IOWR(REG_IOC_MAGIC, 2, void *)
#define REG_IOC_W8 _IOWR(REG_IOC_MAGIC, 3, void *)
#define REG_IOC_W16 _IOWR(REG_IOC_MAGIC, 4, void *)
#define REG_IOC_W32 _IOWR(REG_IOC_MAGIC, 5, void *)

#define REG_ATTR(_name, _mode, _show, _store, _index)
{ .dev_attr = __ATTR(_name, _mode, _show, _store),
.index = _index }

static int reg_major;
static struct cdev reg_cdev;
static struct class reg_class;
struct device
reg_device = NULL;

struct ker_rw_msg {
unsigned int val;
unsigned int addr;
unsigned int width;
unsigned int num;

struct mutex lock;  

};

static struct ker_rw_msg rw_msg;

struct reg_device_attribute{
struct device_attribute dev_attr;
int index;
};

static ssize_t reg_num_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
return sprintf(buf, “%u\n”, rw_msg.num);
}

static ssize_t reg_num_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int num;

num = simple_strtoul(buf, NULL, 10);

if ((num > MAX_LEN) || (num == 0))
    printk(KERN_ERR "%s: num range is 0~%d\n",__FUNCTION__, MAX_LEN);
else    
    rw_msg.num = num;
    
return count;

}

static ssize_t reg_width_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
return sprintf(buf, “%u\n”, rw_msg.width);
}

static ssize_t reg_width_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int width = 0;

width = simple_strtoul(buf, NULL, 10);

if ((width != 8) && (width != 16) && (width != 32))
    printk(KERN_WARNING "Address width can only be 8 or 16 or 32.\n");
else
    rw_msg.width = width;
    
return count;

}

static ssize_t reg_addr_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
return sprintf(buf, “0x%08x\n”, rw_msg.addr);
}

static ssize_t reg_addr_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
rw_msg.addr = simple_strtoul(buf, NULL, 16);

return count;

}

static ssize_t reg_val_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
int i;
void __iomem *virtbase;
unsigned int addr;
unsigned int phy_addr[rw_msg.num];
unsigned int vir_addr[rw_msg.num];
unsigned int val[rw_msg.num];
volatile unsigned char *p8;
volatile unsigned short *p16;
volatile unsigned int *p32;

//if (!request_mem_region(rw_msg.addr, 4, "ker_rw")) 
    //return -EBUSY;

mutex_lock(&rw_msg.lock);   

addr = rw_msg.addr;
virtbase = ioremap(addr, 4);
if (virtbase == NULL)
    return -ENOMEM;

p8  = (volatile unsigned char *)virtbase;
p16 = (volatile unsigned short *)virtbase;
p32 = (volatile unsigned int *)virtbase;

for (i=0; i<rw_msg.num; i++)
{
    if (rw_msg.width == 8)
    {
        phy_addr[i] = addr;
        vir_addr[i] = (volatile unsigned int)p8;
        val[i] = readb(p8); //val[i] = *p8;
        p8++;
        addr = addr + 1;
    }
    else if (rw_msg.width == 16)
    {
        phy_addr[i] = addr;
        vir_addr[i] = (volatile unsigned int)p16;
        val[i] = readw(p16); //val[i] = *p16;
        p16++;
        addr = addr + 2;
    }
    else if (rw_msg.width == 32)
    {
        phy_addr[i] = addr;
        vir_addr[i] = (volatile unsigned int)p32;
        val[i] = readl(p32); //val[i] = *p32;
        p32++;
        addr = addr + 4;
    }
    else
        printk(KERN_WARNING "Please check the address width.\n");
    
    sprintf(buf + strlen(buf), "phy_addr:0x%08x  vir_addr:0x%08x  val:0x%08x\n", phy_addr[i], vir_addr[i], val[i]);
}

iounmap(virtbase);

mutex_unlock(&rw_msg.lock);

return strlen(buf);

}

static ssize_t reg_val_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
void __iomem *virtbase;

mutex_lock(&rw_msg.lock);   

rw_msg.val = simple_strtoul(buf, NULL, 16);

virtbase = ioremap(rw_msg.addr, 4);
if (virtbase == NULL)
    return -ENOMEM;

if (rw_msg.width == 8)
    writeb(rw_msg.val, virtbase);
else if (rw_msg.width == 16)
    writew(rw_msg.val, virtbase);
else if (rw_msg.width == 32)
    writel(rw_msg.val, virtbase);
else
    printk(KERN_WARNING "Please check the address width.\n");

iounmap(virtbase);

mutex_unlock(&rw_msg.lock);

return count;

}

static struct reg_device_attribute reg_attribute[] = {
REG_ATTR(val, S_IRUGO | S_IWUSR, reg_val_show, reg_val_store, 1),
REG_ATTR(addr, S_IRUGO | S_IWUSR, reg_addr_show, reg_addr_store, 2),
REG_ATTR(width, S_IRUGO | S_IWUSR, reg_width_show, reg_width_store, 3),
REG_ATTR(num, S_IRUGO | S_IWUSR, reg_num_show, reg_num_store, 4),
};

static long ker_rw_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
volatile unsigned char *p8;
volatile unsigned short *p16;
volatile unsigned int *p32;
unsigned int val;
unsigned int addr;

unsigned int buf[2];

mutex_lock(&rw_msg.lock);

if (copy_from_user(buf, (const void __user *)arg, 8))
    printk(KERN_ERR "%s: copy_from_user error in the %d \n",__FUNCTION__,__LINE__);

addr = buf[0];
val  = buf[1];

p8  = (volatile unsigned char *)ioremap(addr, 4);
if (p8 == NULL)
    return -ENOMEM;
p16 = (volatile unsigned short *)p8;
p32 = (volatile unsigned int *)p8;

switch (cmd)
{
    case REG_IOC_R8:
    {
        val = *p8;
        if (copy_to_user((void __user *)(arg+4), &val, 4))
        {           
            printk(KERN_ERR "%s: copy_to_user error in the %d \n",__FUNCTION__,__LINE__);
            return -EINVAL;
        }
        break;
    }
    case REG_IOC_R16:
    {
        val = *p16;
        if (copy_to_user((void __user *)(arg+4), &val, 4))
        {           
            printk(KERN_ERR "%s: copy_to_user error in the %d \n",__FUNCTION__,__LINE__);
            return -EINVAL;
        }
        break;
    }
    case REG_IOC_R32:
    {
        val = *p32;
        if (copy_to_user((void __user *)(arg+4), &val, 4))
        {           
            printk(KERN_ERR "%s: copy_to_user error in the %d \n",__FUNCTION__,__LINE__);
            return -EINVAL;
        }
        break;
    }
    case REG_IOC_W8:
    {
        *p8 = val;
        break;
    }
    case REG_IOC_W16:
    {
        *p16 = val;
        break;
    }
    case REG_IOC_W32:
    {
        *p32 = val;
        break;
    }
}

iounmap(p8);

mutex_unlock(&rw_msg.lock);

return 0;

}

static struct file_operations ker_rw_ops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ker_rw_ioctl,
};

static int ker_rw_init(void)
{
int i = 0;
int ret = 0;
dev_t reg_devid = 0;

mutex_init(&rw_msg.lock);
mutex_lock(&rw_msg.lock);

if(alloc_chrdev_region(&reg_devid, 0, 1, "ker_rw") < 0)
{
    printk(KERN_ERR "%s: alloc_chrdev_region error in the %d \n",__FUNCTION__,__LINE__);
    return -EINVAL;
} 

reg_major = MAJOR(reg_devid);
cdev_init(&reg_cdev, &ker_rw_ops);   

ret = cdev_add(&reg_cdev, reg_devid, 1);
if (ret < 0)
{
    printk(KERN_ALERT "%s: cdev_add error in the %d \n",__FUNCTION__,__LINE__);
    goto error1;
}
    
reg_class = class_create(THIS_MODULE, "ker_rw"); 
if (IS_ERR(reg_class))
{
    printk(KERN_ALERT "%s: device_create error in the %d \n",__FUNCTION__,__LINE__);
    goto error2;
}

reg_device = device_create(reg_class, NULL, MKDEV(reg_major, 0), NULL, "ker_rw"); 
if (IS_ERR(reg_device))
{
    printk(KERN_ALERT "%s: device_create error in the %d \n",__FUNCTION__,__LINE__);
    goto error3;
}


for (i=0; i<4; i++)
{
    ret = device_create_file(reg_device, &reg_attribute[i].dev_attr);
    if (ret)
    {
        printk(KERN_ALERT "%s: device_create_file error in the %d \n",__FUNCTION__,__LINE__);
        goto error4;
    }
}

//Defaults
rw_msg.num = 1;
rw_msg.width = 32;
mutex_unlock(&rw_msg.lock);

return 0;

error4:
device_destroy(reg_class, MKDEV(reg_major, 0));
error3:
class_destroy(reg_class);
error2:
cdev_del(&reg_cdev);
error1:
unregister_chrdev_region(MKDEV(reg_major, 0), 1);
mutex_unlock(&rw_msg.lock);

return -EINVAL;

}

static void ker_rw_exit(void)
{
int i;
mutex_lock(&rw_msg.lock);

for (i=0; i<4; i++)
    device_remove_file(reg_device, &reg_attribute[i].dev_attr);

device_destroy(reg_class,  MKDEV(reg_major, 0));
class_destroy(reg_class);

unregister_chrdev_region(MKDEV(reg_major, 0), 1);
cdev_del(&reg_cdev);

mutex_unlock(&rw_msg.lock);

}

module_init(ker_rw_init);
module_exit(ker_rw_exit);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“Read and write register.”);
MODULE_VERSION(“v1.0”);
{% endcodeblock %}

该驱动程序向用户层提供了两个接口:sysfs文件系统接口和devfs文件系统接口。
对于sysfs文件系统接口,加载驱动后,会在/sys/class/ker_rw/ker_rw/生成如下节点:

addr       num        subsystem  val
dev        power      uevent     width

其中,num用于设置一次读取的寄存器数量,范围为1~MAX_LEN(1000);
witdh用于设置每次读/写寄存器的宽度,支持8、16、32;
addr用于设置每次读/写寄存器的地址(16进制);
val用于设置每次读/写寄存器的值(16进制);

因此,查看操作前面的GPIO可执行:

# echo 0x4804c13c > addr

# cat val
phy_addr:0x4804c13c  vir_addr:0xfa04c13c  val:0x00000000

# echo 0x40000 > val
# cat val
phy_addr:0x4804c13c  vir_addr:0xfa04c13c  val:0x00040000

# echo 4 > num

# cat val
phy_addr:0x4804c13c  vir_addr:0xfa04c13c  val:0x00040000
phy_addr:0x4804c140  vir_addr:0xfa04c140  val:0x00000000
phy_addr:0x4804c144  vir_addr:0xfa04c144  val:0x00000000
phy_addr:0x4804c148  vir_addr:0xfa04c148  val:0x00000000

这里仍然保留了传统的devfs文件系统接口,需要编写应用程序访问:

{% codeblock lang:c [app_rw.c] %}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>

#define REG_IOC_MAGIC ‘r’
#define REG_IOC_R8 _IOWR(REG_IOC_MAGIC, 0, void *)
#define REG_IOC_R16 _IOWR(REG_IOC_MAGIC, 1, void *)
#define REG_IOC_R32 _IOWR(REG_IOC_MAGIC, 2, void *)
#define REG_IOC_W8 _IOWR(REG_IOC_MAGIC, 3, void *)
#define REG_IOC_W16 _IOWR(REG_IOC_MAGIC, 4, void *)
#define REG_IOC_W32 _IOWR(REG_IOC_MAGIC, 5, void *)

/* Usage:

  • ./regeditor r8 addr [num]
  • ./regeditor r16 addr [num]
  • ./regeditor r32 addr [num]
  • ./regeditor w8 addr val
  • ./regeditor w16 addr val
  • ./regeditor w32 addr val
    */

void print_usage(char *file)
{
printf(“Usage:\n”);
printf("%s <r8 | r16 | r32> [num]\n", file);
printf("%s <w8 | w16 | w32> \n", file);
}

int main(int argc, char **argv)
{
int fd;
unsigned int buf[2];
unsigned int i;
unsigned int num;
int ret;

if ((argc != 3) && (argc != 4))
{
    print_usage(argv[0]);
    return -1;
}

fd = open("/dev/ker_rw", O_RDWR);
if (fd < 0)
{
    printf("can't open /dev/ker_rw\n");
    return -2;
}

/* addr */
buf[0] = strtoul(argv[2], NULL, 0);

if (argc == 4)
{
    buf[1] = strtoul(argv[3], NULL, 0);
    num    = buf[1];
}
else
{
    num = 1;
}

if (strcmp(argv[1], "r8") == 0)
{
    for ( i = 0; i < num; i++)
    {
        ioctl(fd, REG_IOC_R8, buf);  /* val = buf[1] */
        printf("%02d. [%08x] = %02x\n", i, buf[0], (unsigned char)buf[1]);
        buf[0] += 1;
    }
}
else if (strcmp(argv[1], "r16") == 0)
{
    for ( i = 0; i < num; i++)
    {
        ioctl(fd, REG_IOC_R16, buf);  /* val = buf[1] */
        printf("%02d. [%08x] = %04x\n", i, buf[0], (unsigned short)buf[1]);
        buf[0] += 2;
    }
}
else if (strcmp(argv[1], "r32") == 0)
{
    for ( i = 0; i < num; i++)
    {
        ret = ioctl(fd, REG_IOC_R32, buf);  /* val = buf[1] */
        if (ret == -1)
        {
            printf("errno = %d\n", errno);
        }
        printf("%02d. [%08x] = %08x\n", i, buf[0], (unsigned int)buf[1]);
        buf[0] += 4;
    }
}
else if (strcmp(argv[1], "w8") == 0)
{
    ioctl(fd, REG_IOC_W8, buf);  /* val = buf[1] */
}
else if (strcmp(argv[1], "w16") == 0)
{
    ioctl(fd, REG_IOC_W16, buf);  /* val = buf[1] */
}
else if (strcmp(argv[1], "w32") == 0)
{
    ioctl(fd, REG_IOC_W32, buf);  /* val = buf[1] */
}
else
{
    printf(argv[0]);
    return -1;
}

return 0;

}
{% endcodeblock %}

猜你喜欢

转载自blog.csdn.net/hceng_linux/article/details/89913985
今日推荐