From 78915641ae892a8d57fcae7493caf3d10001851c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 6 Jul 2010 12:53:44 +0200 Subject: [PATCH] hda-analyzer: initial version to show interactive HDA codec node graph - it's a preview version - more features will follow Signed-off-by: Jaroslav Kysela --- alsatool | 6 + comments.py | 39 +- config.py | 4 +- drivermerge.py | 5 +- hda-analyzer/hda_analyzer.py | 563 ++----------------------- hda-analyzer/hda_codec.py | 183 ++++++++ hda-analyzer/hda_graph.py | 797 +++++++++++++++++++++++++++++++++++ hda-analyzer/hda_guilib.py | 577 +++++++++++++++++++++++++ hda-analyzer/run.py | 3 +- 9 files changed, 1637 insertions(+), 540 deletions(-) create mode 100755 hda-analyzer/hda_graph.py create mode 100644 hda-analyzer/hda_guilib.py diff --git a/alsatool b/alsatool index 9cdc074..e8c3ef5 100755 --- a/alsatool +++ b/alsatool @@ -602,6 +602,12 @@ def changes(argv): continue if l[:15].lower() == "signed-off-by: ": continue + if l[:10].lower() == "acked-by: ": + continue + if l[:6].lower() == "from: ": + continue + if l[:4].lower() == "cc: ": + continue print(': %s %s' % (first, esc(l))) first = " " print('') diff --git a/comments.py b/comments.py index ecd09af..19b5fd8 100644 --- a/comments.py +++ b/comments.py @@ -253,6 +253,8 @@ COMMENT_MAP = { ['/soc/codecs/Kconfig', 'SoC Layer'], ['/soc/codecs/l3.(c|h)', 'SoC L3 bus'], ['/soc/codecs/ac97.(c|h)', 'SoC Codec AC97'], + ['/include/wm2000.h', 'SoC Codec WM2000'], + ['/soc/codecs/wm2000.(c|h)', 'SoC Codec WM2000'], ['/soc/codecs/wm8350.(c|h)', 'SoC Codec WM8350'], ['/soc/codecs/wm8400.(c|h)', 'SoC Codec WM8400'], ['/soc/codecs/wm8510.(c|h)', 'SoC Codec WM8510'], @@ -268,21 +270,25 @@ COMMENT_MAP = { ['/soc/codecs/wm8776.(c|h)', 'SoC Codec WM8776'], ['/soc/codecs/wm8794.(c|h)', 'SoC Codec WM8794'], ['/soc/codecs/wm8900.(c|h)', 'SoC Codec WM8900'], + ['/include/wm8903.h', 'SoC Codec WM8903'], ['/soc/codecs/wm8903.(c|h)', 'SoC Codec WM8903'], ['/include/wm8904.h', 'SoC Codec WM8904'], ['/soc/codecs/wm8904.(c|h)', 'SoC Codec WM8904'], ['/soc/codecs/wm8940.(c|h)', 'SoC Codec WM8940'], ['/include/wm8955.h', 'SoC Codec WM8955'], ['/soc/codecs/wm8955.(c|h)', 'SoC Codec WM8955'], + ['/include/wm8960.h', 'SoC Codec WM8960'], ['/soc/codecs/wm8960.(c|h)', 'SoC Codec WM8960'], ['/soc/codecs/wm8961.(c|h)', 'SoC Codec WM8961'], ['/soc/codecs/wm8971.(c|h)', 'SoC Codec WM8971'], ['/soc/codecs/wm8974.(c|h)', 'SoC Codec WM8974'], + ['/soc/codecs/wm8978.(c|h)', 'SoC Codec WM8978'], ['/soc/codecs/wm8988.(c|h)', 'SoC Codec WM8988'], ['/soc/codecs/wm8990.(c|h)', 'SoC Codec WM8990'], ['/soc/codecs/wm_hubs.(c|h)', 'SoC Codec WM8993/4'], ['/include/wm8993.h', 'SoC Codec WM8993/4'], ['/soc/codecs/wm8993.(c|h)', 'SoC Codec WM8993/4'], + ['/soc/codecs/wm8994.(c|h)', 'SoC Codec WM8994'], ['/include/wm9081.h', 'SoC Codec WM9081'], ['/soc/codecs/wm9081.(c|h)', 'SoC Codec WM9081'], ['/soc/codecs/wm9705.(c|h)', 'SoC Codec WM9705'], @@ -291,6 +297,7 @@ COMMENT_MAP = { ['/soc/codecs/cs4270.(c|h)', 'SoC Codec CS4270'], ['/soc/codecs/ad1836.(c|h)', 'SoC Codec AD1836'], ['/soc/codecs/ad1938.(c|h)', 'SoC Codec AD1938'], + ['/soc/codecs/ad193x.(c|h)', 'SoC Codec AD193X'], ['/soc/codecs/ad1963.(c|h)', 'SoC Codec AD1963'], ['/soc/codecs/ad1980.(c|h)', 'SoC Codec AD1980'], ['/soc/codecs/tlv320aic23.(c|h)', 'SoC Codec TLV320AIC23'], @@ -307,6 +314,7 @@ COMMENT_MAP = { ['/soc/codecs/ssm2602.(c|h)', 'SoC Codec SSM2602'], ['/soc/codecs/ad73311.(c|h)', 'SoC Codec AD73311'], ['/soc/codecs/twl4030.(c|h)', 'SoC Codec TWL4030'], + ['/soc/codecs/twl6040.(c|h)', 'SoC Codec TWL6040'], ['/soc/codecs/pcm3008.(c|h)', 'SoC Codec PCM3008'], ['/soc/codecs/cx20442.(c|h)', 'SoC Codec CX20442'], ['/soc/codecs/max9877.(c|h)', 'SoC Codec MAX9877'], @@ -314,6 +322,7 @@ COMMENT_MAP = { ['/soc/codecs/spdif_transciever.(c|h)', 'SoC Codec DIT SPDI/F'], ['/soc/codecs/ads117x.(c|h)', 'SoC Codec ads1174/8'], ['/soc/codecs/da7210.(c|h)', 'SoC Codec DA7210'], + ['/soc/codecs/cq93vc.(c|h)', 'SoC Codec CQ0093 Voice'], ['/include/tpa6130a2-plat.h', 'SoC Codec TPA6130A2'], ['/soc/codecs/tpa6130a2.(c|h)', 'SoC Codec TPA6130A2'], ['/soc/codecs', 'ERROR'], @@ -322,6 +331,7 @@ COMMENT_MAP = { ['/soc/at91/.*', 'SoC Audio for the Atmel AT91 System-on-Chip'], ['/soc/imx/.*', 'SoC Audio for Freecale i.MX1x i.MX2x CPUs'], ['/soc/txx9/.*', 'SoC Audio for TXx9'], + ['/soc/pxa/z2.c', 'SoC PXA2xx Aeronix Zipit Z2'], ['/soc/pxa/spitz.c', 'SoC PXA2xx Spitz'], ['/soc/pxa/corgi.c', 'SoC PXA2xx Corgi'], ['/soc/pxa/poodle.c', 'SoC PXA2xx Poodle'], @@ -356,15 +366,30 @@ COMMENT_MAP = { ['/soc', 'ERROR'], ['/usb/usx2y/.*', 'USB USX2Y'], ['/usb/caiaq/.*', 'USB caiaq'], + ['/usb/misc/ua101.(c|h)', 'USB Edirol UA101 driver'], ['/usb/Kconfig', 'USB'], ['/usb/Makefile', 'USB'], - ['/usb/usbaudio.(c|h|inc|patch)', 'USB generic driver'], + ['/usb/card.(c|h|inc|patch)', 'USB generic driver'], + ['/usb/midi.(c|h|inc|patch)', 'USB generic driver'], ['/usb/usbmidi.(c|h|inc|patch)', 'USB generic driver'], - ['/usb/usbmixer.(c|h|patch)', 'USB generic driver'], - ['/usb/usbquirks.(c|h)', 'USB generic driver'], - ['/usb/usbmixer_maps.c', 'USB generic driver'], - ['/usb/usbcompat.h', 'USB generic driver'], - ['/usb/ua101.(c|h)', 'Edirol UA-101 driver'], + ['/usb/mixer.(c|h|inc|patch)', 'USB generic driver'], + ['/usb/usbmixer.(c|h|inc|patch)', 'USB generic driver'], + ['/usb/usbmixer_maps.(c|h|inc|patch)', 'USB generic driver'], + ['/usb/usbaudio.(c|h|inc|patch)', 'USB generic driver'], + ['/usb/mixer_quirks.(c|h|patch)', 'USB generic driver'], + ['/usb/quirks.(c|h|patch)', 'USB generic driver'], + ['/usb/quirks-table.(c|h|patch)', 'USB generic driver'], + ['/usb/usbquirks.(c|h|patch)', 'USB generic driver'], + ['/usb/endpoint.(c|h|patch)', 'USB generic driver'], + ['/usb/helper.(c|h|patch)', 'USB generic driver'], + ['/usb/debug.(c|h|patch)', 'USB generic driver'], + ['/usb/urb.(c|h|patch)', 'USB generic driver'], + ['/usb/pcm.(c|h)', 'USB generic driver'], + ['/usb/format.(c|h)', 'USB generic driver'], + ['/usb/proc.(c|h)', 'USB generic driver'], + ['/usb/mixer_maps.c', 'USB generic driver'], + ['/usb/usbcompat.(c|h)', 'USB generic driver'], + ['/usb/ua101.(c|h)', 'USB Edirol UA101 driver'], ['/usb', 'ERROR'], ['/include/atmel-abdac.h', 'Atmel on-chip Audio Bitstream DAC (ABDAC)'], ['/include/atmel-ac97c.h', 'Atmel on-chip Audio Bitstream DAC (ABDAC)'], @@ -423,6 +448,7 @@ COMMENT_MAP = { ['/include/compat_22.h', 'ALSA Core'], ['/include/linux/pci_ids.h', 'ALSA Core'], ['/include/autoconf.*', 'ALSA Core'], + ['/include/alsa-autoconf.*', 'ALSA Core'], ['/include/platform_device_compat.h', 'ALSA Core'], ['/include/typedefs.h', 'ALSA Core'], ['/include/old/gf1.h', 'OLD GF1 header'], @@ -497,6 +523,7 @@ COMMENT_MAP = { ['/sb16_csp/.*', 'sb16_csp'], ['/sscape_ctl/.*', 'sscape_ctl'], ['/usx2yloader/.*', 'usx2yloader'], + ['/hwmixvolume/.*', 'hwmixvolume'], ['/seq/sbiload/.*', 'sbiload'], ['/seq/cvscompile', 'Core'], ['/seq/hgcompile', 'Core'], diff --git a/config.py b/config.py index 2e5e774..c6a7941 100644 --- a/config.py +++ b/config.py @@ -9,8 +9,8 @@ VERBOSE = False GERRORS = 0 TMPDIR = '/dev/shm/alsatool' SMTP_SERVER = 'localhost' -GIT_KERNEL_MERGE = 'v2.6.32' -GIT_DRIVER_MERGE = 'v1.0.21' +GIT_KERNEL_MERGE = 'v2.6.33' +GIT_DRIVER_MERGE = 'v1.0.22' GIT_MERGE_REPOS = [ ('git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git', 'master', 'linux-2.6', 'http://www.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git'), ('git://git.alsa-project.org/alsa-kernel.git', 'fixes', 'perex-fixes', 'http://git.alsa-project.org/http/alsa-kernel.git'), diff --git a/drivermerge.py b/drivermerge.py index 753ebef..4c4c626 100644 --- a/drivermerge.py +++ b/drivermerge.py @@ -264,7 +264,8 @@ def compare_trees(driver_repo, driver_branch, kernel_repo, kernel_branch): rmtree("alsa-kernel-repo/Documentation/DocBook") mkdir("alsa-kernel-repo/Documentation/DocBook") system("mv alsa-kernel-repo/Documentation/alsa-driver-api.tmpl alsa-kernel-repo/Documentation/DocBook") - for i in ['.git-ok-commits', '.hgignore', '.hgtags', '.gitignore', 'kernel', 'scripts', 'oss']: + for i in ['.git-ok-commits', '.hgignore', '.hgtags', '.gitignore', 'kernel', 'scripts', + 'oss', 'usb/usbmixer.h', 'usb/usbmixer_maps.c']: if isdir("alsa-kmirror-repo/%s" % i): rmtree("alsa-kmirror-repo/%s" % i) elif exists("alsa-kmirror-repo/%s" % i): @@ -290,6 +291,8 @@ def compare_trees(driver_repo, driver_branch, kernel_repo, kernel_branch): 'usb/caiaq/caiaq-device.c', 'usb/caiaq/caiaq-device.h', 'usb/caiaq/caiaq-input.c', 'usb/caiaq/caiaq-input.h', 'usb/caiaq/caiaq-midi.c', 'usb/caiaq/caiaq-midi.h', + 'usb/usbmixer.h', + 'usb/usbmixer_maps.c', 'isa/wavefront/yss225.c' ]: if isdir("alsa-kernel-repo/%s" % i): diff --git a/hda-analyzer/hda_analyzer.py b/hda-analyzer/hda_analyzer.py index 58d1c3e..8dec0eb 100755 --- a/hda-analyzer/hda_analyzer.py +++ b/hda-analyzer/hda_analyzer.py @@ -40,6 +40,8 @@ from hda_codec import HDACodec, HDA_card_list, \ PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \ HDA_INPUT, HDA_OUTPUT from hda_proc import DecodeProcFile, DecodeAlsaInfoFile, HDACodecProc +from hda_guilib import * +from hda_graph import create_graph CODEC_TREE = {} DIFF_TREE = {} @@ -79,6 +81,7 @@ def read_nodes2(card, codec): def read_nodes3(card, codec, proc_file): c = HDACodecProc(card, codec, proc_file) c.analyze() + c.graph(dump=True) if not card in CODEC_TREE: CODEC_TREE[card] = {} DIFF_TREE[card] = {} @@ -154,9 +157,6 @@ def do_diff(): ITALIC_COLUMN ) = range(5) -def get_fixed_font(): - return pango.FontDescription("Misc Fixed,Courier Bold 9") - class HDAAnalyzer(gtk.Window): info_buffer = None node_window = None @@ -196,13 +196,17 @@ class HDAAnalyzer(gtk.Window): button.connect("clicked", self.__diff_clicked) self.tooltips.set_tip(button, "Show settings diff for selected codec.") hbox1.pack_start(button) + button = gtk.Button("Graph") + button.connect("clicked", self.__graph_clicked) + self.tooltips.set_tip(button, "Show graph for selected codec.") + hbox1.pack_start(button) vbox.pack_start(hbox1, False, False) hbox.pack_start(vbox, False, False) self.notebook = gtk.Notebook() hbox.pack_start(self.notebook, expand=True) - self.node_window = self.__create_node() + self.node_window = gtk.Table() self._new_notebook_page(self.node_window, '_Node editor') scrolled_window, self.info_buffer = self.__create_text(self.__dump_visibility) @@ -228,6 +232,13 @@ class HDAAnalyzer(gtk.Window): gtk.main_quit() + def simple_dialog(self, type, msg): + dialog = gtk.MessageDialog(self, + gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + type, gtk.BUTTONS_OK, msg) + dialog.run() + dialog.destroy() + def __about_clicked(self, button): dialog = gtk.Dialog('About', self, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, @@ -270,14 +281,11 @@ mailing list, too. msg = "Setting for codec %s/%s (%s) was reverted!" % (self.codec.card, self.codec.device, self.codec.name) type = gtk.MESSAGE_INFO - dialog = gtk.MessageDialog(self, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - type, gtk.BUTTONS_OK, msg) - dialog.run() - dialog.destroy() + self.simple_dialog(type, msg) def __diff_clicked(self, button): if not self.codec: + self.simple_dialog(gtk.MESSAGE_WARNING, "Please, select a codec in left codec/node tree.") return dialog = gtk.Dialog('Diff', self, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, @@ -300,6 +308,12 @@ mailing list, too. dialog.run() dialog.destroy() + def __graph_clicked(self, button): + if not self.codec: + self.simple_dialog(gtk.MESSAGE_WARNING, "Please, select a codec in left codec/node tree.") + return + create_graph(self.codec) + def __refresh(self): self.load() self.__dump_visibility(None, None) @@ -354,13 +368,13 @@ mailing list, too. for i in CODEC_TREE[self.card]: card = CODEC_TREE[self.card][i].mcard break - self.__build_card(card) + self.node_window.add(NodeGui(card=card)) elif codec: - self.__build_codec(codec) + self.node_window.add(NodeGui(codec=codec)) else: return else: - self.__build_node(n) + self.node_window.add(NodeGui(node=n)) self.node_window.show_all() def _new_notebook_page(self, widget, label): @@ -444,524 +458,6 @@ mailing list, too. return scrolled_window, buffer - def __create_node(self): - scrolled_window = gtk.ScrolledWindow() - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled_window.set_shadow_type(gtk.SHADOW_IN) - return scrolled_window - - def __new_text_view(self, text=None): - text_view = gtk.TextView() - text_view.set_border_width(4) - fontName = get_fixed_font() - text_view.modify_font(fontName) - if not text is None: - buffer = gtk.TextBuffer(None) - iter = buffer.get_iter_at_offset(0) - if text[-1] == '\n': - text = text[:-1] - buffer.insert(iter, text) - text_view.set_buffer(buffer) - text_view.set_editable(False) - text_view.set_cursor_visible(False) - return text_view - - def __build_node_caps(self, node): - frame = gtk.Frame('Node Caps') - frame.set_border_width(4) - if len(node.wcaps_list) == 0: - return frame - str = '' - for i in node.wcaps_list: - str += node.wcap_name(i) + '\n' - frame.add(self.__new_text_view(text=str)) - return frame - - def __node_connection_toggled(self, widget, row, data): - model, node = data - if not model[row][0]: - node.set_active_connection(int(row)) - for r in model: - r[0] = False - idx = 0 - for r in model: - r[0] = node.active_connection == idx - idx += 1 - - def __build_connection_list(self, node): - frame = gtk.Frame('Connection List') - frame.set_border_width(4) - sw = gtk.ScrolledWindow() - #sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) - sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - frame.add(sw) - if node.conn_list and node.connections: - model = gtk.ListStore( - gobject.TYPE_BOOLEAN, - gobject.TYPE_STRING - ) - idx = 0 - for i in node.connections: - iter = model.append() - node1 = self.codec.get_node(node.connections[idx]) - model.set(iter, 0, node.active_connection == idx, - 1, node1.name()) - idx += 1 - treeview = gtk.TreeView(model) - treeview.set_rules_hint(True) - treeview.get_selection().set_mode(gtk.SELECTION_SINGLE) - treeview.set_size_request(300, 30 + len(node.connections) * 25) - renderer = gtk.CellRendererToggle() - renderer.set_radio(True) - if node.active_connection != None: - renderer.connect("toggled", self.__node_connection_toggled, (model, node)) - column = gtk.TreeViewColumn("Active", renderer, active=0) - treeview.append_column(column) - renderer = gtk.CellRendererText() - column = gtk.TreeViewColumn("Destination Node", renderer, text=1, editable=False) - treeview.append_column(column) - sw.add(treeview) - return frame - - def __amp_mute_toggled(self, button, data): - caps, vals, idx = data - val = button.get_active() - vals.set_mute(idx, val) - button.set_active(vals.vals[idx] & 0x80) - - def __amp_value_changed(self, adj, data): - caps, vals, idx = data - val = int(adj.get_value()) - vals.set_value(idx, val) - adj.set_value(vals.vals[idx] & 0x7f) - - def __build_amps(self, node): - - def build_caps(title, caps, vals): - if caps and caps.ofs is None: - caps = caps.dir == HDA_INPUT and \ - node.codec.amp_caps_in or node.codec.amp_caps_out - title += ' (Global)' - frame = gtk.Frame(title) - frame.set_border_width(4) - vbox = gtk.VBox(False, 0) - if caps: - str = 'Offset: %d\n' % caps.ofs - str += 'Number of steps: %d\n' % caps.nsteps - str += 'Step size: %d\n' % caps.stepsize - str += 'Mute: %s\n' % (caps.mute and "True" or "False") - vbox.pack_start(self.__new_text_view(text=str), True, True, 0) - idx = 0 - frame1 = None - vbox1 = None - for val in vals.vals: - if vals.stereo and idx & 1 == 0: - frame1 = gtk.Frame() - vbox.pack_start(frame1, False, False) - vbox1 = gtk.VBox(False, 0) - frame1.add(vbox1) - hbox = gtk.HBox(False, 0) - label = gtk.Label('Val[%d]' % idx) - hbox.pack_start(label, False, False) - if caps.mute: - checkbutton = gtk.CheckButton('Mute') - checkbutton.set_active(val & 0x80 and True or False) - checkbutton.connect("toggled", self.__amp_mute_toggled, (caps, vals, idx)) - hbox.pack_start(checkbutton, False, False) - if caps.stepsize > 0: - adj = gtk.Adjustment((val & 0x7f) % (caps.nsteps+1), 0.0, caps.nsteps+1, 1.0, 1.0, 1.0) - scale = gtk.HScale(adj) - scale.set_digits(0) - scale.set_value_pos(gtk.POS_RIGHT) - adj.connect("value_changed", self.__amp_value_changed, (caps, vals, idx)) - hbox.pack_start(scale, True, True) - if vbox1: - vbox1.pack_start(hbox, False, False) - else: - vbox.pack_start(hbox, False, False) - idx += 1 - frame.add(vbox) - return frame - - hbox = gtk.HBox(False, 0) - c = build_caps('Input Amplifier', - node.in_amp and node.amp_caps_in or None, - node.in_amp and node.amp_vals_in or None) - hbox.pack_start(c) - c = build_caps('Output Amplifier', - node.out_amp and node.amp_caps_out or None, - node.out_amp and node.amp_vals_out or None) - hbox.pack_start(c) - - return hbox - - def __pincap_eapdbtl_toggled(self, button, data): - node, name = data - node.eapdbtl_set_value(name, button.get_active()) - button.set_active(name in node.pincap_eapdbtl) - - def __pinctls_toggled(self, button, data): - node, name = data - node.pin_widget_control_set_value(name, button.get_active()) - button.set_active(name in node.pinctl) - - def __pinctls_vref_change(self, combobox, node): - index = combobox.get_active() - idx1 = 0 - for name in PIN_WIDGET_CONTROL_VREF: - if not name: continue - if idx1 == index: - node.pin_widget_control_set_value('vref', name) - break - idx1 += 1 - idx = idx1 = 0 - for name in PIN_WIDGET_CONTROL_VREF: - if name == node.pinctl_vref: - combobox.set_active(idx1) - break - if name != None: - idx1 += 1 - - def __build_pin(self, node): - hbox = gtk.HBox(False, 0) - - if node.pincap or node.pincap_vref or node.pincap_eapdbtl: - vbox = gtk.VBox(False, 0) - if node.pincap or node.pincap_vref: - frame = gtk.Frame('PIN Caps') - frame.set_border_width(4) - str = '' - for i in node.pincap: - str += node.pincap_name(i) + '\n' - for i in node.pincap_vref: - str += 'VREF_%s\n' % i - frame.add(self.__new_text_view(text=str)) - vbox.pack_start(frame) - if 'EAPD' in node.pincap: - frame = gtk.Frame('EAPD') - frame.set_border_width(4) - vbox1 = gtk.VBox(False, 0) - for name in EAPDBTL_BITS: - checkbutton = gtk.CheckButton(name) - checkbutton.set_active(node.pincap_eapdbtls & (1 << EAPDBTL_BITS[name])) - checkbutton.connect("toggled", self.__pincap_eapdbtl_toggled, (node, name)) - vbox1.pack_start(checkbutton, False, False) - frame.add(vbox1) - vbox.pack_start(frame, False, False) - hbox.pack_start(vbox) - - vbox = gtk.VBox(False, 0) - - frame = gtk.Frame('Config Default') - frame.set_border_width(4) - str = 'Jack connection: %s\n' % node.jack_conn_name - str += 'Jack type: %s\n' % node.jack_type_name - str += 'Jack location: %s\n' % node.jack_location_name - str += 'Jack location2: %s\n' % node.jack_location2_name - str += 'Jack connector: %s\n' % node.jack_connector_name - str += 'Jack color: %s\n' % node.jack_color_name - if 'NO_PRESENCE' in node.defcfg_misc: - str += 'No presence\n' - frame.add(self.__new_text_view(text=str)) - vbox.pack_start(frame) - - frame = gtk.Frame('Widget Control') - frame.set_border_width(4) - vbox1 = gtk.VBox(False, 0) - for name in PIN_WIDGET_CONTROL_BITS: - checkbutton = gtk.CheckButton(name) - checkbutton.set_active(node.pinctls & (1 << PIN_WIDGET_CONTROL_BITS[name])) - checkbutton.connect("toggled", self.__pinctls_toggled, (node, name)) - vbox1.pack_start(checkbutton, False, False) - if node.pincap_vref: - combobox = gtk.combo_box_new_text() - idx = idx1 = active = 0 - for name in PIN_WIDGET_CONTROL_VREF: - if name == node.pinctl_vref: active = idx1 - if name: - combobox.append_text(name) - idx1 += 1 - idx += 1 - combobox.set_active(active) - combobox.connect("changed", self.__pinctls_vref_change, node) - hbox1 = gtk.HBox(False, 0) - label = gtk.Label('VREF') - hbox1.pack_start(label, False, False) - hbox1.pack_start(combobox) - vbox1.pack_start(hbox1, False, False) - frame.add(vbox1) - vbox.pack_start(frame, False, False) - - hbox.pack_start(vbox) - return hbox - - def __build_mix(self, node): - hbox = gtk.HBox(False, 0) - return hbox - - def __sdi_select_changed(self, adj, node): - val = int(adj.get_value()) - node.sdi_select_set_value(val) - adj.set_value(node.sdi_select) - - def __dig1_toggled(self, button, data): - node, name = data - val = button.get_active() - node.dig1_set_value(name, val) - button.set_active(name in node.dig1) - - def __dig1_category_activate(self, entry, node): - val = entry.get_text() - if val.lower().startswith('0x'): - val = int(val[2:], 16) - else: - try: - val = int(val) - except: - print "Unknown category value '%s'" % val - return - node.dig1_set_value('category', val) - entry.set_text("0x%02x" % node.dig1_category) - - def __build_aud(self, node): - vbox = gtk.VBox(False, 0) - - frame = gtk.Frame('Converter') - frame.set_border_width(4) - str = 'Audio Stream:\t%s\n' % node.aud_stream - str += 'Audio Channel:\t%s\n' % node.aud_channel - if node.format_ovrd: - str += 'Rates:\t\t%s\n' % node.pcm_rates[:6] - if len(node.pcm_rates) > 6: - str += '\t\t\t\t%s\n' % node.pcm_rates[6:] - str += 'Bits:\t\t%s\n' % node.pcm_bits - str += 'Streams:\t%s\n' % node.pcm_streams - else: - str += 'Global Rates:\t%s\n' % node.codec.pcm_rates[:6] - if len(node.codec.pcm_rates) > 6: - str += '\t\t%s\n' % node.codec.pcm_rates[6:] - str += 'Global Bits:\t%s\n' % node.codec.pcm_bits - str += 'Global Streams:\t%s\n' % node.codec.pcm_streams - frame.add(self.__new_text_view(text=str)) - vbox.pack_start(frame) - - if not node.sdi_select is None: - hbox1 = gtk.HBox(False, 0) - frame = gtk.Frame('SDI Select') - adj = gtk.Adjustment(node.sdi_select, 0.0, 16.0, 1.0, 1.0, 1.0) - scale = gtk.HScale(adj) - scale.set_digits(0) - scale.set_value_pos(gtk.POS_LEFT) - scale.set_size_request(200, 16) - adj.connect("value_changed", self.__sdi_select_changed, node) - frame.add(scale) - hbox1.pack_start(frame, False, False) - vbox.pack_start(hbox1, False, False) - - if node.digital: - hbox1 = gtk.HBox(False, 0) - frame = gtk.Frame('Digital Converter') - vbox1 = gtk.VBox(False, 0) - for name in DIG1_BITS: - checkbutton = gtk.CheckButton(name) - checkbutton.set_active(node.digi1 & (1 << DIG1_BITS[name])) - checkbutton.connect("toggled", self.__dig1_toggled, (node, name)) - vbox1.pack_start(checkbutton, False, False) - frame.add(vbox1) - hbox1.pack_start(frame) - frame = gtk.Frame('Digital Converter Category') - entry = gtk.Entry() - entry.set_text("0x%x" % node.dig1_category) - entry.set_width_chars(4) - entry.connect("activate", self.__dig1_category_activate, node) - frame.add(entry) - hbox1.pack_start(frame) - vbox.pack_start(hbox1, False, False) - - return vbox - - def __build_device(self, device): - vbox = gtk.VBox(False, 0) - frame = gtk.Frame('Device') - frame.set_border_width(4) - hbox = gtk.HBox(False, 0) - s = 'name=' + str(device.name) + ', type=' + \ - str(device.type) + ', device=' + str(device.device) - label = gtk.Label(s) - hbox.pack_start(label, False, False) - frame.add(hbox) - vbox.pack_start(frame) - return vbox - - def __build_controls(self, ctrls): - vbox = gtk.VBox(False, 0) - frame = gtk.Frame('Controls') - frame.set_border_width(4) - vbox1 = gtk.VBox(False, 0) - for ctrl in ctrls: - hbox1 = gtk.HBox(False, 0) - vbox1.pack_start(hbox1, False, False) - s = 'name=' + str(ctrl.name) + ', index=' + str(ctrl.index) + \ - ', device=' + str(ctrl.device) - label = gtk.Label(s) - hbox1.pack_start(label, False, False) - if ctrl.amp_chs: - hbox1 = gtk.HBox(False, 0) - vbox1.pack_start(hbox1, False, False) - s = ' chs=' + str(ctrl.amp_chs) + ', dir=' + str(ctrl.amp_dir) + \ - ', idx=' + str(ctrl.amp_idx) + ', ofs=' + str(ctrl.amp_ofs) - label = gtk.Label(s) - hbox1.pack_start(label, False, False) - frame.add(vbox1) - vbox.pack_start(frame) - return vbox - - def __build_proc(self, node): - frame = gtk.Frame('Processing Caps') - frame.set_border_width(4) - str = 'benign=%i\nnumcoef=%i\n' % (node.proc_benign, node.proc_numcoef) - frame.add(self.__new_text_view(text=str)) - return frame - - def __build_node(self, node): - w = self.node_window - - mframe = gtk.Frame(node.name()) - mframe.set_border_width(4) - - vbox = gtk.VBox(False, 0) - dev = node.get_device() - if not dev is None: - vbox.pack_start(self.__build_device(dev), False, False) - ctrls = node.get_controls() - if ctrls: - vbox.pack_start(self.__build_controls(ctrls), False, False) - hbox = gtk.HBox(False, 0) - hbox.pack_start(self.__build_node_caps(node)) - hbox.pack_start(self.__build_connection_list(node)) - vbox.pack_start(hbox, False, False) - if node.in_amp or node.out_amp: - vbox.pack_start(self.__build_amps(node), False, False) - if node.wtype_id == 'PIN': - vbox.pack_start(self.__build_pin(node), False, False) - elif node.wtype_id in ['AUD_IN', 'AUD_OUT']: - vbox.pack_start(self.__build_aud(node), False, False) - else: - if not node.wtype_id in ['AUD_MIX', 'BEEP', 'AUD_SEL']: - print 'Node type %s has no GUI support' % node.wtype_id - if node.proc_wid: - vbox.pack_start(self.__build_proc(node), False, False) - - mframe.add(vbox) - w.add_with_viewport(mframe) - - def __build_codec_info(self, codec): - vbox = gtk.VBox(False, 0) - - frame = gtk.Frame('Codec Identification') - frame.set_border_width(4) - str = 'Audio Fcn Group: %s\n' % (codec.afg and "0x%02x" % codec.afg or "N/A") - str += 'Modem Fcn Group: %s\n' % (codec.mfg and "0x%02x" % codec.mfg or "N/A") - str += 'Vendor ID:\t 0x%08x\n' % codec.vendor_id - str += 'Subsystem ID:\t 0x%08x\n' % codec.subsystem_id - str += 'Revision ID:\t 0x%08x\n' % codec.revision_id - frame.add(self.__new_text_view(text=str)) - vbox.pack_start(frame, False, False) - - frame = gtk.Frame('PCM Global Capabilities') - frame.set_border_width(4) - str = 'Rates:\t\t %s\n' % codec.pcm_rates[:6] - if len(codec.pcm_rates) > 6: - str += '\t\t %s\n' % codec.pcm_rates[6:] - str += 'Bits:\t\t %s\n' % codec.pcm_bits - str += 'Streams:\t %s\n' % codec.pcm_streams - frame.add(self.__new_text_view(text=str)) - vbox.pack_start(frame, False, False) - - return vbox - - def __build_codec_amps(self, codec): - - def build_caps(title, caps): - frame = gtk.Frame(title) - frame.set_border_width(4) - if caps and caps.ofs != None: - str = 'Offset:\t\t %d\n' % caps.ofs - str += 'Number of steps: %d\n' % caps.nsteps - str += 'Step size:\t %d\n' % caps.stepsize - str += 'Mute:\t\t %s\n' % (caps.mute and "True" or "False") - frame.add(self.__new_text_view(text=str)) - return frame - - hbox = gtk.HBox(False, 0) - c = build_caps('Global Input Amplifier Caps', codec.amp_caps_in) - hbox.pack_start(c) - c = build_caps('Global Output Amplifier Caps', codec.amp_caps_out) - hbox.pack_start(c) - - return hbox - - def __gpio_toggled(self, button, (codec, id, idx)): - codec.gpio.set(id, idx, button.get_active()) - button.set_active(codec.gpio.test(id, idx)) - - def __build_codec_gpio(self, codec): - frame = gtk.Frame('GPIO') - frame.set_border_width(4) - hbox = gtk.HBox(False, 0) - str = 'IO Count: %d\n' % codec.gpio_max - str += 'O Count: %d\n' % codec.gpio_o - str += 'I Count: %d\n' % codec.gpio_i - str += 'Unsolicited: %s\n' % (codec.gpio_unsol and "True" or "False") - str += 'Wake: %s\n' % (codec.gpio_wake and "True" or "False") - hbox.pack_start(self.__new_text_view(text=str), False, False) - frame.add(hbox) - for id in GPIO_IDS: - id1 = id == 'direction' and 'out-dir' or id - frame1 = gtk.Frame(id1) - frame1.set_border_width(4) - vbox1 = gtk.VBox(False, 0) - for i in range(codec.gpio_max): - checkbutton = gtk.CheckButton('[%d]' % i) - checkbutton.set_active(codec.gpio.test(id, i)) - checkbutton.connect("toggled", self.__gpio_toggled, (codec, id, i)) - vbox1.pack_start(checkbutton, False, False) - frame1.add(vbox1) - hbox.pack_start(frame1, False, False) - return frame - - def __build_codec(self, codec): - w = self.node_window - - mframe = gtk.Frame(codec.name) - mframe.set_border_width(4) - - vbox = gtk.VBox(False, 0) - vbox.pack_start(self.__build_codec_info(codec), False, False) - vbox.pack_start(self.__build_codec_amps(codec), False, False) - vbox.pack_start(self.__build_codec_gpio(codec), False, False) - mframe.add(vbox) - w.add_with_viewport(mframe) - - def __build_card_info(self, card): - str = 'Card: %s\n' % card.card - str += 'Id: %s\n' % card.id - str += 'Driver: %s\n' % card.driver - str += 'Name: %s\n' % card.name - str += 'LongName: %s\n' % card.longname - return self.__new_text_view(text=str) - - def __build_card(self, card): - w = self.node_window - - mframe = gtk.Frame(card.name) - mframe.set_border_width(4) - - vbox = gtk.VBox(False, 0) - vbox.pack_start(self.__build_card_info(card), False, False) - mframe.add(vbox) - w.add_with_viewport(mframe) - def monitor(): from time import sleep print "Watching %s cards" % len(CODEC_TREE) @@ -999,6 +495,9 @@ def main(argv): if len(argv) > 1 and argv[1] in ('-m', '-monitor', '--monitor'): cmd = 'monitor' del argv[1] + if len(argv) > 1 and argv[1] in ('-g', '-graph', '--graph'): + cmd = 'graph' + del argv[1] if read_nodes(sys.argv[1:]) == 0: print "No HDA codecs were found or insufficient priviledges for " print "/dev/snd/controlC* and /dev/snd/hwdepC*D* device files." @@ -1012,6 +511,10 @@ def main(argv): if cmd == 'monitor': monitor() return 1 + if cmd == 'graph': + for card in CODEC_TREE: + for codec in CODEC_TREE: + create_graph(CODEC_TREE[card][codec]) HDAAnalyzer() gtk.main() return 1 diff --git a/hda-analyzer/hda_codec.py b/hda-analyzer/hda_codec.py index 2e7b25b..bdf008d 100644 --- a/hda-analyzer/hda_codec.py +++ b/hda-analyzer/hda_codec.py @@ -1219,6 +1219,189 @@ class HDACodec: return self.proc_codec.get_controls(nid) return None + def connections(self, nid, dir=0): + if dir == 0: + if nid in self.nodes: + conn = self.nodes[nid].connections + if conn: + return len(conn) + return 0 + res = 0 + for nid in self.nodes: + node = self.nodes[nid] + if nid != nid and node.connections and nid in node.connections: + res += 1 + return res + + def graph(self, dump=False): + + def mfind(nid): + for y in range(len(res)): + if nid in res[y]: + return (y, res[y].index(nid)) + return None, None + + def doplace(nid, y, x): + node = self.nodes[nid] + if node.wtype_id == 'AUD_MIX': + if y == 0: + y += 1 + while 1: + x += 1 + if x >= len(res[0]) - 1: + x = 1 + y += 1 + if y >= len(res) - 1: + raise ValueError, "cannot place nid to graph matrix" + if res[y][x+1] is None and \ + res[y][x-1] is None and \ + res[y+1][x] is None and \ + res[y-1][x] is None and \ + res[y][x] is None: + res[y][x] = nid + return True + if y == 0: + for idx in range(len(res)-2): + if res[idx+1][x] is None: + res[idx+1][x] = nid + return True + elif y == len(res)-1: + for idx in reversed(range(len(res)-2)): + if res[idx+1][x] is None: + res[idx+1][x] = nid + return True + elif x == 0: + for idx in range(len(res[0])-2): + if res[y][idx+1] is None: + res[y][idx+1] = nid + return True + elif x == len(res)-1: + for idx in range(len(res[0])-2): + if res[y][idx+1] is None: + res[y][idx+1] = nid + return True + else: + if y+1 < len(res) and res[y+1][x] is None: + res[y+1][x] = nid + return True + if y-1 != 0 and res[y-1][x] is None: + res[y-1][x] = nid + return True + if x+1 < len(res[0]) and res[y][x+1] is None: + res[y][x+1] = nid + return True + if x-1 != 0 and res[y][x-1] is None: + res[y][x-1] = nid + return True + if y+1 < len(res): + return doplace(nid, y+1, 1) + if x+1 < len(res[0]): + return doplace(nid, 1, x+1) + raise ValueError, "cannot place nid to graph matrix" + return False + + res = [] + unplaced = [] + # determine all termination widgets + terms = {'AUD_IN':[], 'AUD_OUT':[], 'PIN_IN':[], 'PIN_OUT':[]} + for nid in self.nodes: + node = self.nodes[nid] + if node.wtype_id in ['AUD_IN', 'AUD_OUT', 'PIN']: + id = node.wtype_id + if id == 'PIN': + id = 'IN' in node.pinctl and 'PIN_IN' or 'PIN_OUT' + terms[id].append(nid) + else: + unplaced.append(nid) + for id in terms: + terms[id].sort() + # build the matrix + x = max(len(terms['AUD_IN']), len(terms['AUD_OUT'])) + 2 + y = max(len(terms['PIN_IN']), len(terms['PIN_OUT'])) + 2 + if x == 2 and y == 2: + return None + for a in range(y): + res.append([None]*x) + if 'PIN_IN' in terms: + for idx in range(len(terms['PIN_IN'])): + res[idx+1][0] = terms['PIN_IN'][idx] + if 'PIN_OUT' in terms: + for idx in range(len(terms['PIN_OUT'])): + res[idx+1][-1] = terms['PIN_OUT'][idx] + if 'AUD_IN' in terms: + idx = 1 + for nid in terms['AUD_IN']: + res[0][idx] = nid + idx += 1 + if 'AUD_OUT' in terms: + idx = 1 + for nid in terms['AUD_OUT']: + res[-1][idx] = nid + idx += 1 + # check and resize the matrix for unplaced nodes + while len(res)**len(res[0]) < len(unplaced): + res.insert(-2, [None]*x) + # assign unplaced nids - check connections + unplaced.sort() + while unplaced: + change = len(unplaced) + for idx in range(len(res)): + for idx1 in range(len(res[idx])): + nid = res[idx][idx1] + if nid is None: + continue + node = self.nodes[nid] + if not node or not node.connections: + continue + for conn in node.connections: + if conn in unplaced and doplace(conn, idx, idx1): + unplaced.remove(conn) + for nid in unplaced: + node = self.nodes[nid] + if not node.connections: + continue + for conn in node.connections: + placed = False + y, x = mfind(nid) + if doplace(nid, y, x): + unplaced.remove(nid) + break + if len(unplaced) == change: + break + y = len(res) + x = 0 + for nid in unplaced: + if y >= len(res): + res.append([None]*len(res[0])) + res[y][x] = nid + x += 1 + if x >= len(res[0]): + y += 1 + x = 0 + # do extra check + check = [] + for y in range(len(res)): + for x in range(len(res[0])): + nid = res[y][x] + if not nid is None: + if nid in check: + raise ValueError, "double nid in graph matrix" + if not nid in self.nodes: + raise ValueError, "unknown nid in graph matrix" + check.append(nid) + if len(check) != len(self.nodes): + raise ValueError, "not all nids in graph matrix" + # do addition dump + if dump: + print "****", len(self.nodes) + for nodes in res: + str = '' + for node2 in nodes: + str += node2 is None and '-- ' or '%02x ' % node2 + print str + print "****" + return res + def HDA_card_list(): from dircache import listdir result = [] diff --git a/hda-analyzer/hda_graph.py b/hda-analyzer/hda_graph.py new file mode 100755 index 0000000..1ac6987 --- /dev/null +++ b/hda-analyzer/hda_graph.py @@ -0,0 +1,797 @@ +#!/usr/bin/env python +# +# Copyright (c) 2008-2010 by Jaroslav Kysela +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import gobject +import gtk +from gtk import gdk +import pango +import cairo +from hda_guilib import * + +from hda_codec import EAPDBTL_BITS, PIN_WIDGET_CONTROL_BITS, \ + PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \ + HDA_INPUT, HDA_OUTPUT + +GRAPH_WINDOWS = {} + +class Node: + + def __init__(self, codec, node, x, y, nodesize, extra): + self.codec = codec + self.node = node + self.extra = extra + sx = sy = nodesize + self.myarea = [extra+x*(sx+extra), extra+y*(sy+extra), sx, sy] + self.src_routes = [] + self.dst_routes = [] + self.win = None + + def expose(self, cr, event, graph): + + width = self.myarea[2] + height = self.myarea[3] + + cr.select_font_face("Misc Fixed", + cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) + cr.set_font_size(14) + if graph.startnode == self: + cr.set_line_width(1.8) + cr.set_source_rgb(0, 0, 1) + elif graph.endnode == self: + cr.set_line_width(1.8) + cr.set_source_rgb(1, 0, 0) + else: + cr.set_line_width(0.8) + cr.set_source_rgb(0, 0, 0) + cr.rectangle(*self.myarea) + cr.stroke() + + cr.set_line_width(0.4) + cr.move_to(self.myarea[0]+5, self.myarea[1]+13) + cr.text_path("0x%02x: %s" % (self.node.nid, self.node.wtype_id)) + cr.stroke() + + cr.set_line_width(0.2) + cr.rectangle(self.myarea[0], self.myarea[1] + (height/4)*3, width/2, height/4) + cr.rectangle(self.myarea[0]+width/2, self.myarea[1] + (height/4)*3, width/2, height/4) + cr.stroke() + + cr.set_font_size(11) + cr.move_to(self.myarea[0]+20, self.myarea[1] + (height/4)*3+15) + cr.text_path('IN') + cr.stroke() + cr.move_to(self.myarea[0]+width/2+20, self.myarea[1] + (height/4)*3+15) + cr.text_path('OUT') + cr.stroke() + + def has_x(self, x): + x1 = self.myarea[0] + x2 = x1 + self.myarea[2] + return x >= x1 and x <= x2 + + def compressx(self, first, size): + if self.myarea[0] > first: + self.myarea[0] -= size + + def has_y(self, y): + y1 = self.myarea[1] + y2 = y1 + self.myarea[3] + return y >= y1 and y <= y2 + + def compressy(self, first, size): + if self.myarea[1] > first: + self.myarea[1] -= size + + def in_area(self, x, y): + if x >= self.myarea[0] and \ + y >= self.myarea[1] and \ + x < self.myarea[0] + self.myarea[2] and \ + y < self.myarea[1] + self.myarea[3]: + wherex = x - self.myarea[0] + wherey = y - self.myarea[1] + if wherey >= (self.myarea[3]/4) * 3: + if wherex >= self.myarea[2]/2: + return "dst" + else: + return "src" + else: + return "body" + + def mouse_move(self, x, y, graph): + what = self.in_area(x, y) + if not what is None: + if what == "dst": + for r in self.dst_routes: + r.highlight = True + elif what == "src": + for r in self.src_routes: + r.highlight = True + else: + graph.popup = self.codec.dump_node(self.node) + return True + +class Route: + + def __init__(self, codec, src_node, dst_node, routes, nodes): + self.codec = codec + self.src = src_node + self.dst = dst_node + self.lines = [] + self.analyze_routes(routes, nodes) + src_node.dst_routes.append(self) + dst_node.src_routes.append(self) + self.highlight = False + self.marked = False + + def shortdest(self): + return "0x%02x->0x%02x" % (self.src.node.nid, self.dst.node.nid) + + def longdesc(self): + src = self.src.node + dst = self.dst.node + return "%s 0x%02x -> %s 0x%02x" % (src.wtype_id.replace('_', '-'), + src.nid, dst.wtype_id.replace('_', '-'), dst.nid) + + def expose(self, cr, event): + width = self.src.myarea[2] + height = self.src.myarea[3] + + if 0: # direct green lines + cr.set_line_width(0.3) + cr.set_source_rgb(0.2, 1.0, 0.2) + cr.move_to(self.src.myarea[0]+(width/4)*3, self.src.myarea[1]+height) + cr.line_to(self.dst.myarea[0]+width/4, self.dst.myarea[1]+height) + cr.stroke() + + for line in self.lines: + if self.marked: + cr.set_line_width(1.8) + cr.set_source_rgb(1, 0, 1) + elif self.highlight: + cr.set_line_width(1.5) + cr.set_source_rgb(1, 0, 0) + else: + cr.set_line_width(0.5) + cr.set_source_rgb(0, 0, 0) + if len(line) > 4: + cr.set_source_rgb(0, 1, 0) + cr.move_to(line[0], line[1]) + cr.line_to(line[2], line[3]) + cr.stroke() + + def select_line(self, routes, nodes, possible): + + def check_dot(posx, posy, line): + if posx == line[0] and posx == line[2]: + if line[1] < line[3]: + if posy >= line[1] and posy <= line[3]: + #print "Clash1", posx, posy, line + return True + else: + if posy >= line[3] and posy <= line[1]: + #print "Clash2", posx, posy, line + return True + if posy == line[1] and posy == line[3]: + if line[0] < line[2]: + if posx >= line[0] and posx <= line[2]: + #print "Clash3", posx, posy, line + return True + else: + if posx >= line[2] and posx <= line[0]: + #print "Clash4", posx, posy, line + return True + if posx == line[0] and posy == line[1]: + #print "Clash5", posx, posy, line + return True + if posx == line[2] and posy == line[3]: + #print "Clash6", posx, posy, line + return True + return False + + for p in possible: + found = False + for route in routes: + if found: + break + for line in route.lines: + if check_dot(line[0], line[1], p) or \ + check_dot(line[2], line[3], p) or \ + check_dot(p[0], p[1], line) or \ + check_dot(p[2], p[3], line): + #print "Found1", p + found = True + break + if nodes and not found: + x1, y1, x2, y2 = p + if x1 > x2 or y1 > y2: + x2, y2, x1, y1 = p + for node in nodes: + xx1, yy1, xx2, yy2 = node.myarea + xx2 += xx1 + yy2 += yy1 + if x1 < xx2 and x2 >= xx1 and y1 < yy2 and y2 >= yy1: + #print "Found2", x1, y1, x2, y2, xx1, yy1, xx2, yy2 + found = True + break + if not found: + #print "OK x1=%s,y1=%s,x2=%s,y2=%s" % (p[0], p[1], p[2], p[3]) + return p + + def analyze_routes(self, routes, nodes): + posx, posy, width, height = self.src.myarea + dposx, dposy, dwidth, dheight = self.dst.myarea + extra = self.src.extra + + possible = [] + startx = posx >= dposx and posx - extra or posx + width + xrange = range(5, extra-1, 5) + if posx >= dposx: + xrange.reverse() + a = range(width+extra+5, width+extra*2-1, 5) + a.reverse() + xrange = xrange + a + for i in range(2, 10): + a = range(width*i+extra*i+5, width*i+extra*(i+1)-1, 5) + a.reverse() + xrange = xrange + a + else: + xrange += range(width+extra+5, width+extra*2-1, 5) + for i in range(2, 10): + xrange += range(width*i+extra*i+5, width*i+extra*(i+1)-1, 5) + for j in xrange: + possible.append([startx + j, posy + height + 5, + startx + j, dposy + height + 5]) + sel = self.select_line(routes, None, possible) + if not sel: + raise ValueError, "unable to route" + + self.lines.append(sel) + + def finish(self, routes, nodes): + + if not self.lines: + return + + posx, posy, width, height = self.src.myarea + dposx, dposy, dwidth, dheight = self.dst.myarea + extra = self.src.extra + sel = self.lines[0] + res = True + + x = posx+(width/2) + y = posy+height + for tryit in range(3): + possible = [] + fixup = sel[0] > posx and -1 or 1 + r = range(tryit*extra, (tryit+1)*extra-5-1, 5) + if tryit == 2: + r = range(-height-extra+5, -height-5, 5) + r.reverse() + for i in range(tryit*extra, (tryit+1)*extra-5-1, 5): + possible.append([x+5+fixup, sel[1]+i, sel[0]-fixup, sel[1]+i]) + sel1 = self.select_line(routes, nodes, possible) + if sel1: + sel1[0] -= fixup + sel1[2] += fixup + possible = [] + for j in range(0, width/2-10, 5): + possible.append([sel1[0]+j, y, sel1[0]+j, sel1[1]]) + sel2 = self.select_line(routes, nodes, possible) + if sel2: + sel1[0] = sel2[0] + self.lines[0][1] = sel1[1] + self.lines.append(sel1) + self.lines.append(sel2) + tryit = -1 + break + if tryit >= 0: + self.lines.append([x+5, y, sel[0], sel[1], 1]) + print "[1] displaced route 0x%x->0x%x %s %s" % (self.src.node.nid, self.dst.node.nid, repr(self.lines[-1]), repr(sel)) + res = False + + x = dposx + y = dposy+height + for tryit in range(3): + possible = [] + fixup = sel[2] > posx and -1 or 1 + r = range(tryit * extra, (tryit+1)*extra-5-1, 5) + if tryit == 2: + r = range(-height-extra+5, -height-5, 5) + r.reverse() + for i in r: + possible.append([x+(width/2-1)+fixup, sel[3]+i, sel[2]-fixup, sel[3]+i]) + sel1 = self.select_line(routes, nodes, possible) + if sel1: + sel1[0] -= fixup + (width/2-1) - 5 + sel1[2] += fixup + possible = [] + for j in range(0, width/2-10, 5): + possible.append([sel1[0]+j, y, sel1[0]+j, sel1[1]]) + sel2 = self.select_line(routes, nodes, possible) + if sel2: + sel1[0] = sel2[0] + self.lines[0][3] = sel1[3] + self.lines.append(sel1) + self.lines.append(sel2) + tryit = -1 + break + if tryit >= 0: + self.lines.append([x+5, y, sel[2], sel[3], 1]) + print "[2] displaced route 0x%x->0x%x %s %s" % (self.src.node.nid, self.dst.node.nid, repr(self.lines[-1]), repr(sel)) + res = False + + return res + + def has_x(self, x): + for line in self.lines: + if line[0] == x or line[2] == x: + return True + return False + + def compressx(self, first, size): + idx = 0 + while idx < len(self.lines): + line = self.lines[idx] + if line[0] > first: + line[0] -= size + self.lines[idx] = line + if line[2] > first: + line[2] -= size + self.lines[idx] = line + idx += 1 + + def has_y(self, y): + for line in self.lines: + if line[1] == y or line[3] == y: + return True + return False + + def compressy(self, first, size): + idx = 0 + while idx < len(self.lines): + line = self.lines[idx] + if line[1] > first: + line[1] -= size + self.lines[idx] = line + if line[3] > first: + line[3] -= size + self.lines[idx] = line + idx += 1 + + def in_area(self, x, y): + for line in self.lines: + x1, y1, x2, y2 = line + if x1 > x2 or y1 > y2: + x2, y2, x1, y1 = line + if x1 == x2 and abs(x1 - x) < 2: + if y1 <= y and y2 >= y: + return True + elif y1 == y2 and abs(y1 - y) < 2: + if x1 <= x and x2 >= x: + return True + + def mouse_move(self, x, y, graph): + if self.in_area(x, y): + self.highlight = True + return True + +class CodecGraphLayout(gtk.Layout): + + def __init__(self, adj1, adj2, codec, mytitle): + gtk.Layout.__init__(self, adj1, adj2) + self.set_events(0) + self.add_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.POINTER_MOTION_MASK | + gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK) + self.expose_handler = self.connect("expose-event", self.expose) + self.click_handler = self.connect("button-press-event", self.button_click) + self.release_handler = self.connect("button-release-event", self.button_release) + self.motion_handler = self.connect("motion-notify-event", self.mouse_move) + + self.popup_win = None + self.popup = None + + self.codec = codec + self.mytitle = mytitle + self.graph = codec.graph(dump=False) + self.startnode = None + self.endnode = None + ok = False + for extra in [150, 200, 300]: + if self.build(extra): + ok = True + break + break + if not ok: + print "Not all routes are placed correctly!!!" + + def __destroy(self, widget): + if self.popup_win: + self.popup_win.destroy() + + def build(self, extra=50): + self.nodes = [] + self.routes = [] + maxconns = 0 + for nid in self.codec.nodes: + node = self.codec.nodes[nid] + conns = max(self.codec.connections(nid, 0), + self.codec.connections(nid, 1)) + if conns > maxconns: + maxconns = conns + nodesize = max((maxconns * 5 + 10) * 2, 100) + if self.graph: + for y in range(len(self.graph)): + for x in range(len(self.graph[0])): + nid = self.graph[y][x] + if not nid is None: + node = self.codec.nodes[nid] + w = Node(self.codec, node, x, y, nodesize, extra) + self.nodes.append(w) + sx = len(self.graph[0])*(nodesize+extra)+extra + sy = len(self.graph)*(nodesize+extra)+extra + self.set_size(sx, sy) + for node in self.nodes: + if not node.node.connections: + continue + for conn in node.node.connections: + for node1 in self.nodes: + if node1.node.nid == conn: + r = Route(self.codec, node1, node, self.routes, self.nodes) + self.routes.append(r) + break + res = True + for route in self.routes: + if not route.finish(self.routes, self.nodes): + res = False + break + # final step - optimize drawings + while 1: + size = self.compressx(sx) + if not size: + break + sx -= size + while 1: + size = self.compressy(sy) + if not size: + break + sy -= size + self.set_size(sx, sy) + return res + + def expose(self, widget, event): + if not self.flags() & gtk.REALIZED: + return + + # background + cr = self.bin_window.cairo_create() + cr.set_source_rgb(1.0, 1.0, 1.0) + cr.rectangle(event.area.x, event.area.y, + event.area.width, event.area.height) + cr.clip() + cr.paint() + + # draw nodes + for node in self.nodes: + node.expose(cr, event, self) + + # draw routes + for route in self.routes: + route.expose(cr, event) + + def compressx(self, sx): + first = None + for a in range(15, sx, 5): + found = False + for node in self.nodes: + if node.has_x(a): + found = True + break + if not found: + for route in self.routes: + if route.has_x(a): + found = True + break + if not found: + if first is None: + first = a + last = a + elif first is not None: + size = (last - first) + 5 + for node in self.nodes: + node.compressx(first, size) + for route in self.routes: + route.compressx(first, size) + return size + return None + + def compressy(self, sy): + first = None + for a in range(15, sy, 5): + found = False + for node in self.nodes: + if node.has_y(a): + found = True + break + if not found: + for route in self.routes: + if route.has_y(a): + found = True + break + if not found: + if first is None: + first = a + last = a + elif first is not None: + size = (last - first) + 5 + for node in self.nodes: + node.compressy(first, size) + for route in self.routes: + route.compressy(first, size) + return size + return None + + def find_node(self, event): + for node in self.nodes: + what = node.in_area(event.x, event.y) + if not what is None: + return (node, what) + return (None, None) + + def find_route(self, event): + for route in self.routes: + if route.in_area(event.x, event.y): + return route + + def show_popup(self, event): + screen_width = gtk.gdk.screen_width() + screeen_height = gtk.gdk.screen_height() + + if self.popup_win: + self.popup_win.destroy() + self.popup_win = gtk.Window(gtk.WINDOW_POPUP) + label = gtk.Label() + label.modify_font(get_fixed_font()) + label.set_text(self.popup) + self.popup_win.add(label) + popup_width, popup_height = self.popup_win.get_size() + + rootwin = self.get_screen().get_root_window() + x, y, mods = rootwin.get_pointer() + + pos_x = x - popup_width/2 + if pos_x < 0: + pos_x = 0 + elif pos_x + popup_width > screen_width: + pos_x = screen_width - popup_width + + pos_y = y + 3 + if pos_y + popup_height > screeen_height: + pos_y = event.y - 3 - popup_height + + self.popup_win.move(int(pos_x), int(pos_y)) + self.popup_win.show_all() + + def mouse_move(self, widget, event): + oldpopup = self.popup + self.popup = None + for route in self.routes: + route.highlight = False + found = False + for node in self.nodes: + if node.mouse_move(event.x, event.y, self): + self.queue_draw() + found = True + break + if not found: + for route in self.routes: + if route.mouse_move(event.x, event.y, self): + self.queue_draw() + found = True + break + if self.popup: + if oldpopup != self.popup: + self.show_popup(event) + else: + if self.popup_win: + self.popup_win.destroy() + + def mark_it(self, widget, node, what, enable): + if what == "start": + if enable: + if not self.startnode: + self.startnode = node + self.queue_draw() + else: + if self.startnode: + self.startnode = None + self.queue_draw() + elif what == "end": + if enable: + if not self.endnode: + self.endnode = node + self.queue_draw() + else: + if self.endnode: + self.endnode = None + self.queue_draw() + + def mark_route(self, widget, route, what, enable): + if what == "mark": + if enable: + if not route.marked: + route.marked = enable + self.queue_draw() + else: + if route.marked: + route.marked = False + self.queue_draw() + + def node_win_destroy(self, widget, node): + node.win = None + + def open_node(self, widget, node): + if self.popup_win: + self.popup_win.destroy() + if not node.win: + win = gtk.Window() + win.set_default_size(500, 600) + gui = NodeGui(node=node.node) + win.set_title(self.mytitle + ' ' + gui.mytitle) + win.add(gui) + win.connect("destroy", self.node_win_destroy, node) + win.show_all() + node.win = win + else: + node.win.present() + + def button_click(self, widget, event): + if event.button != 3: + return False + node, what = self.find_node(event) + m = None + if node: + m = gtk.Menu() + i = gtk.MenuItem("Open") + i.connect("activate", self.open_node, node) + i.show() + m.append(i) + if what in ["src", "dst"]: + routes1 = node.src_routes + text = "Mark Route From" + if what == "dst": + routes1 = node.dst_routes + text = "Mark Route To" + routes = [] + for route in routes1: + if not route.marked: + routes.append(route) + if routes: + i = None + if len(routes) == 1: + i = gtk.MenuItem(text + ' ' + routes[0].longdesc()) + i.connect("activate", self.mark_route, routes[0], "mark", True) + else: + menu = gtk.Menu() + for route in routes: + i = gtk.MenuItem(route.longdesc()) + i.connect("activate", self.mark_route, route, "mark", True) + i.show() + menu.append(i) + i = gtk.MenuItem(text) + i.set_submenu(menu) + if i: + i.show() + m.append(i) + if what in ["src", "dst"]: + routes1 = node.src_routes + text = "Unmark Route From" + if what == "dst": + routes1 = node.dst_routes + text = "Unmark Route To" + routes = [] + for route in routes1: + if route.marked: + routes.append(route) + if routes: + i = None + if len(routes) == 1: + i = gtk.MenuItem(text + ' ' + routes[0].longdesc()) + i.connect("activate", self.mark_route, routes[0], "mark", False) + else: + menu = gtk.Menu() + for route in routes: + i = gtk.MenuItem(route.longdesc()) + i.connect("activate", self.mark_route, route, "mark", False) + i.show() + menu.append(i) + i = gtk.MenuItem(text) + i.set_submenu(menu) + if i: + i.show() + m.append(i) + if not self.startnode: + i = gtk.MenuItem("Mark as start point") + i.connect("activate", self.mark_it, node, "start", True) + else: + i = gtk.MenuItem("Clear start point") + i.connect("activate", self.mark_it, None, "start", False) + i.show() + m.append(i) + if not self.endnode: + i = gtk.MenuItem("Mark as finish point") + i.connect("activate", self.mark_it, node, "end", True) + else: + i = gtk.MenuItem("Clear finish point") + i.connect("activate", self.mark_it, None, "end", False) + i.show() + m.append(i) + else: + route = self.find_route(event) + if route: + m = gtk.Menu() + if not route.marked: + i = gtk.MenuItem("Mark selected route %s" % route.shortdesc()) + i.connect("activate", self.mark_route, route, "mark", True) + else: + i = gtk.MenuItem("Clear selected route %s" % route.shortdesc()) + i.connect("activate", self.mark_route, route, "mark", False) + i.show() + m.append(i) + if m: + m.popup(None, None, None, event.button, event.time, None) + return False + + def button_release(self, widget, event): + + pass + +gobject.type_register(CodecGraphLayout) + +class CodecGraph(gtk.Window): + + def __init__(self, codec): + gtk.Window.__init__(self) + self.codec = codec + self.connect('destroy', self.__destroy) + self.set_default_size(800, 600) + self.set_title(self.__class__.__name__ + ' ' + self.codec.name) + self.set_border_width(0) + + table = gtk.Table(2, 2, False) + self.add(table) + + self.layout = CodecGraphLayout(None, None, codec, self.get_title()) + table.attach(self.layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND, + gtk.FILL|gtk.EXPAND, 0, 0) + vScrollbar = gtk.VScrollbar(None) + table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK, + gtk.FILL|gtk.SHRINK, 0, 0) + hScrollbar = gtk.HScrollbar(None) + table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK, + gtk.FILL|gtk.SHRINK, 0, 0) + vAdjust = self.layout.get_vadjustment() + vScrollbar.set_adjustment(vAdjust) + hAdjust = self.layout.get_hadjustment() + hScrollbar.set_adjustment(hAdjust) + self.show_all() + GRAPH_WINDOWS[codec] = self + + def __destroy(self, widget): + del GRAPH_WINDOWS[self.codec] + +def create_graph(codec): + if codec in GRAPH_WINDOWS: + GRAPH_WINDOWS[codec].present() + else: + CodecGraph(codec) diff --git a/hda-analyzer/hda_guilib.py b/hda-analyzer/hda_guilib.py new file mode 100644 index 0000000..532da33 --- /dev/null +++ b/hda-analyzer/hda_guilib.py @@ -0,0 +1,577 @@ +#!/usr/bin/env python +# +# Copyright (c) 2008-2010 by Jaroslav Kysela +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +import gobject +import gtk +import pango + +from hda_codec import HDACodec, HDA_card_list, \ + EAPDBTL_BITS, PIN_WIDGET_CONTROL_BITS, \ + PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \ + HDA_INPUT, HDA_OUTPUT + +def get_fixed_font(): + return pango.FontDescription("Misc Fixed,Courier Bold 9") + +class NodeGui(gtk.ScrolledWindow): + + def __init__(self, card=None, codec=None, node=None, doframe=False): + gtk.ScrolledWindow.__init__(self) + self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.set_shadow_type(gtk.SHADOW_IN) + if card and not codec and not node: + self.__build_card(card, doframe) + elif codec and not card and not node: + self.__build_codec(codec, doframe) + elif node and not card and not codec: + self.__build_node(node, doframe) + self.show_all() + + def __create_text(self, callback): + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled_window.set_shadow_type(gtk.SHADOW_IN) + + text_view = gtk.TextView() + fontName = get_fixed_font() + text_view.modify_font(fontName) + scrolled_window.add(text_view) + + buffer = gtk.TextBuffer(None) + text_view.set_buffer(buffer) + text_view.set_editable(False) + text_view.set_cursor_visible(False) + text_view.connect("visibility-notify-event", callback) + + text_view.set_wrap_mode(True) + + return scrolled_window, buffer + + def __new_text_view(self, text=None): + text_view = gtk.TextView() + text_view.set_border_width(4) + fontName = get_fixed_font() + text_view.modify_font(fontName) + if not text is None: + buffer = gtk.TextBuffer(None) + iter = buffer.get_iter_at_offset(0) + if text[-1] == '\n': + text = text[:-1] + buffer.insert(iter, text) + text_view.set_buffer(buffer) + text_view.set_editable(False) + text_view.set_cursor_visible(False) + return text_view + + def __build_node_caps(self, node): + frame = gtk.Frame('Node Caps') + frame.set_border_width(4) + if len(node.wcaps_list) == 0: + return frame + str = '' + for i in node.wcaps_list: + str += node.wcap_name(i) + '\n' + frame.add(self.__new_text_view(text=str)) + return frame + + def __node_connection_toggled(self, widget, row, data): + model, node = data + if not model[row][0]: + node.set_active_connection(int(row)) + for r in model: + r[0] = False + idx = 0 + for r in model: + r[0] = node.active_connection == idx + idx += 1 + + def __build_connection_list(self, node): + frame = gtk.Frame('Connection List') + frame.set_border_width(4) + sw = gtk.ScrolledWindow() + #sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + frame.add(sw) + if node.conn_list and node.connections: + model = gtk.ListStore( + gobject.TYPE_BOOLEAN, + gobject.TYPE_STRING + ) + idx = 0 + for i in node.connections: + iter = model.append() + node1 = node.codec.get_node(node.connections[idx]) + model.set(iter, 0, node.active_connection == idx, + 1, node1.name()) + idx += 1 + treeview = gtk.TreeView(model) + treeview.set_rules_hint(True) + treeview.get_selection().set_mode(gtk.SELECTION_SINGLE) + treeview.set_size_request(300, 30 + len(node.connections) * 25) + renderer = gtk.CellRendererToggle() + renderer.set_radio(True) + if node.active_connection != None: + renderer.connect("toggled", self.__node_connection_toggled, (model, node)) + column = gtk.TreeViewColumn("Active", renderer, active=0) + treeview.append_column(column) + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn("Destination Node", renderer, text=1, editable=False) + treeview.append_column(column) + sw.add(treeview) + return frame + + def __amp_mute_toggled(self, button, data): + caps, vals, idx = data + val = button.get_active() + vals.set_mute(idx, val) + button.set_active(vals.vals[idx] & 0x80) + + def __amp_value_changed(self, adj, data): + caps, vals, idx = data + val = int(adj.get_value()) + vals.set_value(idx, val) + adj.set_value(vals.vals[idx] & 0x7f) + + def __build_amps(self, node): + + def build_caps(title, caps, vals): + if caps and caps.ofs is None: + caps = caps.dir == HDA_INPUT and \ + node.codec.amp_caps_in or node.codec.amp_caps_out + title += ' (Global)' + frame = gtk.Frame(title) + frame.set_border_width(4) + vbox = gtk.VBox(False, 0) + if caps: + str = 'Offset: %d\n' % caps.ofs + str += 'Number of steps: %d\n' % caps.nsteps + str += 'Step size: %d\n' % caps.stepsize + str += 'Mute: %s\n' % (caps.mute and "True" or "False") + vbox.pack_start(self.__new_text_view(text=str), True, True, 0) + idx = 0 + frame1 = None + vbox1 = None + for val in vals.vals: + if vals.stereo and idx & 1 == 0: + frame1 = gtk.Frame() + vbox.pack_start(frame1, False, False) + vbox1 = gtk.VBox(False, 0) + frame1.add(vbox1) + hbox = gtk.HBox(False, 0) + label = gtk.Label('Val[%d]' % idx) + hbox.pack_start(label, False, False) + if caps.mute: + checkbutton = gtk.CheckButton('Mute') + checkbutton.set_active(val & 0x80 and True or False) + checkbutton.connect("toggled", self.__amp_mute_toggled, (caps, vals, idx)) + hbox.pack_start(checkbutton, False, False) + if caps.stepsize > 0: + adj = gtk.Adjustment((val & 0x7f) % (caps.nsteps+1), 0.0, caps.nsteps+1, 1.0, 1.0, 1.0) + scale = gtk.HScale(adj) + scale.set_digits(0) + scale.set_value_pos(gtk.POS_RIGHT) + adj.connect("value_changed", self.__amp_value_changed, (caps, vals, idx)) + hbox.pack_start(scale, True, True) + if vbox1: + vbox1.pack_start(hbox, False, False) + else: + vbox.pack_start(hbox, False, False) + idx += 1 + frame.add(vbox) + return frame + + hbox = gtk.HBox(False, 0) + c = build_caps('Input Amplifier', + node.in_amp and node.amp_caps_in or None, + node.in_amp and node.amp_vals_in or None) + hbox.pack_start(c) + c = build_caps('Output Amplifier', + node.out_amp and node.amp_caps_out or None, + node.out_amp and node.amp_vals_out or None) + hbox.pack_start(c) + + return hbox + + def __pincap_eapdbtl_toggled(self, button, data): + node, name = data + node.eapdbtl_set_value(name, button.get_active()) + button.set_active(name in node.pincap_eapdbtl) + + def __pinctls_toggled(self, button, data): + node, name = data + node.pin_widget_control_set_value(name, button.get_active()) + button.set_active(name in node.pinctl) + + def __pinctls_vref_change(self, combobox, node): + index = combobox.get_active() + idx1 = 0 + for name in PIN_WIDGET_CONTROL_VREF: + if not name: continue + if idx1 == index: + node.pin_widget_control_set_value('vref', name) + break + idx1 += 1 + idx = idx1 = 0 + for name in PIN_WIDGET_CONTROL_VREF: + if name == node.pinctl_vref: + combobox.set_active(idx1) + break + if name != None: + idx1 += 1 + + def __build_pin(self, node): + hbox = gtk.HBox(False, 0) + + if node.pincap or node.pincap_vref or node.pincap_eapdbtl: + vbox = gtk.VBox(False, 0) + if node.pincap or node.pincap_vref: + frame = gtk.Frame('PIN Caps') + frame.set_border_width(4) + str = '' + for i in node.pincap: + str += node.pincap_name(i) + '\n' + for i in node.pincap_vref: + str += 'VREF_%s\n' % i + frame.add(self.__new_text_view(text=str)) + vbox.pack_start(frame) + if 'EAPD' in node.pincap: + frame = gtk.Frame('EAPD') + frame.set_border_width(4) + vbox1 = gtk.VBox(False, 0) + for name in EAPDBTL_BITS: + checkbutton = gtk.CheckButton(name) + checkbutton.set_active(node.pincap_eapdbtls & (1 << EAPDBTL_BITS[name])) + checkbutton.connect("toggled", self.__pincap_eapdbtl_toggled, (node, name)) + vbox1.pack_start(checkbutton, False, False) + frame.add(vbox1) + vbox.pack_start(frame, False, False) + hbox.pack_start(vbox) + + vbox = gtk.VBox(False, 0) + + frame = gtk.Frame('Config Default') + frame.set_border_width(4) + str = 'Jack connection: %s\n' % node.jack_conn_name + str += 'Jack type: %s\n' % node.jack_type_name + str += 'Jack location: %s\n' % node.jack_location_name + str += 'Jack location2: %s\n' % node.jack_location2_name + str += 'Jack connector: %s\n' % node.jack_connector_name + str += 'Jack color: %s\n' % node.jack_color_name + if 'NO_PRESENCE' in node.defcfg_misc: + str += 'No presence\n' + frame.add(self.__new_text_view(text=str)) + vbox.pack_start(frame) + + frame = gtk.Frame('Widget Control') + frame.set_border_width(4) + vbox1 = gtk.VBox(False, 0) + for name in PIN_WIDGET_CONTROL_BITS: + checkbutton = gtk.CheckButton(name) + checkbutton.set_active(node.pinctls & (1 << PIN_WIDGET_CONTROL_BITS[name])) + checkbutton.connect("toggled", self.__pinctls_toggled, (node, name)) + vbox1.pack_start(checkbutton, False, False) + if node.pincap_vref: + combobox = gtk.combo_box_new_text() + idx = idx1 = active = 0 + for name in PIN_WIDGET_CONTROL_VREF: + if name == node.pinctl_vref: active = idx1 + if name: + combobox.append_text(name) + idx1 += 1 + idx += 1 + combobox.set_active(active) + combobox.connect("changed", self.__pinctls_vref_change, node) + hbox1 = gtk.HBox(False, 0) + label = gtk.Label('VREF') + hbox1.pack_start(label, False, False) + hbox1.pack_start(combobox) + vbox1.pack_start(hbox1, False, False) + frame.add(vbox1) + vbox.pack_start(frame, False, False) + + hbox.pack_start(vbox) + return hbox + + def __build_mix(self, node): + hbox = gtk.HBox(False, 0) + return hbox + + def __sdi_select_changed(self, adj, node): + val = int(adj.get_value()) + node.sdi_select_set_value(val) + adj.set_value(node.sdi_select) + + def __dig1_toggled(self, button, data): + node, name = data + val = button.get_active() + node.dig1_set_value(name, val) + button.set_active(name in node.dig1) + + def __dig1_category_activate(self, entry, node): + val = entry.get_text() + if val.lower().startswith('0x'): + val = int(val[2:], 16) + else: + try: + val = int(val) + except: + print "Unknown category value '%s'" % val + return + node.dig1_set_value('category', val) + entry.set_text("0x%02x" % node.dig1_category) + + def __build_aud(self, node): + vbox = gtk.VBox(False, 0) + + frame = gtk.Frame('Converter') + frame.set_border_width(4) + str = 'Audio Stream:\t%s\n' % node.aud_stream + str += 'Audio Channel:\t%s\n' % node.aud_channel + if node.format_ovrd: + str += 'Rates:\t\t%s\n' % node.pcm_rates[:6] + if len(node.pcm_rates) > 6: + str += '\t\t\t\t%s\n' % node.pcm_rates[6:] + str += 'Bits:\t\t%s\n' % node.pcm_bits + str += 'Streams:\t%s\n' % node.pcm_streams + else: + str += 'Global Rates:\t%s\n' % node.codec.pcm_rates[:6] + if len(node.codec.pcm_rates) > 6: + str += '\t\t%s\n' % node.codec.pcm_rates[6:] + str += 'Global Bits:\t%s\n' % node.codec.pcm_bits + str += 'Global Streams:\t%s\n' % node.codec.pcm_streams + frame.add(self.__new_text_view(text=str)) + vbox.pack_start(frame) + + if not node.sdi_select is None: + hbox1 = gtk.HBox(False, 0) + frame = gtk.Frame('SDI Select') + adj = gtk.Adjustment(node.sdi_select, 0.0, 16.0, 1.0, 1.0, 1.0) + scale = gtk.HScale(adj) + scale.set_digits(0) + scale.set_value_pos(gtk.POS_LEFT) + scale.set_size_request(200, 16) + adj.connect("value_changed", self.__sdi_select_changed, node) + frame.add(scale) + hbox1.pack_start(frame, False, False) + vbox.pack_start(hbox1, False, False) + + if node.digital: + hbox1 = gtk.HBox(False, 0) + frame = gtk.Frame('Digital Converter') + vbox1 = gtk.VBox(False, 0) + for name in DIG1_BITS: + checkbutton = gtk.CheckButton(name) + checkbutton.set_active(node.digi1 & (1 << DIG1_BITS[name])) + checkbutton.connect("toggled", self.__dig1_toggled, (node, name)) + vbox1.pack_start(checkbutton, False, False) + frame.add(vbox1) + hbox1.pack_start(frame) + frame = gtk.Frame('Digital Converter Category') + entry = gtk.Entry() + entry.set_text("0x%x" % node.dig1_category) + entry.set_width_chars(4) + entry.connect("activate", self.__dig1_category_activate, node) + frame.add(entry) + hbox1.pack_start(frame) + vbox.pack_start(hbox1, False, False) + + return vbox + + def __build_device(self, device): + vbox = gtk.VBox(False, 0) + frame = gtk.Frame('Device') + frame.set_border_width(4) + hbox = gtk.HBox(False, 0) + s = 'name=' + str(device.name) + ', type=' + \ + str(device.type) + ', device=' + str(device.device) + label = gtk.Label(s) + hbox.pack_start(label, False, False) + frame.add(hbox) + vbox.pack_start(frame) + return vbox + + def __build_controls(self, ctrls): + vbox = gtk.VBox(False, 0) + frame = gtk.Frame('Controls') + frame.set_border_width(4) + vbox1 = gtk.VBox(False, 0) + for ctrl in ctrls: + hbox1 = gtk.HBox(False, 0) + vbox1.pack_start(hbox1, False, False) + s = 'name=' + str(ctrl.name) + ', index=' + str(ctrl.index) + \ + ', device=' + str(ctrl.device) + label = gtk.Label(s) + hbox1.pack_start(label, False, False) + if ctrl.amp_chs: + hbox1 = gtk.HBox(False, 0) + vbox1.pack_start(hbox1, False, False) + s = ' chs=' + str(ctrl.amp_chs) + ', dir=' + str(ctrl.amp_dir) + \ + ', idx=' + str(ctrl.amp_idx) + ', ofs=' + str(ctrl.amp_ofs) + label = gtk.Label(s) + hbox1.pack_start(label, False, False) + frame.add(vbox1) + vbox.pack_start(frame) + return vbox + + def __build_proc(self, node): + frame = gtk.Frame('Processing Caps') + frame.set_border_width(4) + str = 'benign=%i\nnumcoef=%i\n' % (node.proc_benign, node.proc_numcoef) + frame.add(self.__new_text_view(text=str)) + return frame + + def __build_node(self, node, doframe=False): + self.mytitle = node.name() + if doframe: + mframe = gtk.Frame(self.mytitle) + mframe.set_border_width(4) + else: + mframe = gtk.Table() + + vbox = gtk.VBox(False, 0) + dev = node.get_device() + if not dev is None: + vbox.pack_start(self.__build_device(dev), False, False) + ctrls = node.get_controls() + if ctrls: + vbox.pack_start(self.__build_controls(ctrls), False, False) + hbox = gtk.HBox(False, 0) + hbox.pack_start(self.__build_node_caps(node)) + hbox.pack_start(self.__build_connection_list(node)) + vbox.pack_start(hbox, False, False) + if node.in_amp or node.out_amp: + vbox.pack_start(self.__build_amps(node), False, False) + if node.wtype_id == 'PIN': + vbox.pack_start(self.__build_pin(node), False, False) + elif node.wtype_id in ['AUD_IN', 'AUD_OUT']: + vbox.pack_start(self.__build_aud(node), False, False) + else: + if not node.wtype_id in ['AUD_MIX', 'BEEP', 'AUD_SEL']: + print 'Node type %s has no GUI support' % node.wtype_id + if node.proc_wid: + vbox.pack_start(self.__build_proc(node), False, False) + + mframe.add(vbox) + self.add_with_viewport(mframe) + + def __build_codec_info(self, codec): + vbox = gtk.VBox(False, 0) + + frame = gtk.Frame('Codec Identification') + frame.set_border_width(4) + str = 'Audio Fcn Group: %s\n' % (codec.afg and "0x%02x" % codec.afg or "N/A") + str += 'Modem Fcn Group: %s\n' % (codec.mfg and "0x%02x" % codec.mfg or "N/A") + str += 'Vendor ID:\t 0x%08x\n' % codec.vendor_id + str += 'Subsystem ID:\t 0x%08x\n' % codec.subsystem_id + str += 'Revision ID:\t 0x%08x\n' % codec.revision_id + frame.add(self.__new_text_view(text=str)) + vbox.pack_start(frame, False, False) + + frame = gtk.Frame('PCM Global Capabilities') + frame.set_border_width(4) + str = 'Rates:\t\t %s\n' % codec.pcm_rates[:6] + if len(codec.pcm_rates) > 6: + str += '\t\t %s\n' % codec.pcm_rates[6:] + str += 'Bits:\t\t %s\n' % codec.pcm_bits + str += 'Streams:\t %s\n' % codec.pcm_streams + frame.add(self.__new_text_view(text=str)) + vbox.pack_start(frame, False, False) + + return vbox + + def __build_codec_amps(self, codec): + + def build_caps(title, caps): + frame = gtk.Frame(title) + frame.set_border_width(4) + if caps and caps.ofs != None: + str = 'Offset:\t\t %d\n' % caps.ofs + str += 'Number of steps: %d\n' % caps.nsteps + str += 'Step size:\t %d\n' % caps.stepsize + str += 'Mute:\t\t %s\n' % (caps.mute and "True" or "False") + frame.add(self.__new_text_view(text=str)) + return frame + + hbox = gtk.HBox(False, 0) + c = build_caps('Global Input Amplifier Caps', codec.amp_caps_in) + hbox.pack_start(c) + c = build_caps('Global Output Amplifier Caps', codec.amp_caps_out) + hbox.pack_start(c) + + return hbox + + def __gpio_toggled(self, button, (codec, id, idx)): + codec.gpio.set(id, idx, button.get_active()) + button.set_active(codec.gpio.test(id, idx)) + + def __build_codec_gpio(self, codec): + frame = gtk.Frame('GPIO') + frame.set_border_width(4) + hbox = gtk.HBox(False, 0) + str = 'IO Count: %d\n' % codec.gpio_max + str += 'O Count: %d\n' % codec.gpio_o + str += 'I Count: %d\n' % codec.gpio_i + str += 'Unsolicited: %s\n' % (codec.gpio_unsol and "True" or "False") + str += 'Wake: %s\n' % (codec.gpio_wake and "True" or "False") + hbox.pack_start(self.__new_text_view(text=str), False, False) + frame.add(hbox) + for id in GPIO_IDS: + id1 = id == 'direction' and 'out-dir' or id + frame1 = gtk.Frame(id1) + frame1.set_border_width(4) + vbox1 = gtk.VBox(False, 0) + for i in range(codec.gpio_max): + checkbutton = gtk.CheckButton('[%d]' % i) + checkbutton.set_active(codec.gpio.test(id, i)) + checkbutton.connect("toggled", self.__gpio_toggled, (codec, id, i)) + vbox1.pack_start(checkbutton, False, False) + frame1.add(vbox1) + hbox.pack_start(frame1, False, False) + return frame + + def __build_codec(self, codec, doframe=False): + self.mytitle = codec.name + if doframe: + mframe = gtk.Frame(self.mytitle) + mframe.set_border_width(4) + else: + mframe = gtk.Table() + + vbox = gtk.VBox(False, 0) + vbox.pack_start(self.__build_codec_info(codec), False, False) + vbox.pack_start(self.__build_codec_amps(codec), False, False) + vbox.pack_start(self.__build_codec_gpio(codec), False, False) + mframe.add(vbox) + self.add_with_viewport(mframe) + + def __build_card_info(self, card): + str = 'Card: %s\n' % card.card + str += 'Id: %s\n' % card.id + str += 'Driver: %s\n' % card.driver + str += 'Name: %s\n' % card.name + str += 'LongName: %s\n' % card.longname + return self.__new_text_view(text=str) + + def __build_card(self, card, doframe=False): + self.mytitle = card.name + if doframe: + mframe = gtk.Frame(self.mytitle) + mframe.set_border_width(4) + else: + mframe = gtk.Table() + + vbox = gtk.VBox(False, 0) + vbox.pack_start(self.__build_card_info(card), False, False) + mframe.add(vbox) + self.add_with_viewport(mframe) diff --git a/hda-analyzer/run.py b/hda-analyzer/run.py index 0fd48e4..59def3d 100755 --- a/hda-analyzer/run.py +++ b/hda-analyzer/run.py @@ -1,7 +1,8 @@ #!/usr/bin/env python URL="http://git.alsa-project.org/?p=alsa.git;a=blob_plain;f=hda-analyzer/" -FILES=["hda_analyzer.py", "hda_codec.py", "hda_proc.py"] +FILES=["hda_analyzer.py", "hda_guilib.py", "hda_codec.py", "hda_proc.py", + "hda_graph.py"] try: import gobject -- 2.47.1