字符设备驱动-用户态构造IP寄存器结构体和读写寄存器

1 用户态定义寄存器结构#

以键盘keyscan为例,定义一个IP寄存器描述头文件,IOCRREG, IOCWREG定义了两个ioctl命令,用来读写寄存器。struct msg用来存放寄存器地址和值。
image
image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 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 驱动代码#

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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中的addrdata, 接收cmd调用writel, readl进行读写。

3 用户态代码#

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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));
m.addr = addr;
ret = ioctl(fd, IOCRREG, &m);
if (ret) {
perror("ioctl: read reg error!");
return -1;
}
return m.data;
}

首先实现基础读写函数进行寄存器读写,reg_read函数传入addr即可得到val, clrsetbits_32需要先读,在写入val.

1
2
3
4
5
6
7
8
#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))

image

例如,当调用

1
2
3
4
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位域的内容

又例如,当调用

1
2
3
4
5
KEYSCAN_SET(reg_enable, 1);
//表示
clrsetbits_32(keycan_top_reg_enable,
keycan_top_reg_enable_MASK,
1 << keycan_top_reg_enable_OFFSET);

image

1
2
3
4
5
6
7
8
9
#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掉其他位,再次写入该寄存器。