1 用户态定义寄存器结构
以键盘keyscan
为例,定义一个IP寄存器描述头文件,IOCRREG
, IOCWREG
定义了两个ioctl命令,用来读写寄存器。struct msg
用来存放寄存器地址和值。
/* Keyscan register: addr + offset + mask */
#define keyscan_top_keyscan_config1 0x0
#define keyscan_top_keyscan_config2 0x4
#define keyscan_top_keyscan_config3 0x8
#define keyscan_top_keyscan_config4 0xc
......
#define keyscan_top_reg_fifo_count 0x20
#define keyscan_top_reg_fifo_count_OFFSET 0
#define keyscan_top_reg_fifo_count_MASK 0xf
#define keyscan_top_reg_fifo_not_empty 0x20
#define keyscan_top_reg_fifo_not_empty_OFFSET 4
#define keyscan_top_reg_fifo_not_empty_MASK 0x10
struct msg {
long unsigned int addr;
unsigned int data;
};
#define IOC_MAGIC 'k'
#define IOCRREG _IOR(IOC_MAGIC, 1, struct msg)
#define IOCWREG _IOW(IOC_MAGIC, 2, struct msg)
2 驱动代码
static long keyscan_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct msg m;
memset(&m, 0, sizeof(m));
void __iomem* base = dev->base;
switch (cmd) {
case IOCRREG:
if (copy_from_user(&m, (struct msg __user *)arg, sizeof(m)))
return -EFAULT;
m.data = readl(base + m.addr);
printk(KERN_DEBUG "base_addr:0x%lx, offset:0x%lx, read data: 0x%x \n", base, m.addr, m.data);
if (copy_to_user((struct msg __user *)arg, &m, sizeof(m)))
return -EFAULT;
break;
case IOCWREG:
if (copy_from_user(&m, (struct msg __user *)arg, sizeof(m)))
return -EFAULT;
printk(KERN_DEBUG "base_addr:0x%lx, offset:0x%lx, write data: 0x%x \n", base, m.addr, m.data);
writel(m.data, base + m.addr);
break;
default:
return -EINVAL;
}
return 0;
}
驱动先定义IP base addr
,然后透过ioctl
进行arg
参数接收,确定msg
中的addr
和data
, 接收cmd
调用writel, readl
进行读写。
3 用户态代码
static inline void clrsetbits_32(long unsigned int addr, unsigned int clear, unsigned int set) {
int ret;
struct msg m;
memset(&m, 0, sizeof(m));
m.addr = addr;
ret = ioctl(fd, IOCRREG, &m);
if (ret) {
perror("ioctl: read error!");
return;
}
m.data = (m.data & (~clear)) | set;
ret = ioctl(fd, IOCWREG, &m);
if (ret) {
perror("ioctl: write error");
return;
}
return;
}
static unsigned int read_reg(long unsigned int addr) {
int ret;
struct msg m;
memset(&m, 0, sizeof(m));
ret = ioctl(fd, IOCRREG, &m);
if (ret) {
perror("ioctl: read reg error!");
return -1;
}
return m.data;
}
首先实现基础读写函数进行寄存器读写,reg_read
函数传入addr
即可得到val
, clrsetbits_32
需要先读,在写入val.
#define KEYSCAN_MASK(REG_NAME) keyscan_top_##REG_NAME##_MASK
#define KEYSCAN_OFFSET(REG_NAME) keyscan_top_##REG_NAME##_OFFSET
#define KEYSCAN_SET(REG_NAME, VAL) \
clrsetbits_32(keyscan_top_##REG_NAME, KEYSCAN_MASK(REG_NAME), \
(VAL) << KEYSCAN_OFFSET(REG_NAME))
#define KEYSCAN_GET(REG_NAME) \
((read_reg(keyscan_top_##REG_NAME) & KEYSCAN_MASK(REG_NAME)) >> \
KEYSCAN_OFFSET(REG_NAME))
例如,当调用
KEYSCAN_GET(reg_fifo_count); //表示read_reg(keycan_top_reg_fifo_count) & keycan_top_reg_fifo_count_MASK >> keycan_top_reg_fifo_count_OFFSET
reg_fifo
表示IP的某一个寄存器,count
表示位域,因此对OFFSET
定义成位域在该寄存器的偏移量(count位域是bit[3:0]
)定义为0,因此MASK
定义为0xf
.
同理not_empty
也是一个位域,bit[4]
,OFFSET
定义成4,MASK
定义成0x10
,来屏蔽除bit[4]
的其他bit.
KEYSCAN_GET(reg_fifo_count);
最终就获取到了reg_fifo
寄存器的count
位域的内容。
KEYSCAN_GET(reg_fifo_not_empty);
最终就获取到了reg_fifo
寄存器的not_empty
位域的内容
又例如,当调用
KEYSCAN_SET(reg_enable, 1);//表示clrsetbits_32(keycan_top_reg_enable, keycan_top_reg_enable_MASK, 1 << keycan_top_reg_enable_OFFSET
#define keyscan_top_reg_row_mask 0x0
#define keyscan_top_reg_row_mask_OFFSET 0
#define keyscan_top_reg_row_mask_MASK 0xff
#define keyscan_top_reg_col_mask 0x0
#define keyscan_top_reg_col_mask_OFFSET 8
#define keyscan_top_reg_col_mask_MASK 0xff00
#define keyscan_top_reg_enable 0x00
#define keyscan_top_reg_enable_OFFSET 16
#define keyscan_top_reg_enable_MASK 0x10000
可以看到offset
定义为16
,mask定义为0x10000
,用来屏蔽除bit[16]
的其他位。clrsetbits_32
会先读出该寄存器,然后对该位set1, mask掉其他位,再次写入该寄存器。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
2022-08-27 vmware虚拟机使用双网卡(NAT+桥接)