前言
我们有时想要临时修改一个寄存器的值, 虽然可以专门写个驱动模块但是有点麻烦. 因此就写个简单的动态修改寄存器值的工具
main 代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
int ret;
if (argc < 4) {
printf("Sample# ./reg w 0xC001C000 32,28,3,1 4,14,1,1 0,14,1,1 // 写入寄存器(蜂鸣器响) \n");
printf("Sample# ./reg w 0xC001C000 32,28,3,1 4,14,1,1 0,14,1,0 // 写入寄存器(蜂鸣器关) \n");
return -1;
}
struct info_option option;
fd = open("/dev/myinfo", O_RDWR);
if (fd == -1) {
perror("open");
return -1;
}
if (strcmp(argv[1], "w") == 0)
{
char *addr = argv[2];
char *tokenPtr;
int i;
unspace(addr, strlen(addr));
option.address = strtoul(addr, NULL, 16);
printf("写入地址: %#x\n", option.address);
for(i = 3; i < argc; i++) {
int j = 0;
char *argx = argv[i];
tokenPtr = strtok(argx, ",");
// 分解
while(tokenPtr != NULL && strlen(tokenPtr) > 0) {
if (j == 0) {
option.offset = atoi(tokenPtr);
}
else if (j == 1){
option.pos = atoi(tokenPtr);
}
else if (j == 2){
option.width = atoi(tokenPtr);
}
else if (j == 3){
option.value = atoi(tokenPtr);
}
tokenPtr = strtok(NULL, ",");
j++;
}
if (j == 4) {
ret = ioctl(fd, WRITE_REG, &option);
if (ret < 0) {
perror("WRITE_REG");
return -1;
}
printf("偏移: %03u 位置: %02u 宽度: %02u 值: %u\n", option.offset, option.pos, option.width, option.value);
}
else {
printf("写参数不足, 写入必须要凑齐4个整数\n");
return -1;
}
}
}
close(fd);
return 0;
}
驱动代码
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/unistd.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/io.h> //ioremap,iounmap
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/fs.h> //struct file_operations
#include <linux/uaccess.h>
struct info_option {
unsigned long address;
int count;
int offset;
int pos;
int width;
int value;
};
#define INFO_MAGIC 's'
#define WRITE_REG _IOW(INFO_MAGIC, 1, struct info_option)
//指定宽度指定位置设置内存值
static void wirte_mem(unsigned long addr, int offset, int pos, int width, int value) {
unsigned long *reg = (unsigned long*)(addr + offset);
int i;
//清零指定宽度和位置
for(i=0;i<width;i++) {
*reg &= ~(1 << (pos + i));
}
//指定位置设置指定值
*reg |= (value << pos);
}
static void wirte_reg(unsigned long addr, int offset, int pos, int width, int value) {
//映射
void * vadr = ioremap(addr, sizeof(long));
if (!vadr) {
printk("ioremap fail at %lu", addr);
return;
}
wirte_mem(vadr, offset, pos, width, value);
//解开
iounmap(vadr);
}
static long info_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
struct info_option option;
// 检查
if (_IOC_TYPE(cmd) != INFO_MAGIC)
return -ENOTTY;
// 复制参数
if(copy_from_user(&option, (void *)arg, sizeof(option))) {
return -EFAULT;
}
switch(cmd) {
case WRITE_REG:// 写32位寄存器
wirte_reg(option.address, option.offset, option.pos, option.width, option.value);
break;
default:
return -EFAULT;
}
return 0;
}
static struct file_operations info_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = info_ioctl
};
static struct miscdevice led_misc = {
.name = "myinfo",//自动创建设备文件/dev/myled,混杂设备的主设备号都是相同的
.minor = MISC_DYNAMIC_MINOR,//自动分配次设备号
.fops = &info_fops //给混杂设备添加硬件操作接口
};
static int __init info_init(void)
{
misc_register(&led_misc);
return 0;
}
static void __exit info_exit(void)
{
misc_deregister(&led_misc);
}
module_init(info_init);
module_exit(info_exit);
MODULE_LICENSE("GPL");