ARM Linux 驱动 利用 ioremap 动态修改寄存器实现 beep 的打开和关闭

前言

我们有时想要临时修改一个寄存器的值, 虽然可以专门写个驱动模块但是有点麻烦. 因此就写个简单的动态修改寄存器值的工具

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");

效果, 打开和关闭beep, 按照寄存器白皮书设定参数即可

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Potcutre

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值