修改Linux内核驱动程序以使用虚拟地址而不是物理地址

修改Linux内核驱动程序以使用虚拟地址而不是物理地址,linux,memory,linux-kernel,alsa,dma,Linux,Memory,Linux Kernel,Alsa,Dma,我修改了Linux内核声音驱动程序,在接收数据时使用新的虚拟地址而不是物理地址。当驱动程序被调用时,它报告数据为0。我正在寻找有关我在流程和实施中可能缺少的内容的任何意见或反馈。如果我在下面的描述过于模糊,无法给出适当的反馈,我很乐意展开并提供更多细节 我在地址0xc0056014处有一个物理寄存器。硬件寄存器可读,并在设备树和Linux内核(4.4.71)中正确初始化。寄存器保存从麦克风输入的音频数据 物理寄存器中的位没有正确映射以进行播放。这会导致音频播放充满白噪声和嘶嘶声。我已经制作了一个

我修改了Linux内核声音驱动程序,在接收数据时使用新的虚拟地址而不是物理地址。当驱动程序被调用时,它报告数据为0。我正在寻找有关我在流程和实施中可能缺少的内容的任何意见或反馈。如果我在下面的描述过于模糊,无法给出适当的反馈,我很乐意展开并提供更多细节

我在地址
0xc0056014
处有一个物理寄存器。硬件寄存器可读,并在设备树和Linux内核(4.4.71)中正确初始化。寄存器保存从麦克风输入的音频数据

物理寄存器中的位没有正确映射以进行播放。这会导致音频播放充满白噪声和嘶嘶声。我已经制作了一个用户空间程序,它将获取原始音频样本,并将数据位重新映射到新的原始/wav样本,其中播放质量符合预期

我的目标是在内核空间中重新映射这些数据。 物理寄存器中的示例数据:
0x4000f965
重新映射的示例数据:
0xf9654000

在为硬件音频接口编写的内核声音驱动程序中,我完成了以下工作:

  • 使用
    vzalloc(数据大小)
  • 开始一个线程
  • 在线程中,使用
    \uuu iomem*regs=ioremap(0xc0056014,4)映射物理寄存器
  • 将寄存器值读入临时缓冲区并重新映射32位
  • 使用
    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指向虚拟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);`