Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

switch_tplg: Implement tool for switching topologies #9525

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions tools/switch_tplg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Topology Switch Tool

Copy link
Collaborator

@dbaluta dbaluta Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a prefix to the commit subject like tools: . Also add some implementations detail in the commit message.

Like for example: we remove and then insert back all SOF related modules thus triggering topology reload.

As a development tool I think this is super useful. On the longer term I wonder if we can have this functionality in the SOF Linux driver.

Cc: @lgirdwood @ranj063

Copy link
Collaborator

@ranj063 ranj063 Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dbaluta @AnneOnciulescu would it make sense to update the sof-insert/sof-remove scripts in the sof-test repo to do what you're suggesting? https://github.com/thesofproject/sof-test/tree/main/tools/kmod

And as for choosing a different topology should be just a simple command to se the kernel module params to choose the new topology isnt it? Can you please elaborate the need of the python tool for choosing a different topology?

Copy link
Collaborator

@dbaluta dbaluta Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be yes, but then the user needs to run 3 scripts instead of selecting the topology from a GUI. On the other hand we can create a wrapper over existing sof-insert / sof-remove scripts.

Let me talk to @AnneOnciulescu and get back with a imporved solution.

The GUI offers you a list of topology to use. That is the nicest part. Lets see though if it worth the effort.

### switch_tplg.py switch_tplg.sh
This tool allows switching to a different topology without rebooting the board.

### How to use the tool
Copy both switch_tplg.py and switch_tplg.sh to the target hardware, ensuring they are in the same directory.

To start the UI, run the following command on the board:
```python3 switch_tplg.py```

After selecting the topology, applying it will remove and reload the relevant kernel modules, allowing the new topology to take effect without requiring a reboot.
144 changes: 144 additions & 0 deletions tools/switch_tplg/switch_tplg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import os
import argparse
import curses
import subprocess

os.chdir(os.path.dirname(os.path.abspath(__file__)))

class TopologySelectorUI:

def __init__(self, tplg_path):
self.selected_tplg_file = None
self.tplg_path = tplg_path
self.tplg_files = self.scan_for_files('', '.tplg', extra_paths=[tplg_path])
self.tplg_files.sort()
if self.tplg_files:
self.selected_tplg_file = self.tplg_files[0]
self.run_ui()

def scan_for_files(self, directory_name: str, file_extension: str, extra_paths: list = None):
found_files = []
dir_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), directory_name)

if os.path.exists(dir_path):
found_files.extend([f for f in os.listdir(dir_path) if f.endswith(file_extension)])
else:
print(f"Error: The '{directory_name}' directory is missing. It should be located in the same folder as this script.")

if extra_paths:
for path in extra_paths:
if os.path.exists(path):
found_files.extend([f for f in os.listdir(path) if f.endswith(file_extension)])
else:
print(f"Warning: The directory '{path}' does not exist.")

return found_files

def run_ui(self):
curses.wrapper(self.main_menu)

def main_menu(self, stdscr):
curses.start_color()
self.initialize_colors()
stdscr.bkgd(' ', curses.color_pair(2))

current_row = 0
max_row = 3

while True:
stdscr.clear()
self.display_menu(stdscr, current_row)
key = stdscr.getch()

if key == curses.KEY_UP:
current_row = (current_row - 1) % (max_row + 1)
elif key == curses.KEY_DOWN:
current_row = (current_row + 1) % (max_row + 1)

actions = {
0: lambda: self.select_tplg_file(stdscr),
1: lambda: self.switch_topology(self.selected_tplg_file),
2: lambda: exit()
}

if key == 10:
if current_row in actions:
actions[current_row]()
elif current_row == 3:
break

def initialize_colors(self):
curses.init_pair(1, curses.COLOR_BLACK, 7)
curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE)

def display_menu(self, stdscr, current_row):
stdscr.addstr(0, 0, "---------------Topology Selector----------------", curses.A_BOLD)
stdscr.addstr(1, 0, "================================================")

menu = [
f"| Select Topology (Currently: {self.selected_tplg_file})",
"| Apply Selected Topology",
"| Quit "
]

for idx, item in enumerate(menu):
color_pair = curses.color_pair(1) if idx == current_row else curses.color_pair(2)
stdscr.addstr(idx + 3, 0, item, color_pair)

stdscr.refresh()

def select_tplg_file(self, stdscr):
self.select_file_from_list(stdscr, self.tplg_files, "-----Select Topology-----")


def select_file_from_list(self, stdscr, file_list, title):
current_row = 0
offset = 0
max_visible_rows = curses.LINES - 2

while True:
stdscr.clear()
stdscr.addstr(0, 0, title, curses.A_BOLD)

visible_rows = min(max_visible_rows, len(file_list))

for idx in range(visible_rows):
file_idx = idx + offset
if file_idx >= len(file_list):
break
file_name = file_list[file_idx]
color_pair = curses.color_pair(1) if file_idx == current_row else curses.color_pair(2)
stdscr.addstr(idx + 1, 0, file_name, color_pair)

stdscr.refresh()
key = stdscr.getch()

if key == curses.KEY_UP:
if current_row > 0:
current_row -= 1
if current_row < offset:
offset -= 1
elif key == curses.KEY_DOWN:
if current_row < len(file_list) - 1:
current_row += 1
if current_row >= offset + visible_rows:
offset += 1
elif key == curses.KEY_ENTER or key in [10, 13]:
self.selected_tplg_file = file_list[current_row]
self.switch_topology(self.selected_tplg_file)
break

def switch_topology(self, tplg_file):
tplg_full_path = os.path.join(self.tplg_path, tplg_file)
subprocess.run(['./switch_tplg.sh', tplg_full_path], check=True)
print(f"Switched to topology: {tplg_full_path}")

def main():
parser = argparse.ArgumentParser(description="Topology Selector")
parser.add_argument('--tplg-path', default='/lib/firmware/imx/sof-tplg', help="Path to search for topology files (.tplg)")
args = parser.parse_args()

TopologySelectorUI(tplg_path=args.tplg_path)

if __name__ == "__main__":
main()
26 changes: 26 additions & 0 deletions tools/switch_tplg/switch_tplg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/sh

selected_tplg=$1
target_tplg="/lib/firmware/imx/sof-tplg/sof-imx8-wm8960.tplg"

cp "$target_tplg" /lib/firmware/imx/sof-tplg/previous-topology.tplg

rmmod snd_sof_imx8
rmmod snd_sof_xtensa_dsp
rmmod snd_sof_of
rmmod imx_common
rmmod snd_sof
rmmod snd_sof_utils
rmmod snd_soc_wm8960
rmmod snd_soc_simple_card

cp "$selected_tplg" "$target_tplg"

modprobe snd_soc_simple_card
modprobe snd_soc_wm8960
modprobe snd_sof_utils
modprobe snd_sof
modprobe imx_common
modprobe snd_sof_of
modprobe snd_sof_xtensa_dsp
modprobe snd_sof_imx8
Loading