我已经修改了Linux内核声音驱动程序,以在接收数据时使用新的虚拟地址而不是物理地址。调用驱动程序后,它将报告数据为0。我正在寻找有关过程和实现中可能缺少的任何输入或反馈。如果我在下面的描述过于模糊而无法给出正确的反馈,那么我很乐意扩展并提供更多详细信息。
我在地址0xc0056014
有一个物理寄存器。硬件寄存器是可读的,并且已在设备树和Linux内核中正确初始化(4.4.71)。该寄存器保存从麦克风输入的音频数据。
物理寄存器中的位未正确映射以进行播放。这会使音频播放充满白噪声和嘶嘶声。我制作了一个用户空间程序,该程序将获取原始音频样本,并将数据位重新映射到新的原始/ wav样本,从而达到预期的播放质量。
我的目标是在内核空间中完成此数据重映射。
物理寄存器中的示例数据:0x4000f965
重新映射的示例数据:0xf9654000
在为硬件的音频接口编写的内核声音驱动程序中,我完成了以下操作:
vzalloc(data_size)
初始化一个新的虚拟地址__iomem *regs = ioremap(0xc0056014, 4);
memcpy(dst_addr, src_addr, data_size);
将重映射值写入虚拟地址dma->peri_addr = dst_addr;
我能够执行内核打印,以读取我的虚拟地址中的值。内核打印文件在虚拟地址中显示正确的值,但是当使用alsa录音工具调用内核声音驱动程序时,音频数据被报告为0。
内核未报告任何错误或警告,我对丢失的内容或下一步的工作感到有些困惑。欢迎任何指示,技巧,反馈或问题!干杯。
驱动程序设置地址:
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是物理数据地址寄存器。先前设置为0xc0056014。我更改为指向虚拟地址。
dma->peri_addr = phy_base + (i == 0 ? I2S_TXD_OFFSET : I2S_RXD_OFFSET);
dma
指向pcm dma参数结构的指针
一旦缓冲区正确映射了数据,我就使用memcpy
将其移动到虚拟地址。
`void *src_addr;
src_addr = &sample;
memcpy(dst_addr, src_addr, data_size);`
我唯一要做的就是将peri_addr指向虚拟地址。
dma->peri_addr = dst_addr;