简介

​ 近段时间,msm8916-mainline项目的大佬们又为红米2完成了充电芯片的驱动,这意味着可以在主线内核中能够正常充电和检测电池容量,还有otg的供电。距离完整可用的主线Linux手机又进了一步。

​ Postmarketos项目的那帮大佬也不知道最近在整什么新的功能,导致edge版本的postmarketos要么会在构建rootfs时对包的checksum报错,要么刷进去产生一些稀奇古怪的bug,体验很差,也许基于musl libc的alpine linux可能真的不太适合手机这种设备,postmarketos的稳定版还没有完全准备好的情况下,想要让这些老爷机发挥余热也只能继续造新的轮子了。

​ 另外,针对于移动端的debian衍生版mobian也推出了搭载sdm845芯片手机的支持。终于有了与msm8916芯片相似度更高的平台出现,这也使我的移植更为轻松了,为了方便起见,我没有完成msm8916平台支持包的打包,而是在mobian一加6的刷机包上进行修改,最终形成一个个人觉得很稳定的debian刷机包,这个基本上可以算的上是一个最廉价的带屏幕的树莓派代替方案了,除了没有使用chroot方案安卓层的性能损耗和一个完整的,带systemd的文件系统,它还拥有比各种派更多的传感器和电池等一大波外设,最终完成了这么一个刷机包,(⊙v⊙)嗯~想想还有点小激动。

我最终解决了前篇文章中提到的一些致命的问题,使得不是那么令人抓狂的、可以使用的小型linux设备成为可能。

最终的系统镜像会分享给大家,Have Fun ~~

目前硬件支持的程度

支持的

  • wifi
  • 蓝牙
  • 重力传感器
  • gpu
  • 显示
    • 某些红米2的屏幕驱动暂时不能正确处理屏幕休眠,休眠唤醒之后可能会有轻微的残影或者闪屏现象
    • 已知完美的屏幕有 mdss_dsi_nt35521_720p_video 不会闪屏和残影。
    • 屏幕型号请参见lk2nd的PANEL那一栏。
  • 充电 & 电量显示
  • otg usb
  • 触摸屏
  • 放音 & 录音
  • gps
  • 旁边的音量键和电源键

不支持的

  • 前后摄像头 (调了几天,结果相机模块连电都通不了,果真还是自己太菜了~)
  • modem (通话、发短信、流量)<-- 自己用不到就没有调~~
  • 面板上的三大功能键(反正这个系统里面这几个键没什么用了)
  • 光线传感器 (懒得调,主线linux有驱动)
  • 磁场传感器(懒得调,主线linux有驱动)
  • 休眠(容易睡死,可能很长时间才会唤醒,建议关掉休眠)

填坑

解决debian的ramdisk挂载不上文件系统的问题

​ 最主要的问题是msm8916的mmc设备号在启动的时候不是固定的,有时候是mmc1,有时候是mmc0。postmarketos的解决方案是魔改initramfs,同时把system(看安装的分区来定也可能是userdata)划分为两个分区,一个叫pmos_boot,一个叫pmos_root,分别存储内核、alpine的initramfs和rootfs。在boot分区中的initramfs在启动时会按照卷标名去查找目标分区,然后引导真正的initramfs,这种解决方案巧妙之处就在于可以适用于各种空间大小的boot.img分区,不至于initramfs太大而刷不进去。

​ 一开始我就放弃了,因为如果要学postmarketos的方案的话就需要自己构建initramfs,直到无聊的我在GPT分区的archlinux执行了cat /proc/cmdline这个指令

guo@handsomelaptop cat /proc/cmdline            
BOOT_IMAGE=/boot/vmlinuz-linux-lts root=UUID=e7c0a739-2da2-4422-b936-f3a0ccd8914b rw loglevel=3 quiet

​ 原来initramfs可以通过指定UUID的方式来确定root分区,使用userdata分区的UUID来生成新的boot.img,一切就迎刃而解了!还好老米的boot分区管够,轻松放下一个包含所有内核模块的标准Debian Initramfs。。。

mkbootimg --base 0x80000000 \
        --kernel_offset 0x00080000 \
        --ramdisk_offset 0x02000000 \
        --tags_offset 0x01e00000 \
        --pagesize 2048 \
        --second_offset 0x00f00000 \
        --ramdisk initrd \
        --cmdline "console=tty0 root=UUID=9086bed9-957e-46e8-8beb-32b3727cc753 rw loglevel=3 splash"\
        --kernel kernel-dtb -o boot.img

解决没有声音的问题

​ 事实上,我还没能完全解决声音的问题,插入耳机切换好像仍然没有工作,如果要使用耳机需要在设置里面手动设置,其他完美。在msm8916-mainline项目里面把ucm文件拷到/usr/share/alsa就可以使用了。

msm8916声卡ucm文件

驱动摄像头(尚未完成)

​ 整个postmarkos社区至今为止都没有人跑通过任何一款msm8916手机的摄像头,但是摄像头的驱动在主线中存在很久了,主要是供DragonBoard使用的。主要的难点主要有三个,一个是控制相机的供电,还有就是驱动和firmware。

​ 高通的相机i2c(cci)在安卓内核里面是没有办法通过i2c-tools读取的,所以我连i2c到底什么地址上接了什么都摸不太清,只能把相机的供电调好才能够知道两个摄像头的i2c地址,可是在目前的状态下i2c下,还是什么设备都没有。看原厂的内核日志,在摄像头启动的时候还加载了固件进去,而且固件还不止一个,可能跑起来有一定的难度。

​ 但是通过分析原厂内核源码和设备树,还是大体能够知道摄像头的一些信息。红米2后摄像头是ov8865,还挂了一颗eeprom,可能需要魔改驱动才能用上这颗eeprom里面的校准数据。前摄是ov5670。幸运的是我找到了后摄像头在安卓用户层的驱动(就vendor里面的动态链接库)的源码,应该需要稍微亿些些研究才能够跑通吧。

ov8865源码

eeprom源码

另外的一些尝试

​ 基于lk2nd的引导方法还是太过固定和死板了,还是安卓的那一套,采用boot.img的方法来加载内核使得内核的更新不是那么的通用(相较于其他单板电脑来说)。所以,一种新的引导方式的存在便极为重要了,也可使从sd卡引导系统和更为通用和统一的文件系统构建方法成为可能。

基于kexec的bootloader

​ 安卓的boot分区的设计初衷就是需要有一个被上一级bootloader加载的内核和ramdisk。而从2.x的linux内核开始linux就引入了kexec功能,使得一个linux内核可以引导另一个linux内核进行启动。若使用一个带kexec的内核和携带相应用户层工具的ramdisk便可以从任何分区引导任何合法的内核镜像,让多系统共存成为可能。但是单纯的从上一级原厂lk bootloader里传来的内核参数也许对于主线内核来说还是少了些东西,还是要让lk2nd把正确的参数传给这个主线内核,但是这个启动的过程就变得更加复杂了,好处就是比较好实现~

前面的一大批初始化程序 --> lk --> lk2nd --> kexec --> 目标内核
开启simple-framebuffer

​ 高通在主线的drm十分的奇怪,不能编译成内建的模块也就是选项,否则这个模块就会加载失败,还有就是不能在wifi不工作的情况下运行,wifi必须在正常加载firmware并运行的情况下drm才能正常运行,还有就是所有的panel和drm模块加载的顺序是确定的,panel与drm加载的顺序不对也是没有显示的。(来自postmarket os wiki)

​ 所以若是要在空间有限的情况下完成显示,使用drm是不太现实的,最好的情况是使用来自bootloader初始化好的framebuffer,经过多次尝试,对红米2的设备树修改如下。使用的是dragonPi项目中我移植msm8916-mainline项目的一部分特性的5.10内核。

	reserved-memory {
		/delete-node/ wcnss@89300000;
		/delete-node/ venus@89900000;
	// 让内核不要动framebuffer的内存
	+ framebuffer@83000000 {
	+   	reg = <0x0 0x83000000 0x0 0x1400000>;
	+   	no-map;
	+ };

		mpss_mem: gps_mem: mpss@86800000 {
			status = "disabled";
			reg = <0x0 0x86800000 0x0 0x5100000>;
			no-map;
		};

		wcnss_mem: wcnss@8b900000 {
			reg = <0x0 0x8b900000 0x0 0x600000>;
			no-map;
		};

		venus_mem: venus@8bf00000 {
			reg = <0x0 0x8bf00000 0x0 0x600000>;
			no-map;
		};
	};
	
// chosen 加入以下内容	
	chosen {
		stdout-path = "serial0";
		
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;

		framebuffer0: framebuffer@83200000 {
			status = "okay";
			compatible = "simple-framebuffer";
			reg = <0x0 0x83200000 0x0 (720 * 1280 * 3)>;
			width = <720>;
			height = <1280>;
			stride = <(720 * 3)>;
			format = "r8g8b8";
			power-domains = <&gcc MDSS_GDSC>;
			// 不要让系统重新初始化这些时钟
			clocks = <&gcc GCC_MDSS_AHB_CLK>,
				 <&gcc GCC_MDSS_VSYNC_CLK>,
				 <&gcc GCC_MDSS_AXI_CLK>,
				 <&gcc GCC_MDSS_MDP_CLK>,
				 <&gcc GCC_MDSS_BYTE0_CLK>,
				 <&gcc GCC_MDSS_PCLK0_CLK>,
				 <&gcc GCC_MDSS_ESC0_CLK>;
		};

	};
	// 弄掉高通的mipi驱动和gpu驱动 把mdss和gpu有关的节点都设为disabled

至此,我们的bootloader便可以丢掉一大波多余的内核模块了~

魔改jumpdriver

​ jumpdriver是postmarketos社区和danct12大佬弄出来的一个给pinephone刷机的小型linux系统,仅仅只带了一个busybox,用于把内置的emmc挂载成电脑u盘。buildroot对于这种简单用途来说显然是太肥了,所以我魔改了jumpdriver,让它能够在msm8916的机器上运行,带有kexec-tools和kexecboot等必要的软件,作为kexec bootloader的主体部分,执行例如扫描硬盘和开始kexec的操作。(kexec-tools暂时还用不了libz,所以还不支持以gz压缩的内核镜像)

DragonRoot

修改kexecboot

​ kexecboot是一个很老的项目,不太著名的项目maemo leste的driod4移植就是使用的kexecboot,这个应用程序能够自动挂载各种格式的分区然后在分区里面寻找包含引导项的配置文件,再通过配置文件作为kexec的参数执行kexec引导新的内核。

​ 但是在archlinux gcc版本升级到了11,引入了一些稀奇古怪的特性,比如说不能在多个c文件里声明同一个名字的公有变量,这使得kexecboot在arch里面编译不过去了。稍微修了一波,可以完美编译了,我也尝试着向kexecboot项目pull request了,但是估计是我太菜这个pull request一直就没接受~~

​ 如有需要的话可以使用我的fork

能运行,但没完全运行~
kexecboot

kexec-hardboot ?

​ 捣鼓出来能够boot到kexecboot阶段的bootloader以后,它能够正确挂载sd卡和emmc,正确加载里面的文件,但是没有办法引导新的内核,主要是由于kexec没有办法重置cpu核心,内核一直都在报cpus are stuck in the kernel的错误。看了下相关的代码这也许是由于缺少psci功能的trustzone firmware导致的,试着把dragonboard 410c的trustzone刷到红米2里面,结果红米2的分区太小刷不进去。只能去寻找另一个方法 kexec-hardboot。

Kexec-hardboot patch adds a real device restart to that process, so that all the drivers can be properly reinitialized. It stores new kernel to RAM, reboots the device as usual, and kernel from boot partition immediately jumps to the one which was stored to RAM before reboot.

​ 这个办法是在nexus5时代著名的MultiROM所采用的,它在整个kexec过程中加入了重启,确保整个硬件在新内核中能够正确的被初始化,看了下红米2早就有MultiRom支持了,这个要对主线内核和kexec-tools做一些修改,限于时间问题也只能作罢。

U-boot 支持

如果想实现像传统arm linux的那种引导方式又不想在整个引导环境中引入更多的环节,也可以使用u-boot来代替lk2nd的工作。一个印度老哥做了一个移植,试了一下用串口可以正常操作uboot终端,但是屏幕是不工作的,而且在archlinux上编译的u-boot启动不了。这个移植现在可以正确的为主线内核配置mac地址,但是好像不能给主线内核传电池配置和mipi panel相关的参数,有时间我在修一下这个方案,感觉比kexecboot的那个更麻烦一点。
U-boot-wt88047项目

效果展示

看B站
流畅能看

mesa的3d测试
mesa
python+opencv+usb摄像头测试
opencv
支持自动翻转屏幕
横屏

搜狗输入法(工作不正常而且会破坏屏幕键盘的一些功能)
搜狗

关于DragonPi项目

​ 为了探索高通骁龙手机的主线内核类似树莓派的玩法,我创建了DragonPi项目。未来主要会放出一些linux发行版的构建系统源码(buildroot、armbian)和一些bootloader实现还有内核源码,目前的计划是专心做好红米2的主线方案,跑通尽可能多的外设,为大家带来便宜又好玩的硬件,觉得我这个项目对你有所帮助的话,你也可以资助我们的项目,让它变得更好!

DragonPi项目

刷机包下载

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐