要开发驱动程序,您基本上需要执行 3 个步骤:
- 了解 Linux 驱动程序编程的一般知识。这与您的特定硬件无关,并且涉及诸如“我如何将虚拟内存地址转换为物理内存地址并返回?我如何读取字节/将字节写入硬件寄存器?我如何在等待硬件设置时让出 CPU有点?如果设备是中断驱动的,当驱动程序向设备写入命令时如何“停止”驱动程序的执行,以及当设备使用中断告诉我命令时如何“重新启动”它已经完成/数据准备好让我处理?
- 了解硬件的工作原理。它有多少和哪些寄存器,这些寄存器的各个位的含义是什么,我必须向哪个寄存器写入来配置硬件,我如何(在网络摄像头的情况下)从中读取像素硬件,以及如何设置整个图像到内存的 DMA 传输?
- 将 1) 和 2) 的知识应用于您的硬件,编写驱动程序,如果它不能正常工作,则对其进行调试。如果您的用户级软件告诉驱动程序将网络摄像头配置为 1024x768 像素,驱动程序是否将正确的数据写入正确的寄存器?它是否遵守设备强加的任何时间,或者有时是否将您的寄存器设置得太快?
虽然 1) 只是大量阅读和理解文档,但模拟器可能能够帮助解决 2),当然还有 3)。
了解硬件的工作原理
如果你很幸运,你可以做 2) 查看供应商的数据表。在您的情况下,供应商 (Broadcom) 似乎没有发布任何内容,因此找出答案的唯一方法是查看现有驱动程序的功能。这是允许记录某些操作的模拟器可能派上用场的地方。
如果您在 Linux 下的 Qemu 下运行 MacOS,并且 MacOS 驱动程序访问网络摄像头硬件,那么它并没有真正访问硬件。每次硬件访问都会进入 Qemu,Qemu 将拦截访问并将其路由到真正的硬件。可以记录这些操作中的每一个。现在,您可以在 MacOS 中启动一些软件,例如,告诉它将分辨率设置为 1024x768 像素。这可能会导致 QEMU 日志如下:
Write the value 01 to the memory address 1234500
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 00 to the memory address 1234502
Write the value 04 to the memory address 1234503
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 02 to the memory address 1234500
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 00 to the memory address 1234502
Write the value 03 to the memory address 1234503
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 00 to the memory address 1234500
这告诉我们什么?
好吧,您的分辨率值 - 1024x768 - 是 0400 和 0300(十六进制)。这对应于写入 1234502 和 1234503 的字节。因此,这些似乎是设置分辨率的寄存器。但不幸的是,相同的寄存器似乎用于宽度和高度。这可能意味着写入 1234500 的字节是一个选择器 - 在那里写入 1 会将其他 2 个寄存器变为“高度”模式,写入 2 会将它们变为“宽度”模式。
而重复读取1234501呢?
每次向设备写入内容后,该寄存器的第 7 位 (0x80) 似乎已设置,并且驱动程序会一直读取它,直到清除为止;然后继续写一些。因此,该位似乎是“硬件就绪”位,这意味着硬件正在处理您之前的命令,并且无法接受进一步的命令。处理完所有内容后,该位将被清除,并且允许驱动程序写入更多内容。
当然,如果您有实际硬件的文档,所有这些都会容易得多。但是,类似硬件的文档可能仍然有帮助。假设有一个不同的芯片,Broadcom 1571,它有一个 USB 接口。并假设文档说明“要将分辨率设置为 1024x768,您必须0x01 0x00 0x04 0x02 0x00 0x03 0x00通过 USB发送字节”。您会看到字节序列是相同的,但计时内容可能由 1571 芯片的 USB 控制器处理。因此,您可以轻松地将指令“打开 LED,发送0x04 0x01 0x00,关闭发送0x04 0x00 0x00”转换为相应的 PCI 寄存器序列,并使用 QEMU 日志验证驱动程序是否在您打开 LED 时执行相同的操作/off 从您的模拟软件。
开发驱动程序
一旦你有足够的关于硬件的信息,你就可以开始编写 linux 驱动程序了。在这一点上,模拟器可以像跟踪其他驱动程序时一样为您提供帮助 - 在模拟器中运行带有驱动程序的 Linux 可以帮助您调试代码,就像跟踪原始驱动程序时一样。
此外,您可以使用模拟器来防止大量崩溃。您的驱动程序可能存在一些错误,导致其将错误数据写入错误地址,您当然希望阻止它向 PCI/SATA 适配器发送低级格式命令。例如,您可以使用 QEMU 仅将特定范围的 PCI 寄存器传递到实际硬件。或者,如果您的设备文档说明“将 0x01 写入寄存器 7 将信号设置为接地,写入 0x02 将其设置为 +5V。切勿同时设置这两个位,这会造成短路并在几秒钟内炸毁您的设备”,您可以在模拟器中放置适当的保护措施以防止在驱动程序疯狂的情况下发生这种情况。
Qemu 真的可以做到所描述的吗?
可能不是香草版本。qemu 描述中没有提到 PCI 寄存器日志记录。但是,似乎github 上有一个项目将这种日志记录添加到 qemu。这个项目已经 4 年没有更新了,这可能意味着它仍然按预期工作(而且从来没有理由更新它),或者维护者失去了兴趣,它根本不再工作了。
所描述的一些其他用途过于具体而无法轻松配置,因此您可能需要破解 qemu 本身来实现它们。但是,嘿,这就是 qemu 开源的目的。
由于已经有一个项目可以为您的相机开发驱动程序,因此该项目的维护人员很可能已经完成了这项工作。我会联系他们以获取更多信息。