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
节点来访问硬件寄存器,可以通过devmem
或devmem2
等应用程序来读写寄存器。
一些嵌入式的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 (J.D.Bakker@its.tudelft.nl)
- 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
节点存在;
二是不能同时读取多个寄存器值;
三是必须依赖应用程序,不能直接echo
或cat
读写寄存器;
因此,编写一个新的驱动和应用程序,独立的实现读写寄存器的功能,以解决前面可能出现的情况。
驱动程序如下:
{% 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(®_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(®_cdev, &ker_rw_ops);
ret = cdev_add(®_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, ®_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(®_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, ®_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(®_cdev);
mutex_unlock(&rw_msg.lock);
}
module_init(ker_rw_init);
module_exit(ker_rw_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng huangcheng.job@foxmail.com”);
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 %}