修改Linux内核驱动程序以使用虚拟地址而不是物理地址
我修改了Linux内核声音驱动程序,在接收数据时使用新的虚拟地址而不是物理地址。当驱动程序被调用时,它报告数据为0。我正在寻找有关我在流程和实施中可能缺少的内容的任何意见或反馈。如果我在下面的描述过于模糊,无法给出适当的反馈,我很乐意展开并提供更多细节 我在地址修改Linux内核驱动程序以使用虚拟地址而不是物理地址,linux,memory,linux-kernel,alsa,dma,Linux,Memory,Linux Kernel,Alsa,Dma,我修改了Linux内核声音驱动程序,在接收数据时使用新的虚拟地址而不是物理地址。当驱动程序被调用时,它报告数据为0。我正在寻找有关我在流程和实施中可能缺少的内容的任何意见或反馈。如果我在下面的描述过于模糊,无法给出适当的反馈,我很乐意展开并提供更多细节 我在地址0xc0056014处有一个物理寄存器。硬件寄存器可读,并在设备树和Linux内核(4.4.71)中正确初始化。寄存器保存从麦克风输入的音频数据 物理寄存器中的位没有正确映射以进行播放。这会导致音频播放充满白噪声和嘶嘶声。我已经制作了一个
0xc0056014
处有一个物理寄存器。硬件寄存器可读,并在设备树和Linux内核(4.4.71)中正确初始化。寄存器保存从麦克风输入的音频数据
物理寄存器中的位没有正确映射以进行播放。这会导致音频播放充满白噪声和嘶嘶声。我已经制作了一个用户空间程序,它将获取原始音频样本,并将数据位重新映射到新的原始/wav样本,其中播放质量符合预期
我的目标是在内核空间中重新映射这些数据。
物理寄存器中的示例数据:0x4000f965
重新映射的示例数据:0xf9654000
在为硬件音频接口编写的内核声音驱动程序中,我完成了以下工作:
vzalloc(数据大小)
\uuu iomem*regs=ioremap(0xc0056014,4)映射物理寄存器代码>
memcpy(dst_addr、src_addr、data_size)将重映射值写入虚拟地址代码>
if (phy_base == 0xC0056000 && dma == &par->capt)
{
void *p;
size_t data_size;
data_size = sizeof(unsigned long);
p = vzalloc(data_size);
void *dst_addr = (void *)(p);
printk(KERN_INFO "Virtual Address is : 0x%p\n", p);
char our_thread[8]="thread1";
thread1 = kthread_create(thread_fn,dst_addr,our_thread);
if((thread1))
{
printk(KERN_INFO "Hit thread1");
wake_up_process(thread1);
}
dma->peri_addr = dst_addr;
printk(KERN_INFO "snd i2s1: %s dma peri addr is 0x%p and virtual addr is 0x%p\n", STREAM_STR(i), (void *)dma->peri_addr, p);
}
线程:
int thread_fn(void *dst_addr) {
printk(KERN_INFO "Hit inside thread1\n");
size_t data_size;
data_size = sizeof(unsigned long);
int k = 0;
while (1) {
//printk(KERN_INFO "Virtual Address is : 0x%p\n", p);
unsigned long read_result;
void __iomem *regs = ioremap(0xc0056014, 4);
read_result = *((unsigned long *) regs);
uint32_t sample = read_result;
uint32_t sample1, sample1buf1, sample1buf2, sample1buf3, sample1buf4;
sample1buf1 = (sample >> (8*0)) & 0xff;
sample1buf2 = (sample >> (8*1)) & 0xff;
sample1buf3 = (sample >> (8*2)) & 0xff;
sample1buf4 = (sample >> (8*3)) & 0xff;
/* Shift bytes from s16le format bit order to s32le format bit order
[ L | R ] [ L]
[ L | R ] [ R]
MSB MSB MSB
[99 f9 | 00 40] -> [00 40 99 f9]
[cf f9 | 00 40] -> [00 40 cf f9]
[12 34 | 56 78] -> [56 78 12 34]
*/
sample1buf1 = sample1buf1 << 16;
sample1buf2 = sample1buf2 << 24;
sample1buf4 = sample1buf4 << 8;
sample1 = sample1 & 0x00000000;
sample1 = sample1 | sample1buf3;
sample1 = sample1 | sample1buf4;
sample1 = sample1 | sample1buf1;
sample1 = sample1 | sample1buf2;
sample = sample1;
void *src_addr;
src_addr = &sample;
memcpy(dst_addr, src_addr, data_size);
iounmap(regs);
}
return 0;
}
我唯一要做的就是将peri_addr指向虚拟addr
dma->peri_addr=dst_addr代码>什么是peri_addr
?如何处理报告给ALSA API的缓冲区?指针回调呢?和期限完成通知?请参阅初始帖子中的响应编辑。你可能知道我错过了什么。您希望在何时何地使用时段完成通知?您对“pcm dma参数结构”的含义是什么?在哪里使用?怎么用?瞧,什么?它是一种保存设备参数的结构,在本例中为麦克风。哪里音频驱动程序使用它来定义、设置、识别和引用音频端口的信息。怎么用?通过alsa驱动程序snd_soc_dai_ops
结构启动、停止、暂停、触发声卡上的设备。在结构内部,有以下内容:bool-active代码>bool(*dma_filter)(结构dma_chan*chan,void*filter_参数)代码>char*dma\u\u名称代码>dma地址周期地址代码>int-bus\u-width\u字节代码>int max\u突发字节代码>无符号整数实时钟代码>结构设备*dev
dma\u addr\t
不是CPU内存总线上的物理地址,而是设备使用的总线上的物理地址。在任何情况下,都不能在那里使用虚拟地址。你的驱动程序必须有一个环形缓冲区。
`void *src_addr;
src_addr = &sample;
memcpy(dst_addr, src_addr, data_size);`