修改后的Linux内核驱动程序,以使用虚拟地址代替物理地址

时间:2018-08-16 14:15:43

标签: linux memory linux-kernel alsa dma

我已经修改了Linux内核声音驱动程序,以在接收数据时使用新的虚拟地址而不是物理地址。调用驱动程序后,它将报告数据为0。我正在寻找有关过程和实现中可能缺少的任何输入或反馈。如果我在下面的描述过于模糊而无法给出正确的反馈,那么我很乐意扩展并提供更多详细信息。

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

物理寄存器中的位未正确映射以进行播放。这会使音频播放充满白噪声和嘶嘶声。我制作了一个用户空间程序,该程序将获取原始音频样本,并将数据位重新映射到新的原始/ wav样本,从而达到预期的播放质量。

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

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

  1. 使用vzalloc(data_size)初始化一个新的虚拟地址
  2. 启动了一个线程。
  3. 在线程中,映射物理寄存器为__iomem *regs = ioremap(0xc0056014, 4);
  4. 将寄存器值读入临时缓冲区并重新映射32位。
  5. 使用memcpy(dst_addr, src_addr, data_size);将重映射值写入虚拟地址
  6. 最后,在驱动程序正在设置物理寄存器地址的线程之外,我更改为虚拟地址。 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;

0 个答案:

没有答案