在 Hackaday.io 上寻找项目时,我偶然发现了 Stephen Holdaway 的这个项目(点击)。在这个项目中,他解决了每个双启动用户面临的一个令人沮丧的任务,即在我们想要切换到 Windows 的任何时候,等待从 GRUB 菜单中选择 os (Windows)。他能够添加一个硬件开关,以确定每次打开计算机时要启动的操作系统。
他通过将 STM32 微控制器配置为 USB 大容量存储设备来实现这一点。他在hackaday 帖子(点击)中记录了他通过项目研究和实施的整个旅程。请阅读他的帖子以更好地了解实现。
在这个项目中,我将展示如何将更改移植到 Raspberry Pi Pico。你可以在这个 GitHub Repo (点击)中找到我的版本。
GNU GRUB 是一个在加载任何操作系统之前运行的程序。通过这个菜单,我们可以选择要加载的操作系统。GRUB 提供的模块非常有限。这意味着它无法从通过 USB 连接的微控制器读取数据。但它可以从存储磁盘中读取数据。
因此,我们可以通过将我们的微控制器枚举为大容量存储设备来欺骗 GRUB 从微控制器读取数据。
因此,我们通过tinyUSB库将我们的树莓派 pico 枚举为大容量存储设备,该库将具有文件switch.cfg文件,pico 将向其中写入开关位置,即1 表示 ON 0 表示 OFF 。
我们必须在 GRUB 中添加一个脚本,它是读取switch.cfg文件并将默认设置为0(Ubuntu )/2(Windows) 的函数。
GRUB 在加载时运行我们的自定义脚本,然后通过其 UUID 标识符搜索我们的设备,如果退出则读取switch.cfg文件。获得开关位置后,它分别设置默认的操作系统选择。
总之,
我使用 tinyUSB 的cdc_msc示例来实现这一点。该示例将 pico 配置为大容量存储设备,并创建 FAT12 文件系统并枚举README.txt文件。
我将README.txt更改为switch.cfg并将“set os_hw_switch=0\n”行添加到文件中。
#define SWITCH_CFG_CONTENTS \
"set os_hw_switch=0\n"
...
//------------- Block3: Readme Content -------------//
SWITCH_CFG_CONTENTS
现在我们已将 pico 配置为大容量存储设备。将uf2文件复制到pico后,枚举为存储设备。我们将需要 GRUB 脚本的设备 UUID id,即UUID="0000-1234"。
$ sudo blkid
...
/dev/sda: SEC_TYPE="msdos" LABEL_FATBOOT="TinyUSB MSC" LABEL="TinyUSB MSC" UUID="0000-1234" BLOCK_SIZE="512" TYPE="vfat"
现在我们需要读取开关位置并相应地更改switch.cfg文件的内容,即
我使用 GPIO_PIN 28 作为开关引脚,设置为下拉。
read_switch_value返回开关位置,即“1”打开(拉高),“0”关闭(拉低)。
//-------------------------main.c---------------------
#define SWITCH_PIN 28
// read switch value
uint8_t read_switch_value()
{
return gpio_get(SWITCH_PIN) ? '1' : '0';
}
int main(void)
{
gpio_init(SWITCH_PIN);
//configure pin as INPUT
gpio_set_dir(SWITCH_PIN, false);
//configure pin as PULL_DOWN
gpio_set_pulls (SWITCH_PIN,false,true);
要将开关位置写入switch.cfg ,我使用了调用read_switch_value函数的readGRUBConfig() ,并使用开关位置设置输出缓冲区。
我发现在读取第三个 block3 时lba设置为 3,因此拦截调用并调用readGrubConfig并传递将复制文件内容的缓冲区。
//-------------------------msc_disk.c---------------------
static char grubConfigStr[] = "set os_hw_switch=0\n";
static void readGrubConfig(uint8_t* output)
{
// Modify config string with current switch value
grubConfigStr[sizeof(grubConfigStr)-3] = read_switch_value();
memcpy(output, &grubConfigStr, sizeof(grubConfigStr));
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
(void) lun;
// when reading the file
if(lba == 3){
readGrubConfig(buffer);
return bufsize;
}
...
...
}
我们需要将 pico stdlib添加到我们的代码中以获取gpio引脚访问权限。
//-------------------------main.c-----------------------------------
#include
#include
#include
#include "bsp/board.h"
#include "tusb.h"
...
#include "pico/stdlib.h"
制作项目:
$ mkdir build
$ cd build
$ cmake ..
$ make
我在我的 Ubuntu 20.10 中添加了这些更改。
$ sudo vim /etc/grub.d/40_custom
....
# Look for hardware switch device by its hard-coded filesystem ID
search --no-floppy --fs-uuid --set hdswitch 0000-1234
# If found, read dynamic config file and select appropriate entry for each position
if [ "${hdswitch}" ] ; then
source ($hdswitch)/switch.cfg
if [ "${os_hw_switch}" == 0 ] ; then
# Boot Linux
set default="0"
elif [ "${os_hw_switch}" == 1 ] ; then
# Boot Windows
set default="2"
else
# Fallback to default
set default="${GRUB_DEFAULT}"
fi
else
set default="${GRUB_DEFAULT}"
fi
首先,我们搜索我们的文件系统。GRUB 有一个子命令search就是为了这个。
如果找到任何设备,则将找到的第一个设备设置为环境变量的值。
--set hdswitch hdswitch是我们的环境变量,如果找到,则使用磁盘名称进行设置。
接下来,如果设置了hdswitch变量 ,我们将获取 switch.cfg文件,这将创建另一个具有开关位置的环境变量,即 0/1。os_hw_switch
我们读取 的值 os_hw_switch
并将默认值分别设置为 0 或 2。0 因为 Ubuntu 在 GRUB 菜单中位于第 0 位,而 windows 位于第 2 位。
最后,如果 没有设置hdswitch ,我们将默认设置为GRUB_DEFAULT。
现在我们需要更新我们的 grub:
$ sudo update-grub
我们完成了。
在连接设备的情况下重新启动系统。
如果一切正常,打开按钮将选择 windows 或 ubuntu(如果关闭)。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !