--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+# 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.
+
+"""
+hda_analyzer - a tool to analyze HDA codecs widgets and connections
+
+Usage: hda_analyzer [[codec_proc] ...]
+ or: hda_analyzer --monitor
+
+ codec_proc might specify multiple codec files per card:
+ codec_proc_file1+codec_proc_file2
+ or codec_proc might be a dump from alsa-info.sh
+ or codec_proc might be a hash for codec database at www.alsa-project.org
+ or codec_proc might be a URL for codec dump or alsa-info.sh dump
+
+ Monitor mode: check for codec changes in realtime and dump diffs.
+"""
+
+import os
+import sys
+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
+from hda_proc import DecodeProcFile, DecodeAlsaInfoFile, HDACodecProc
+from hda_guilib import *
+from hda_graph import create_graph
+
+def gethttpfile(url, size=1024*1024):
+ from urllib import splithost
+ from httplib import HTTP
+ if not url.startswith('http:'):
+ raise ValueError, "URL %s" % url
+ host, selector = splithost(url[5:])
+ h = HTTP(host)
+ h.putrequest('GET', url)
+ h.endheaders()
+ h.getreply()
+ res = h.getfile().read(size)
+ h.close()
+ return res
+
+def read_nodes2(card, codec):
+ try:
+ c = HDACodec(card, codec)
+ except OSError, msg:
+ if msg[0] == 13:
+ print "Codec %i/%i unavailable - permissions..." % (card, codec)
+ elif msg[0] == 16:
+ print "Codec %i/%i is busy..." % (card, codec)
+ elif msg[0] != 2:
+ print "Codec %i/%i access problem (%s)" % repr(msg)
+ return
+ c.analyze()
+ if not card in CODEC_TREE:
+ CODEC_TREE[card] = {}
+ DIFF_TREE[card] = {}
+ CODEC_TREE[card][c.device] = c
+ DIFF_TREE[card][c.device] = c.dump()
+
+def read_nodes3(card, codec, proc_file):
+ c = HDACodecProc(card, codec, proc_file)
+ c.analyze()
+ if not card in CODEC_TREE:
+ CODEC_TREE[card] = {}
+ DIFF_TREE[card] = {}
+ CODEC_TREE[card][c.device] = c
+ DIFF_TREE[card][c.device] = c.dump()
+
+def read_nodes(proc_files):
+ l = HDA_card_list()
+ for c in l:
+ for i in range(4):
+ read_nodes2(c.card, i)
+ card = 1000
+ for f in proc_files:
+ a = f.split('+')
+ idx = 0
+ if len(a) == 1:
+ if a[0].startswith('http://'):
+ proc_file = gethttpfile(a[0])
+ elif len(a[0]) == 40 and not os.path.exists(a[0]):
+ url = 'http://www.alsa-project.org/db/?f=' + a[0]
+ print 'Downloading contents from %s' % url
+ proc_file = gethttpfile(url)
+ if not proc_file:
+ print "HASH %s cannot be downloaded..." % a[0]
+ continue
+ else:
+ print ' Success'
+ else:
+ proc_file = DecodeProcFile(a[0])
+ proc_file = DecodeAlsaInfoFile(proc_file)
+ for i in proc_file:
+ read_nodes3(card, idx, i)
+ card += 1
+ a = []
+ for i in a:
+ proc_file = DecodeProcFile(i)
+ read_nodes3(card, idx, proc_file)
+ idx += 1
+ card += 1
+ cnt = 0
+ for c in CODEC_TREE:
+ if len(CODEC_TREE[c]) > 0:
+ cnt += 1
+ return cnt
+
+(
+ TITLE_COLUMN,
+ CARD_COLUMN,
+ CODEC_COLUMN,
+ NODE_COLUMN,
+ ITALIC_COLUMN
+) = range(5)
+
+class HDAAnalyzer(gtk.Window):
+ info_buffer = None
+ node_window = None
+ codec = None
+ node = None
+
+ def __init__(self):
+ gtk.Window.__init__(self)
+ self.connect('destroy', self.__destroy)
+ self.set_default_size(800, 400)
+ self.set_title(self.__class__.__name__)
+ self.set_border_width(10)
+
+ self.tooltips = gtk.Tooltips()
+
+ hbox = gtk.HBox(False, 3)
+ self.add(hbox)
+
+ vbox = gtk.VBox(False, 0)
+ scrolled_window = gtk.ScrolledWindow()
+ scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled_window.set_shadow_type(gtk.SHADOW_IN)
+ treeview = self.__create_treeview()
+ treeview.set_size_request(250, 600)
+ scrolled_window.add(treeview)
+ vbox.pack_start(scrolled_window)
+ hbox1 = gtk.HBox(False, 0)
+ button = gtk.Button("About")
+ button.connect("clicked", self.__about_clicked)
+ self.tooltips.set_tip(button, "README! Show the purpose of this program.")
+ hbox1.pack_start(button)
+ button = gtk.Button("Revert")
+ button.connect("clicked", self.__revert_clicked)
+ self.tooltips.set_tip(button, "Revert settings for selected codec.")
+ hbox1.pack_start(button)
+ button = gtk.Button("Diff")
+ 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 = gtk.Table()
+ self._new_notebook_page(self.node_window, '_Node editor')
+
+ scrolled_window, self.info_buffer = self.__create_text(self.__dump_visibility)
+ self._new_notebook_page(scrolled_window, '_Text dump')
+
+ self.show_all()
+ TRACKER.add(self)
+
+ def __destroy(self, widget):
+ TRACKER.close(self)
+
+ 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,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK))
+ text_view = gtk.TextView()
+ text_view.set_border_width(4)
+ str = """\
+HDA Analyzer
+
+This tool allows change the HDA codec setting using direct hardware access
+bypassing driver's mixer layer.
+
+To learn more about HDA (High Definition Audio), see
+http://www.intel.com/standards/hdaudio/ for more details.
+
+Please, if you find how your codec work, send this information to alsa-devel
+mailing list - http://www.alsa-project.org .
+
+Bugs, ideas, comments about this program should be sent to alsa-devel
+mailing list, too.
+"""
+ buffer = gtk.TextBuffer(None)
+ iter = buffer.get_iter_at_offset(0)
+ buffer.insert(iter, str[:-1])
+ text_view.set_buffer(buffer)
+ text_view.set_editable(False)
+ text_view.set_cursor_visible(False)
+ dialog.vbox.pack_start(text_view, False, False)
+ dialog.show_all()
+ dialog.run()
+ dialog.destroy()
+
+ def __revert_clicked(self, button):
+ if not self.codec:
+ msg = "Please, select a codec in left codec/node tree."
+ type = gtk.MESSAGE_WARNING
+ else:
+ self.codec.revert()
+ self.__refresh()
+ msg = "Setting for codec %s/%s (%s) was reverted!" % (self.codec.card, self.codec.device, self.codec.name)
+ type = gtk.MESSAGE_INFO
+
+ 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,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK))
+ text_view = gtk.TextView()
+ text_view.set_border_width(4)
+ fontName = get_fixed_font()
+ text_view.modify_font(fontName)
+ str = do_diff1(self.codec, DIFF_TREE[self.card][self.codec.device])
+ if str == '':
+ str = 'No changes'
+ buffer = gtk.TextBuffer(None)
+ iter = buffer.get_iter_at_offset(0)
+ buffer.insert(iter, str[:-1])
+ text_view.set_buffer(buffer)
+ text_view.set_editable(False)
+ text_view.set_cursor_visible(False)
+ dialog.vbox.pack_start(text_view, False, False)
+ dialog.show_all()
+ 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)
+
+ def __dump_visibility(self, textview, event):
+ codec = self.codec
+ node = self.node
+ if not codec:
+ txt = 'Show some card info here...'
+ elif codec and self.node < 0:
+ txt = codec.dump(skip_nodes=True)
+ else:
+ n = codec.get_node(node)
+ txt = codec.dump_node(n)
+ buffer = self.info_buffer
+ start, end = buffer.get_bounds()
+ buffer.delete(start, end)
+ if not txt: return
+ iter = buffer.get_iter_at_offset(0)
+ buffer.insert(iter, txt)
+
+ def selection_changed_cb(self, selection):
+ model, iter = selection.get_selected()
+ if not iter:
+ return False
+ card = model.get_value(iter, CARD_COLUMN)
+ codec = model.get_value(iter, CODEC_COLUMN)
+ node = model.get_value(iter, NODE_COLUMN)
+ self.card = card
+ self.codec = None
+ if codec >= 0:
+ self.codec = CODEC_TREE[card][codec]
+ self.node = node
+ self.__refresh()
+
+ def load(self):
+ codec = self.codec
+ node = self.node
+ n = None
+ if not codec:
+ txt = 'Show some card info here...'
+ elif codec and node < 0:
+ txt = codec.dump(skip_nodes=True)
+ else:
+ n = codec.get_node(node)
+
+ for child in self.node_window.get_children()[:]:
+ self.node_window.remove(child)
+ child.destroy()
+
+ if not n:
+ if not codec:
+ for i in CODEC_TREE[self.card]:
+ card = CODEC_TREE[self.card][i].mcard
+ break
+ self.node_window.add(NodeGui(card=card))
+ elif codec:
+ self.node_window.add(NodeGui(codec=codec))
+ else:
+ return
+ else:
+ self.node_window.add(NodeGui(node=n))
+ self.node_window.show_all()
+
+ def _new_notebook_page(self, widget, label):
+ l = gtk.Label('')
+ l.set_text_with_mnemonic(label)
+ self.notebook.append_page(widget, l)
+
+ def __create_treeview(self):
+ model = gtk.TreeStore(
+ gobject.TYPE_STRING,
+ gobject.TYPE_INT,
+ gobject.TYPE_INT,
+ gobject.TYPE_INT,
+ gobject.TYPE_BOOLEAN
+ )
+
+ treeview = gtk.TreeView(model)
+ selection = treeview.get_selection()
+ selection.set_mode(gtk.SELECTION_BROWSE)
+ treeview.set_size_request(200, -1)
+
+ for card in CODEC_TREE:
+ iter = model.append(None)
+ model.set(iter,
+ TITLE_COLUMN, 'card-%s' % card,
+ CARD_COLUMN, card,
+ CODEC_COLUMN, -1,
+ NODE_COLUMN, -1,
+ ITALIC_COLUMN, False)
+ for codec in CODEC_TREE[card]:
+ citer = model.append(iter)
+ codec = CODEC_TREE[card][codec]
+ model.set(citer,
+ TITLE_COLUMN, 'codec-%s' % codec.device,
+ CARD_COLUMN, card,
+ CODEC_COLUMN, codec.device,
+ NODE_COLUMN, -1,
+ ITALIC_COLUMN, False)
+ for nid in codec.nodes:
+ viter = model.append(citer)
+ node = codec.get_node(nid)
+ model.set(viter,
+ TITLE_COLUMN, 'Node[0x%02x] %s' % (nid, node.wtype_id),
+ CARD_COLUMN, card,
+ CODEC_COLUMN, codec.device,
+ NODE_COLUMN, nid,
+ ITALIC_COLUMN, False)
+ nid += 1
+
+ cell = gtk.CellRendererText()
+ cell.set_property('style', pango.STYLE_ITALIC)
+
+ column = gtk.TreeViewColumn('Nodes', cell, text=TITLE_COLUMN,
+ style_set=ITALIC_COLUMN)
+
+ treeview.append_column(column)
+
+ selection.connect('changed', self.selection_changed_cb)
+
+ treeview.expand_all()
+
+ return treeview
+
+ 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 monitor():
+ from time import sleep
+ print "Watching %s cards" % len(CODEC_TREE)
+ dumps = {}
+ while 1:
+ ok = False
+ for card in CODEC_TREE:
+ if not card in dumps:
+ dumps[card] = {}
+ for codec in CODEC_TREE[card]:
+ if not codec in dumps[card]:
+ dumps[card][codec] = ''
+ c = CODEC_TREE[card][codec]
+ if c.hwaccess:
+ ok = True
+ c.reread()
+ diff = ''
+ dump1 = c.dump()
+ if dumps[card][codec]:
+ diff = do_diff1(c, dumps[card][codec])
+ dumps[card][codec] = dump1
+ if diff:
+ print "======================================"
+ print diff
+ if not ok:
+ print "Nothing to monitor (no hwdep access)"
+ break
+ sleep(1)
+
+def main(argv):
+ cmd = None
+ if len(argv) > 1 and argv[1] in ('-h', '-help', '--help'):
+ print __doc__ % globals()
+ return 0
+ 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."
+ print
+ print "You may also check, if you compiled HDA driver with HWDEP"
+ print "interface as well or close all application using HWDEP."
+ print
+ print "Try run this program as root user."
+ return 0
+ else:
+ 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])
+ else:
+ HDAAnalyzer()
+ gtk.main()
+ return 1
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+# 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 os
+import struct
+from fcntl import ioctl
+from hda_mixer import AlsaMixer, AlsaMixerElem, AlsaMixerElemId
+
+def __ioctl_val(val):
+ # workaround for OverFlow bug in python 2.4
+ if val & 0x80000000:
+ return -((val^0xffffffff)+1)
+ return val
+
+IOCTL_INFO = __ioctl_val(0x80dc4801)
+IOCTL_PVERSION = __ioctl_val(0x80044810)
+IOCTL_VERB_WRITE = __ioctl_val(0xc0084811)
+IOCTL_GET_WCAPS = __ioctl_val(0xc0084812)
+
+CTL_IOCTL_CARD_INFO = __ioctl_val(0x81785501)
+
+AC_NODE_ROOT = 0
+
+(
+ HDA_INPUT,
+ HDA_OUTPUT
+) = range(2)
+
+VERBS = {
+ 'GET_STREAM_FORMAT': 0x0a00,
+ 'GET_AMP_GAIN_MUTE': 0x0b00,
+ 'GET_PROC_COEF': 0x0c00,
+ 'GET_COEF_INDEX': 0x0d00,
+ 'PARAMETERS': 0x0f00,
+ 'GET_CONNECT_SEL': 0x0f01,
+ 'GET_CONNECT_LIST': 0x0f02,
+ 'GET_PROC_STATE': 0x0f03,
+ 'GET_SDI_SELECT': 0x0f04,
+ 'GET_POWER_STATE': 0x0f05,
+ 'GET_CONV': 0x0f06,
+ 'GET_PIN_WIDGET_CONTROL': 0x0f07,
+ 'GET_UNSOLICITED_RESPONSE': 0x0f08,
+ 'GET_PIN_SENSE': 0x0f09,
+ 'GET_BEEP_CONTROL': 0x0f0a,
+ 'GET_EAPD_BTLENABLE': 0x0f0c,
+ 'GET_DIGI_CONVERT_1': 0x0f0d,
+ 'GET_DIGI_CONVERT_2': 0x0f0e,
+ 'GET_VOLUME_KNOB_CONTROL': 0x0f0f,
+ 'GET_GPIO_DATA': 0x0f15,
+ 'GET_GPIO_MASK': 0x0f16,
+ 'GET_GPIO_DIRECTION': 0x0f17,
+ 'GET_GPIO_WAKE_MASK': 0x0f18,
+ 'GET_GPIO_UNSOLICITED_RSP_MASK': 0x0f19,
+ 'GET_GPIO_STICKY_MASK': 0x0f1a,
+ 'GET_CONFIG_DEFAULT': 0x0f1c,
+ 'GET_SUBSYSTEM_ID': 0x0f20,
+
+ 'SET_STREAM_FORMAT': 0x200,
+ 'SET_AMP_GAIN_MUTE': 0x300,
+ 'SET_PROC_COEF': 0x400,
+ 'SET_COEF_INDEX': 0x500,
+ 'SET_CONNECT_SEL': 0x701,
+ 'SET_PROC_STATE': 0x703,
+ 'SET_SDI_SELECT': 0x704,
+ 'SET_POWER_STATE': 0x705,
+ 'SET_CHANNEL_STREAMID': 0x706,
+ 'SET_PIN_WIDGET_CONTROL': 0x707,
+ 'SET_UNSOLICITED_ENABLE': 0x708,
+ 'SET_PIN_SENSE': 0x709,
+ 'SET_BEEP_CONTROL': 0x70a,
+ 'SET_EAPD_BTLENABLE': 0x70c,
+ 'SET_DIGI_CONVERT_1': 0x70d,
+ 'SET_DIGI_CONVERT_2': 0x70e,
+ 'SET_VOLUME_KNOB_CONTROL': 0x70f,
+ 'SET_GPIO_DATA': 0x715,
+ 'SET_GPIO_MASK': 0x716,
+ 'SET_GPIO_DIRECTION': 0x717,
+ 'SET_GPIO_WAKE_MASK': 0x718,
+ 'SET_GPIO_UNSOLICITED_RSP_MASK': 0x719,
+ 'SET_GPIO_STICKY_MASK': 0x71a,
+ 'SET_CONFIG_DEFAULT_BYTES_0': 0x71c,
+ 'SET_CONFIG_DEFAULT_BYTES_1': 0x71d,
+ 'SET_CONFIG_DEFAULT_BYTES_2': 0x71e,
+ 'SET_CONFIG_DEFAULT_BYTES_3': 0x71f,
+ 'SET_CODEC_RESET': 0x7ff
+}
+
+PARAMS = {
+ 'VENDOR_ID': 0x00,
+ 'SUBSYSTEM_ID': 0x01,
+ 'REV_ID': 0x02,
+ 'NODE_COUNT': 0x04,
+ 'FUNCTION_TYPE': 0x05,
+ 'AUDIO_FG_CAP': 0x08,
+ 'AUDIO_WIDGET_CAP': 0x09,
+ 'PCM': 0x0a,
+ 'STREAM': 0x0b,
+ 'PIN_CAP': 0x0c,
+ 'AMP_IN_CAP': 0x0d,
+ 'CONNLIST_LEN': 0x0e,
+ 'POWER_STATE': 0x0f,
+ 'PROC_CAP': 0x10,
+ 'GPIO_CAP': 0x11,
+ 'AMP_OUT_CAP': 0x12,
+ 'VOL_KNB_CAP': 0x13
+}
+
+WIDGET_TYPES = {
+ 'AUD_OUT': 0x00,
+ 'AUD_IN': 0x01,
+ 'AUD_MIX': 0x02,
+ 'AUD_SEL': 0x03,
+ 'PIN': 0x04,
+ 'POWER': 0x05,
+ 'VOL_KNB': 0x06,
+ 'BEEP': 0x07,
+ 'VENDOR': 0x0f
+}
+
+WIDGET_TYPE_NAMES = [
+ "Audio Output",
+ "Audio Input",
+ "Audio Mixer",
+ "Audio Selector",
+ "Pin Complex",
+ "Power Widget",
+ "Volume Knob Widget",
+ "Beep Generator Widget",
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ "Vendor Defined Widget"
+]
+
+WIDGET_TYPE_IDS = [
+ "AUD_OUT",
+ "AUD_IN",
+ "AUD_MIX",
+ "AUD_SEL",
+ "PIN",
+ "POWER",
+ "VOL_KNB",
+ "BEEP",
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ "VENDOR"
+]
+
+WIDGET_CAP_NAMES = {
+ 'STEREO': 'Stereo',
+ 'IN_AMP': 'Input Amplifier',
+ 'OUT_AMP': 'Output Amplifier',
+ 'AMP_OVRD': 'Amplifier Override',
+ 'FORMAT_OVRD': 'Format Override',
+ 'STRIPE': 'Stripe',
+ 'PROC_WID': 'Proc Widget',
+ 'CONN_LIST': 'Connection List',
+ 'UNSOL_CAP': 'Unsolicited Capabilities',
+ 'DIGITAL': 'Digital',
+ 'POWER': 'Power',
+ 'LR_SWAP': 'L/R Swap',
+ 'CP_CAPS': 'CP Capabilities'
+}
+
+WIDGET_PINCAP_NAMES = {
+ 'IMP_SENSE': 'Input Sense',
+ 'TRIG_REQ': 'Trigger Request',
+ 'PRES_DETECT': 'Press Detect',
+ 'HP_DRV': 'Headphone Drive',
+ 'OUT': 'Output',
+ 'IN': 'Input',
+ 'BALANCE': 'Balance',
+ 'HDMI': 'HDMI',
+ 'EAPD': 'EAPD',
+ 'DP': 'Display Port',
+ 'HBR': 'Hight Bit Rate',
+}
+
+GPIO_IDS = {
+ 'enable': (VERBS['GET_GPIO_MASK'], VERBS['SET_GPIO_MASK']),
+ 'direction': (VERBS['GET_GPIO_DIRECTION'], VERBS['SET_GPIO_DIRECTION']),
+ 'wake': (VERBS['GET_GPIO_WAKE_MASK'], VERBS['SET_GPIO_WAKE_MASK']),
+ 'unsol': (VERBS['GET_GPIO_UNSOLICITED_RSP_MASK'], VERBS['SET_GPIO_UNSOLICITED_RSP_MASK']),
+ 'sticky': (VERBS['GET_GPIO_STICKY_MASK'], VERBS['SET_GPIO_STICKY_MASK']),
+ 'data': (VERBS['GET_GPIO_DATA'], VERBS['SET_GPIO_DATA'])
+}
+
+EAPDBTL_BITS = {
+ 'BALANCED': 0,
+ 'EAPD': 1,
+ 'R/L': 2
+}
+
+PIN_WIDGET_CONTROL_BITS = {
+ 'IN': 5,
+ 'OUT': 6,
+ 'HP': 7
+}
+
+PIN_WIDGET_CONTROL_VREF = [
+ "HIZ", "50", "GRD", None, "80", "100", None, None
+]
+
+DIG1_BITS = {
+ 'ENABLE': 0,
+ 'VALIDITY': 1,
+ 'VALIDITYCFG': 2,
+ 'EMPHASIS': 3,
+ 'COPYRIGHT': 4,
+ 'NONAUDIO': 5,
+ 'PROFESSIONAL': 6,
+ 'LEVEL': 7
+}
+
+POWER_STATES = ["D0", "D1", "D2", "D3", "D3cold", "S3D3cold", "CLKSTOP", "EPSS"]
+
+class HDAAmpCaps:
+
+ def __init__(self, codec, nid, dir):
+ self.codec = codec
+ self.nid = nid
+ self.dir = dir
+ self.cloned = False
+ self.reread()
+
+ def reread(self):
+ caps = self.codec.param_read(self.nid,
+ PARAMS[self.dir == HDA_OUTPUT and 'AMP_OUT_CAP' or 'AMP_IN_CAP'])
+ if caps == ~0 or caps == 0:
+ if self.dir == HDA_INPUT:
+ ccaps = self.codec.amp_caps_in
+ else:
+ ccaps = self.codec.amp_caps_out
+ if ccaps:
+ ccaps.clone(self)
+ else:
+ self.ofs = self.nsteps = self.stepsize = self.mute = None
+ else:
+ self.ofs = caps & 0x7f
+ self.nsteps = (caps >> 8) & 0x7f
+ self.stepsize = (caps >> 16) & 0x7f
+ self.mute = (caps >> 31) & 1 and True or False
+
+ def clone(self, ampcaps):
+ ampcaps.ofs = self.ofs
+ ampcaps.nsteps = self.nsteps
+ ampcaps.stepsize = self.stepsize
+ ampcaps.mute = self.mute
+ ampcaps.cloned = True
+
+ def get_val_db(self, val):
+ if self.ofs is None:
+ return None
+ if val & 0x80:
+ return -999999
+ range = (self.stepsize + 1) * 25
+ off = -self.ofs * range
+ if val > self.nsteps:
+ db = off + self.nsteps * range
+ if val != 0 or self.nsteps != 0:
+ print "val > nsteps? for nid 0x%02x" % self.nid, val, self.nsteps
+ else:
+ db = off + val * range
+ return db
+
+ def get_val_perc(self, val):
+ if self.ofs is None:
+ return None
+ if self.nsteps == 0:
+ return 0
+ return (val * 100) / self.nsteps
+
+ def get_val_str(self, val):
+ if self.ofs is None:
+ return "0x%02x" % val
+ else:
+ db = self.get_val_db(val & 0x7f)
+ res = val & 0x80 and "{mute-" or "{"
+ res += "0x%02x" % (val & 0x7f)
+ res += ":%02i.%idB" % (db / 100, db % 100)
+ res += ":%i%%}" % (self.get_val_perc(val & 0x7f))
+ return res
+
+class HDAAmpVal:
+
+ def __init__(self, codec, node, dir, caps):
+ self.codec = codec
+ self.node = node
+ self.dir = dir
+ if caps.ofs == None:
+ self.caps = dir == HDA_INPUT and codec.amp_caps_in or codec.amp_caps_out
+ else:
+ self.caps = caps
+ self.nid = node.nid
+ self.stereo = node.stereo
+ self.indices = 1
+ self.origin_vals = None
+ if dir == HDA_INPUT:
+ self.indices = node.wtype_id == 'PIN' and 1 or len(node.connections)
+ self.reread()
+
+ def __write_val(self, idx):
+ dir = self.dir == HDA_OUTPUT and (1<<15) or (1<<14)
+ verb = VERBS['SET_AMP_GAIN_MUTE']
+ if self.stereo:
+ indice = idx / 2
+ dir |= idx & 1 and (1 << 12) or (1 << 13)
+ else:
+ indice = idx
+ dir |= (1 << 12) | (1 << 13)
+ self.codec.rw(self.nid, verb, dir | (indice << 8) | self.vals[idx])
+
+ def set_mute(self, idx, mute):
+ val = self.vals[idx]
+ if mute:
+ changed = (self.vals[idx] & 0x80) == 0
+ self.vals[idx] |= 0x80
+ else:
+ changed = (self.vals[idx] & 0x80) == 0x80
+ self.vals[idx] &= ~0x80
+ self.__write_val(idx)
+ return changed
+
+ def set_value(self, idx, val):
+ changed = (self.vals[idx] & 0x7f) != val
+ self.vals[idx] &= ~0x7f
+ self.vals[idx] |= val & 0x7f
+ self.__write_val(idx)
+ return changed
+
+ def reread(self):
+ dir = self.dir == HDA_OUTPUT and (1<<15) or (0<<15)
+ self.vals = []
+ verb = VERBS['GET_AMP_GAIN_MUTE']
+ for i in range(self.indices):
+ if self.stereo:
+ val = self.codec.rw(self.nid, verb, (1 << 13) | dir | i)
+ self.vals.append(val)
+ val = self.codec.rw(self.nid, verb, (0 << 13) | dir | i)
+ self.vals.append(val)
+ if self.origin_vals == None:
+ self.origin_vals = self.vals[:]
+
+ def revert(self):
+ self.vals = self.origin_vals[:]
+ for idx in range(len(self.vals)):
+ self.__write_val(idx)
+
+ def get_val(self, idx):
+ if self.stereo:
+ return [self.vals[idx*2], self.vals[idx*2+1]]
+ return self.vals[idx]
+
+ def get_val_db(self, idx):
+ vals = self.get_val(idx)
+ if type(vals) != type([]):
+ vals = [vals]
+ res = []
+ for val in vals:
+ res.append(self.caps.get_val_db(val))
+ return res
+
+ def get_val_str(self, idx):
+
+ def niceval(val):
+ return self.caps.get_val_str(val)
+
+ if self.stereo:
+ return '[' + niceval(self.vals[idx*2]) + ' ' + niceval(self.vals[idx*2+1]) + ']'
+ return niceval(self.vals[idx])
+
+class HDARootNode:
+
+ def __init__(self, codec, _name):
+ self.codec = codec
+ self._name = _name
+
+ def name(self):
+ return self._name
+
+class HDANode:
+
+ def __init__(self, codec, nid, cache=True):
+ self.codec = codec
+ self.nid = nid
+ self.wcaps = cache and codec.get_wcap(nid) or codec.get_raw_wcap(nid)
+ self.stereo = (self.wcaps & (1 << 0)) and True or False
+ self.in_amp = (self.wcaps & (1 << 1)) and True or False
+ self.out_amp = (self.wcaps & (1 << 2)) and True or False
+ self.amp_ovrd = (self.wcaps & (1 << 3)) and True or False
+ self.format_ovrd = (self.wcaps & (1 << 4)) and True or False
+ self.stripe = (self.wcaps & (1 << 5)) and True or False
+ self.proc_wid = (self.wcaps & (1 << 6)) and True or False
+ self.unsol_cap = (self.wcaps & (1 << 7)) and True or False
+ self.conn_list = (self.wcaps & (1 << 8)) and True or False
+ self.digital = (self.wcaps & (1 << 9)) and True or False
+ self.power = (self.wcaps & (1 << 10)) and True or False
+ self.lr_swap = (self.wcaps & (1 << 11)) and True or False
+ self.cp_caps = (self.wcaps & (1 << 12)) and True or False
+ self.chan_cnt_ext = (self.wcaps >> 13) & 7
+ self.wdelay = (self.wcaps >> 16) & 0x0f
+ self.wtype = (self.wcaps >> 20) & 0x0f
+ self.channels = ((self.chan_cnt_ext << 1) | 1) + 1
+ self.wtype_id = WIDGET_TYPE_IDS[self.wtype]
+ if self.wtype_id == 'VOL_KNB': self.conn_list = True
+
+ self.wcaps_list = []
+ if self.stereo: self.wcaps_list.append('STEREO')
+ if self.in_amp: self.wcaps_list.append('IN_AMP')
+ if self.out_amp: self.wcaps_list.append('OUT_AMP')
+ if self.amp_ovrd: self.wcaps_list.append('AMP_OVRD')
+ if self.format_ovrd: self.wcaps_list.append('FORMAT_OVRD')
+ if self.stripe: self.wcaps_list.append('STRIPE')
+ if self.proc_wid: self.wcaps_list.append('PROC_WID')
+ if self.unsol_cap: self.wcaps_list.append('UNSOL_CAP')
+ if self.conn_list: self.wcaps_list.append('CONN_LIST')
+ if self.digital: self.wcaps_list.append('DIGITAL')
+ if self.power: self.wcaps_list.append('POWER')
+ if self.lr_swap: self.wcaps_list.append('LR_SWAP')
+ if self.cp_caps: self.wcaps_list.append('CP_CAPS')
+
+ self.origin_active_connection = None
+ self.origin_pwr = None
+ self.origin_digi1 = None
+ self.origin_pincap_eapdbtls = None
+ self.origin_pinctls = None
+ self.origin_vol_knb = None
+ self.origin_sdi_select = None
+ self.reread()
+
+ def wtype_name(self):
+ name = WIDGET_TYPE_NAMES[self.wtype]
+ if not name:
+ return "UNKNOWN Widget 0x%x" % self.wtype
+ return name
+
+ def wcap_name(self, id):
+ return WIDGET_CAP_NAMES[id]
+
+ def pincap_name(self, id):
+ return WIDGET_PINCAP_NAMES[id]
+
+ def name(self):
+ return self.wtype_name() + " [0x%02x]" % self.nid
+
+ def set_active_connection(self, val):
+ changed = False
+ if self.active_connection != None:
+ changed = self.active_connection != val
+ self.codec.rw(self.nid, VERBS['SET_CONNECT_SEL'], val)
+ self.active_connection = self.codec.rw(self.nid, VERBS['GET_CONNECT_SEL'], 0)
+ return changed
+
+ def reread(self):
+
+ def get_jack_location(cfg):
+ bases = ["N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom"]
+ specials = {0x07: "Rear Panel", 0x08: "Drive Bar",
+ 0x17: "Riser", 0x18: "HDMI", 0x19: "ATAPI",
+ 0x37: "Mobile-In", 0x38: "Mobile-Out"}
+ cfg = (cfg >> 24) & 0x3f
+ if cfg & 0x0f < 7:
+ return bases[cfg & 0x0f]
+ if cfg in specials:
+ return specials[cfg]
+ return "UNKNOWN"
+
+ def get_jack_connector(cfg):
+ names = ["Unknown", "1/8", "1/4", "ATAPI", "RCA", "Optical",
+ "Digital", "Analog", "DIN", "XLR", "RJ11", "Comb",
+ None, None, None, "Oth[6~er"]
+ cfg = (cfg >> 16) & 0x0f
+ return names[cfg] and names[cfg] or "UNKNOWN"
+
+ def get_jack_color(cfg):
+ names = ["Unknown", "Black", "Grey", "Blue", "Green", "Red", "Orange",
+ "Yellow", "Purple", "Pink", None, None, None, None, "White",
+ "Other"]
+ cfg = (cfg >> 12) & 0x0f
+ return names[cfg] and names[cfg] or "UNKNOWN"
+
+ self.connections = None
+ self.active_connection = None
+ if self.conn_list:
+ self.connections = self.codec.get_connections(self.nid)
+ if not self.wtype_id in ['AUD_MIX', 'VOL_KNB', 'POWER']:
+ self.active_connection = self.codec.rw(self.nid, VERBS['GET_CONNECT_SEL'], 0)
+ if self.origin_active_connection == None:
+ self.origin_active_connection = self.active_connection
+ if self.in_amp:
+ self.amp_caps_in = HDAAmpCaps(self.codec, self.nid, HDA_INPUT)
+ self.amp_vals_in = HDAAmpVal(self.codec, self, HDA_INPUT, self.amp_caps_in)
+ if self.out_amp:
+ self.amp_caps_out = HDAAmpCaps(self.codec, self.nid, HDA_OUTPUT)
+ self.amp_vals_out = HDAAmpVal(self.codec, self, HDA_OUTPUT, self.amp_caps_out)
+ if self.wtype_id == 'PIN':
+ jack_conns = ["Jack", "N/A", "Fixed", "Both"]
+ jack_types = ["Line Out", "Speaker", "HP Out", "CD", "SPDIF Out",
+ "Digital Out", "Modem Line", "Modem Hand",
+ "Line In", "Aux", "Mic", "Telephony", "SPDIF In",
+ "Digital In", "Reserved", "Other"]
+ jack_locations = ["Ext", "Int", "Sep", "Oth"]
+
+ caps = self.codec.param_read(self.nid, PARAMS['PIN_CAP'])
+ self.pincaps = caps
+ self.pincap = []
+ if caps & (1 << 0): self.pincap.append('IMP_SENSE')
+ if caps & (1 << 1): self.pincap.append('TRIG_REQ')
+ if caps & (1 << 2): self.pincap.append('PRES_DETECT')
+ if caps & (1 << 3): self.pincap.append('HP_DRV')
+ if caps & (1 << 4): self.pincap.append('OUT')
+ if caps & (1 << 5): self.pincap.append('IN')
+ if caps & (1 << 6): self.pincap.append('BALANCE')
+ if caps & (1 << 7): self.pincap.append('HDMI')
+ if caps & (1 << 16): self.pincap.append('EAPD')
+ if caps & (1 << 24): self.pincap.append('DP') # display port
+ if caps & (1 << 27): self.pincap.append('HBR')
+ self.pincap_vref = []
+ if caps & (1 << 8): self.pincap_vref.append('HIZ')
+ if caps & (1 << 9): self.pincap_vref.append('50')
+ if caps & (1 << 10): self.pincap_vref.append('GRD')
+ if caps & (1 << 12): self.pincap_vref.append('80')
+ if caps & (1 << 13): self.pincap_vref.append('100')
+ self.reread_eapdbtl()
+ caps = self.codec.rw(self.nid, VERBS['GET_CONFIG_DEFAULT'], 0)
+ self.defcfg_pincaps = caps
+ self.jack_conn_name = jack_conns[(caps >> 30) & 0x03]
+ self.jack_type_name = jack_types[(caps >> 20) & 0x0f]
+ self.jack_location_name = jack_locations[(caps >> 28) & 0x03]
+ self.jack_location2_name = get_jack_location(caps)
+ self.jack_connector_name = get_jack_connector(caps)
+ self.jack_color_name = get_jack_color(caps)
+ self.defcfg_assoc = (caps >> 4) & 0x0f
+ self.defcfg_sequence = (caps >> 0) & 0x0f
+ self.defcfg_misc = []
+ if caps & (1 << 8): self.defcfg_misc.append('NO_PRESENCE')
+ self.reread_pin_widget_control()
+ elif self.wtype_id == 'VOL_KNB':
+ cap = self.codec.param_read(self.nid, PARAMS['VOL_KNB_CAP'])
+ self.vol_knb_delta = (cap >> 7) & 1
+ self.vol_knb_steps = cap & 0x7f
+ self.reread_vol_knb()
+ elif self.wtype_id in ['AUD_IN', 'AUD_OUT']:
+ conv = self.codec.rw(self.nid, VERBS['GET_CONV'], 0)
+ self.aud_stream = (conv >> 4) & 0x0f
+ self.aud_channel = (conv >> 0) & 0x0f
+ self.reread_sdi_select()
+ self.reread_dig1()
+ if self.format_ovrd:
+ pcm = self.codec.param_read(self.nid, PARAMS['PCM'])
+ stream = self.codec.param_read(self.nid, PARAMS['STREAM'])
+ self.pcm_rate = pcm & 0xffff
+ self.pcm_rates = self.codec.analyze_pcm_rates(self.pcm_rate)
+ self.pcm_bit = pcm >> 16
+ self.pcm_bits = self.codec.analyze_pcm_bits(self.pcm_bit)
+ self.pcm_stream = stream
+ self.pcm_streams = self.codec.analyze_pcm_streams(self.pcm_stream)
+ if self.proc_wid:
+ proc_caps = self.codec.param_read(self.nid, PARAMS['PROC_CAP'])
+ self.proc_benign = proc_caps & 1 and True or False
+ self.proc_numcoef = (proc_caps >> 8) & 0xff
+ if self.unsol_cap:
+ unsol = self.codec.rw(self.nid, VERBS['GET_UNSOLICITED_RESPONSE'], 0)
+ self.unsol_tag = unsol & 0x3f
+ self.unsol_enabled = (unsol & (1 << 7)) and True or False
+ if self.power:
+ pwr = self.codec.param_read(self.nid, PARAMS['POWER_STATE'])
+ self.pwr_state = pwr
+ self.pwr_states = []
+ for a in range(len(POWER_STATES)):
+ if pwr & (1 << a):
+ self.pwr_states.append(POWER_STATES[a])
+ pwr = self.codec.rw(self.nid, VERBS['GET_POWER_STATE'], 0)
+ self.pwr = pwr
+ if self.origin_pwr == None:
+ self.origin_pwr = pwr
+ self.pwr_setting = pwr & 0x0f
+ self.pwr_actual = (pwr >> 4) & 0x0f
+ self.pwr_setting_name = self.pwr_setting < 4 and POWER_STATES[self.pwr_setting] or "UNKNOWN"
+ self.pwr_actual_name = self.pwr_actual < 4 and POWER_STATES[self.pwr_actual] or "UNKNOWN"
+ # NID 0x20 == Realtek Define Registers
+ if self.codec.vendor_id == 0x10ec and self.nid == 0x20:
+ self.realtek_coeff_proc = self.codec.rw(self.nid, VERBS['GET_PROC_COEF'], 0)
+ self.realtek_coeff_index = self.codec.rw(self.nid, VERBS['GET_COEF_INDEX'], 0)
+
+ def reread_eapdbtl(self):
+ self.pincap_eapdbtl = []
+ self.pincap_eapdbtls = 0
+ if not 'EAPD' in self.pincap:
+ return
+ val = self.codec.rw(self.nid, VERBS['GET_EAPD_BTLENABLE'], 0)
+ self.pincap_eapdbtls = val
+ if self.origin_pincap_eapdbtls == None:
+ self.origin_pincap_eapdbtls = val
+ for name in EAPDBTL_BITS:
+ bit = EAPDBTL_BITS[name]
+ if val & (1 << bit): self.pincap_eapdbtl.append(name)
+
+ def eapdbtl_set_value(self, name, value):
+ mask = 1 << EAPDBTL_BITS[name]
+ value = value and True or False
+ changed = (self.pincap_eapdbtls & mask) and not value or value
+ if value:
+ self.pincap_eapdbtls |= mask
+ else:
+ self.pincap_eapdbtls &= ~mask
+ self.codec.rw(self.nid, VERBS['SET_EAPD_BTLENABLE'], self.pincap_eapdbtls)
+ self.reread_eapdbtl()
+ return changed
+
+ def reread_pin_widget_control(self):
+ pinctls = self.codec.rw(self.nid, VERBS['GET_PIN_WIDGET_CONTROL'], 0)
+ self.pinctls = pinctls
+ if self.origin_pinctls == None:
+ self.origin_pinctls = pinctls
+ self.pinctl = []
+ for name in PIN_WIDGET_CONTROL_BITS:
+ bit = PIN_WIDGET_CONTROL_BITS[name]
+ if pinctls & (1 << bit): self.pinctl.append(name)
+ self.pinctl_vref = None
+ if self.pincap_vref:
+ self.pinctl_vref = PIN_WIDGET_CONTROL_VREF[pinctls & 0x07]
+
+ def pin_widget_control_set_value(self, name, value):
+ if name in PIN_WIDGET_CONTROL_BITS:
+ mask = 1 << PIN_WIDGET_CONTROL_BITS[name]
+ value = value and True or False
+ changed = (self.pinctls & mask) and not value or value
+ if value:
+ self.pinctls |= mask
+ else:
+ self.pinctls &= ~mask
+ elif name == 'vref' and self.pincap_vref:
+ idx = PIN_WIDGET_CONTROL_VREF.index(value)
+ changed = (self.pinctls & 0x07) != idx
+ self.pinctls &= ~0x07
+ self.pinctls |= idx
+ self.codec.rw(self.nid, VERBS['SET_PIN_WIDGET_CONTROL'], self.pinctls)
+ self.reread_pin_widget_control()
+ return changed
+
+ def reread_vol_knb(self):
+ cap = self.codec.rw(self.nid, VERBS['GET_VOLUME_KNOB_CONTROL'], 0)
+ self.vol_knb = cap
+ if self.origin_vol_knb == None:
+ self.origin_vol_knb = cap
+ self.vol_knb_direct = (cap >> 7) & 1
+ self.vol_knb_val = cap & 0x7f
+
+ def vol_knb_set_value(self, name, value):
+ if name == 'direct':
+ value = value and True or False
+ changed = (self.vol_knb & (1 << 7)) and not value or value
+ if value:
+ self.vol_knb |= (1 << 7)
+ else:
+ self.vol_knb &= ~(1 << 7)
+ elif name == 'value':
+ changed = (self.vol_knb & 0x7f) != value
+ self.vol_knb &= ~0x7f
+ self.vol_knb |= value
+ self.codec.rw(self.nid, VERBS['SET_VOLUME_KNOB_CONTROL'], self.vol_knb)
+ self.reread_vol_knb()
+ return changed
+
+ def reread_sdi_select(self):
+ self.sdi_select = None
+ if self.wtype_id == 'AUD_IN' and self.aud_channel == 0:
+ sdi = self.codec.rw(self.nid, VERBS['GET_SDI_SELECT'], 0)
+ self.sdi_select = sdi & 0x0f
+ if self.origin_sdi_select == None:
+ self.origin_sdi_select = sdi
+
+ def sdi_select_set_value(self, value):
+ changed = False
+ if self.sdi_select != None:
+ changed = (self.sdi_select & 0x0f) != value
+ self.sdi_select = value & 0x0f
+ self.codec.rw(self.nid, VERBS['SET_SDI_SELECT'], self.sdi_select)
+ self.reread_sdi_select()
+ return changed
+
+ def reread_dig1(self):
+ self.dig1 = []
+ self.dig1_category = None
+ if not self.digital:
+ return
+ digi1 = self.codec.rw(self.nid, VERBS['GET_DIGI_CONVERT_1'], 0)
+ self.digi1 = digi1
+ if self.origin_digi1 == None:
+ self.origin_digi1 = digi1
+ for name in DIG1_BITS:
+ bit = DIG1_BITS[name]
+ if digi1 & (1 << bit): self.dig1.append(name)
+ self.dig1_category = (digi1 >> 8) & 0x7f
+
+ def dig1_set_value(self, name, value):
+ if name == 'category':
+ changed = ((self.digi1 >> 8) & 0x7f) != (value & 0x7f)
+ self.digi1 &= ~0x7f00
+ self.digi1 |= (value & 0x7f) << 8
+ self.codec.rw(self.nid, VERBS['SET_DIGI_CONVERT_2'], (self.digi1 >> 8) & 0xff)
+ else:
+ mask = 1 << DIG1_BITS[name]
+ value = value and True or False
+ changed = (self.digi1 & mask) and not value or value
+ if value:
+ self.digi1 |= mask
+ else:
+ self.digi1 &= ~mask
+ self.codec.rw(self.nid, VERBS['SET_DIGI_CONVERT_1'], self.digi1 & 0xff)
+ self.reread_dig1()
+ return changed
+
+ def revert(self):
+ if self.origin_active_connection != None:
+ self.set_active_connection(self.origin_active_connection)
+ if self.origin_pwr != None:
+ self.codec.rw(self.nid, VERBS['SET_POWER_STATE'], self.origin_pwr)
+ if self.in_amp:
+ self.amp_vals_in.revert()
+ if self.out_amp:
+ self.amp_vals_out.revert()
+ if self.origin_pincap_eapdbtls != None:
+ self.codec.rw(self.nid, VERBS['SET_EAPD_BTLENABLE'], self.origin_pincap_eapdbtls)
+ if self.origin_vol_knb != None:
+ self.codec.rw(self.nid, VERBS['SET_VOLUME_KNOB_CONTROL'], self.origin_vol_knb)
+ if self.origin_sdi_select != None:
+ self.codec.rw(self.nid, VERBS['SET_SDI_SELECT'], self.origin_sdi_select)
+ if self.origin_digi1 != None:
+ self.codec.rw(self.nid, VERBS['SET_DIGI_CONVERT_1'], self.origin_digi1 & 0xff)
+ self.codec.rw(self.nid, VERBS['SET_DIGI_CONVERT_2'], (self.origin_digi1 >> 8) & 0xff)
+ self.reread()
+
+ def get_device(self):
+ return self.codec.get_device(self.nid)
+
+ def get_controls(self):
+ return self.codec.get_controls(self.nid)
+
+ def get_mixercontrols(self):
+ if not self.codec.mixer:
+ return []
+ ctls = self.get_controls()
+ res = []
+ for ctl in ctls:
+ id = AlsaMixerElemId(name=ctl.name, index=ctl.index, device=ctl.device)
+ e = AlsaMixerElem(self.codec.mixer, id)
+ e.hdactl = ctl
+ res.append(e)
+ return res
+
+ def get_conn_amp_vals_str(self, dst_node):
+ # return amp values for connection between this and dst_node
+ res = []
+ if self.out_amp:
+ res.append(self.amp_vals_out.get_val_str(0))
+ else:
+ res.append(None)
+ if dst_node.in_amp:
+ idx = 0
+ if dst_node.amp_vals_in.indices == dst_node.connections:
+ if not self.nid in dst_node.connections:
+ raise ValueError, "nid 0x%02x is not connected to nid 0x%02x (%s, %s)" % (dst_node.nid, self.nid, repr(self.connections), repr(dst_node.connections))
+ idx = dst_node.connections.index(self.nid)
+ res.append(dst_node.amp_vals_in.get_val_str(idx))
+ else:
+ res.append(None)
+ return res
+
+ def is_conn_active(self, dst_node):
+ # disabled route for PIN widgets
+ if self.wtype_id == 'PIN' and not 'IN' in self.pinctl:
+ return None
+ if dst_node.wtype_id == 'PIN' and not 'OUT' in dst_node.pinctl:
+ return None
+ res = [None, None]
+ if self.out_amp:
+ vals = self.amp_vals_out.get_val_db(0)
+ for idx in range(len(vals)):
+ if res[idx]:
+ res[idx] += vals[idx]
+ else:
+ res[idx] = vals[idx]
+ if dst_node.in_amp:
+ idx = 0
+ if dst_node.amp_vals_in.indices == dst_node.connections:
+ if not self.nid in dst_node.connections:
+ raise ValueError, "nid 0x%02x is not connected to nid 0x%02x (%s, %s)" % (dst_node.nid, self.nid, repr(self.connections), repr(dst_node.connections))
+ idx = dst_node.connections.index(self.nid)
+ vals = dst_node.amp_vals_in.get_val_db(idx)
+ for idx in range(len(vals)):
+ if res[idx]:
+ res[idx] += vals[idx]
+ else:
+ res[idx] = vals[idx]
+ if res[0] is None and res[1] is None:
+ return True
+ for r in res:
+ if r >= -1200:
+ return True
+ return False
+
+class HDAGPIO:
+
+ def __init__(self, codec, nid):
+ self.codec = codec
+ self.nid = nid
+ self.originval = None
+ self.reread()
+
+ def reread(self):
+ self.val = {}
+ for i in GPIO_IDS:
+ self.val[i] = self.codec.rw(self.nid, GPIO_IDS[i][0], 0)
+ if self.originval == None:
+ self.originval = self.val.copy()
+
+ def test(self, name, bit):
+ return (self.val[name] & (1 << bit)) and True or False
+
+ def read(self, name):
+ self.val[name] = self.codec.rw(self.nid, GPIO_IDS[name][0], 0)
+
+ def write(self, name):
+ self.codec.rw(self.nid, GPIO_IDS[name][1], self.val[name])
+ self.read(name)
+
+ def set(self, name, bit, val):
+ old = self.test(name, bit)
+ if val:
+ self.val[name] |= 1 << bit
+ else:
+ self.val[name] &= ~(1 << bit)
+ if old == self.test(name, bit):
+ return False
+ self.write(name)
+ return True
+
+ def revert(self):
+ for i in GPIO_IDS:
+ self.val[i] = self.originval[i]
+ self.write(i)
+
+class HDACard:
+
+ def __init__(self, card, ctl_fd=None):
+ self.card = card
+ if ctl_fd is None:
+ self.fd = ctl_fd = os.open("/dev/snd/controlC%i" % card, os.O_RDONLY)
+ else:
+ self.fd = os.dup(ctl_fd)
+ info = struct.pack('ii16s16s32s80s16s80s128s', 0, 0, '', '', '', '', '', '', '')
+ res = ioctl(ctl_fd, CTL_IOCTL_CARD_INFO, info)
+ a = struct.unpack('ii16s16s32s80s16s80s128s', res)
+ self.id = a[2].replace('\x00', '')
+ self.driver = a[3].replace('\x00', '')
+ self.name = a[4].replace('\x00', '')
+ self.longname = a[5].replace('\x00', '')
+ self.components = a[8].replace('\x00', '')
+
+ def __del__(self):
+ if not self.fd is None:
+ os.close(self.fd)
+
+class HDACodec:
+
+ afg = None
+ mfg = None
+ vendor_id = None
+ subsystem_id = None
+ revision_id = None
+
+ def __init__(self, card=0, device=0, clonefd=None):
+ self.fd = None
+ self.hwaccess = True
+ ctl_fd = None
+ if type(1) == type(card):
+ self.device = device
+ self.card = card
+ self.mcard = HDACard(card)
+ ctl_fd = self.mcard.fd
+ else:
+ self.device = device
+ self.mcard = card
+ self.card = card.card
+ if not clonefd:
+ self.fd = os.open("/dev/snd/hwC%sD%s" % (card, device), os.O_RDWR)
+ else:
+ self.fd = os.dup(clonefd)
+ info = struct.pack('Ii64s80si64s', 0, 0, '', '', 0, '')
+ res = ioctl(self.fd, IOCTL_INFO, info)
+ name = struct.unpack('Ii64s80si64s', res)[3]
+ if not name.startswith('HDA Codec'):
+ raise IOError, "unknown HDA hwdep interface"
+ res = ioctl(self.fd, IOCTL_PVERSION, struct.pack('I', 0))
+ self.version = struct.unpack('I', res)
+ if self.version < 0x00010000: # 1.0.0
+ raise IOError, "unknown HDA hwdep version"
+ self.mixer = AlsaMixer(self.card, ctl_fd=ctl_fd)
+ self.parse_proc()
+
+ def __del__(self):
+ if not self.fd is None:
+ os.close(self.fd)
+
+ def rw(self, nid, verb, param):
+ """do elementary read/write operation"""
+ verb = (nid << 24) | (verb << 8) | param
+ res = ioctl(self.fd, IOCTL_VERB_WRITE, struct.pack('II', verb, 0))
+ return struct.unpack('II', res)[1]
+
+ def get_wcap(self, nid):
+ """get cached widget capabilities"""
+ res = ioctl(self.fd, IOCTL_GET_WCAPS, struct.pack('II', nid << 24, 0))
+ return struct.unpack('II', res)[1]
+
+ def get_raw_wcap(self, nid):
+ """get raw widget capabilities"""
+ return self.rw(nid, VERBS['PARAMETERS'], PARAMS['AUDIO_WIDGET_CAP'])
+
+ def param_read(self, nid, param):
+ """read perameters"""
+ return self.rw(nid, VERBS['PARAMETERS'], param)
+
+ def get_sub_nodes(self, nid):
+ """return sub-nodes count (returns count and start NID)"""
+ res = self.param_read(nid, PARAMS['NODE_COUNT'])
+ return res & 0x7fff, (res >> 16) & 0x7fff
+
+ def get_connections(self, nid):
+ """parses connection list and returns the array of NIDs"""
+ parm = self.param_read(nid, PARAMS['CONNLIST_LEN'])
+ if parm & (1 << 7): # long
+ shift = 16
+ num_elems = 2
+ else: # short
+ shift = 8
+ num_elems = 4
+ conn_len = parm & 0x7f
+ mask = (1 << (shift - 1)) - 1
+ if not conn_len:
+ return None
+ if conn_len == 1:
+ parm = self.rw(nid, VERBS['GET_CONNECT_LIST'], 0)
+ return [parm & mask]
+ res = []
+ prev_nid = 0
+ for i in range(conn_len):
+ if i % num_elems == 0:
+ parm = self.rw(nid, VERBS['GET_CONNECT_LIST'], i)
+ range_val = parm & (1 << (shift - 1))
+ val = parm & mask
+ parm >>= shift
+ if range_val:
+ if not prev_nid or prev_nid >= val:
+ raise IOError, "invalid dep_range_val 0x%x:0x%x\n" % (prev_nid, val)
+ n = prev_nid + 1
+ while n <= val:
+ res.append(n)
+ n += 1
+ else:
+ res.append(val)
+ prev_nid = val
+ return res
+
+ def revert(self):
+ if not self.gpio is None:
+ self.gpio.revert()
+ for nid in self.nodes:
+ self.nodes[nid].revert()
+
+ def get_node(self, nid):
+ if nid == self.afg:
+ return HDARootNode(self, "Audio Root Node")
+ return self.nodes[nid]
+
+ def parse_proc(self):
+ from hda_proc import HDACodecProc, DecodeProcFile
+ file = "/proc/asound/card%i/codec#%i" % (self.card, self.device)
+ if os.path.exists(file):
+ file = DecodeProcFile(file)
+ self.proc_codec = HDACodecProc(self.card, self.device, file)
+ else:
+ self.proc_codec = None
+ print "Unable to find proc file '%s'" % file
+
+ def analyze(self):
+ self.afg = None
+ self.mfg = None
+ self.nodes = {}
+ self.gpio = None
+ self.afg_function_id = 0 # invalid
+ self.mfg_function_id = 0 # invalid
+ self.afg_unsol = 0
+ self.mfg_unsol = 0
+ self.vendor_id = self.param_read(AC_NODE_ROOT, PARAMS['VENDOR_ID'])
+ self.subsystem_id = self.param_read(AC_NODE_ROOT, PARAMS['SUBSYSTEM_ID'])
+ self.revision_id = self.param_read(AC_NODE_ROOT, PARAMS['REV_ID'])
+ self.name = "0x%08x" % self.vendor_id # FIXME
+ self.pcm_rates = []
+ self.pcm_bits = []
+ self.pcm_streams = []
+ self.amp_caps_in = None
+ self.amp_caps_out = None
+ self.gpio_max = 0
+ self.gpio_o = 0
+ self.gpio_i = 0
+ self.gpio_unsol = 0
+ self.gpio_wake = 0
+
+ total, nid = self.get_sub_nodes(AC_NODE_ROOT)
+ for i in range(total):
+ func = self.param_read(nid, PARAMS['FUNCTION_TYPE'])
+ if (func & 0xff) == 0x01: # audio group
+ self.afg_function_id = func & 0xff
+ self.afg_unsol = (func & 0x100) and True or False
+ self.afg = nid
+ elif (func & 0xff) == 0x02: # modem group
+ self.mfg_function_id = func & 0xff
+ self.mfg_unsol = (func & 0x100) and True or False
+ self.mfg = nid
+ else:
+ break
+ nid += 1
+
+ if self.subsystem_id == 0:
+ self.subsystem_id = self.rw(self.afg and self.afg or self.mfg,
+ VERBS['GET_SUBSYSTEM_ID'], 0)
+
+ # parse only audio function group
+ if self.afg == None:
+ return
+
+ pcm = self.param_read(self.afg, PARAMS['PCM'])
+ self.pcm_rate = pcm & 0xffff
+ self.pcm_rates = self.analyze_pcm_rates(self.pcm_rate)
+ self.pcm_bit = pcm >> 16
+ self.pcm_bits = self.analyze_pcm_bits(self.pcm_bit)
+
+ self.pcm_stream = self.param_read(self.afg, PARAMS['STREAM'])
+ self.pcm_streams = self.analyze_pcm_streams(self.pcm_stream)
+
+ self.amp_caps_in = HDAAmpCaps(self, self.afg, HDA_INPUT)
+ self.amp_caps_out = HDAAmpCaps(self, self.afg, HDA_OUTPUT)
+
+ self.gpio_cap = self.param_read(self.afg, PARAMS['GPIO_CAP'])
+ self.gpio_max = self.gpio_cap & 0xff
+ self.gpio_o = (self.gpio_cap >> 8) & 0xff
+ self.gpio_i = (self.gpio_cap >> 16) & 0xff
+ self.gpio_unsol = (self.gpio_cap >> 30) & 1 and True or False
+ self.gpio_wake = (self.gpio_cap >> 31) & 1 and True or False
+ self.gpio = HDAGPIO(self, self.afg)
+
+ nodes_count, nid = self.get_sub_nodes(self.afg)
+ self.base_nid = nid
+ for i in range(nodes_count):
+ self.nodes[nid] = HDANode(self, nid)
+ nid += 1
+
+ def reread(self):
+ if not self.gpio is None:
+ self.gpio.reread()
+ for node in self.nodes:
+ self.nodes[node].reread()
+
+ def analyze_pcm_rates(self, pcm):
+ rates = [8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 384000]
+ res = []
+ for i in range(len(rates)):
+ if pcm & (1 << i):
+ res.append(rates[i])
+ return res
+
+ def analyze_pcm_bits(self, bit):
+ bits = [8, 16, 20, 24, 32]
+ res = []
+ for i in range(len(bits)):
+ if bit & (1 << i):
+ res.append(bits[i])
+ return res
+
+ def analyze_pcm_streams(self, stream):
+ res = []
+ if stream & 0x01: res.append("PCM")
+ if stream & 0x02: res.append("FLOAT")
+ if stream & 0x04: res.append("AC3")
+ return res
+
+ def dump(self, skip_nodes=False):
+
+ def print_pcm_rates(node):
+ s = ''
+ for i in node.pcm_rates:
+ s += " %d" % i
+ return " rates [0x%x]:%s\n" % (node.pcm_rate, s)
+
+ def print_pcm_bits(node):
+ s = ''
+ for i in node.pcm_bits:
+ s += " %d" % i
+ return " bits [0x%x]:%s\n" % (node.pcm_bit, s)
+
+ def print_pcm_formats(node):
+ str = " formats [0x%x]:" % node.pcm_stream
+ for i in node.pcm_streams:
+ str += " %s" % i
+ return str + "\n"
+
+ def print_pcm_caps(node):
+ str = print_pcm_rates(node)
+ str += print_pcm_bits(node)
+ return str + print_pcm_formats(node)
+
+ def print_gpio(node):
+ gpio = node.gpio_cap
+ str = 'GPIO: io=%d, o=%d, i=%d, unsolicited=%d, wake=%d\n' % \
+ (node.gpio_max, node.gpio_o, node.gpio_i,
+ node.gpio_unsol and 1 or 0, node.gpio_wake and 1 or 0)
+ for i in range(node.gpio_max):
+ str += ' IO[%d]: enable=%d, dir=%d, wake=%d, sticky=%d, ' \
+ 'data=%d, unsol=%d\n' % (i,
+ node.gpio.test('enable', i) and 1 or 0,
+ node.gpio.test('direction', i) and 1 or 0,
+ node.gpio.test('wake', i) and 1 or 0,
+ node.gpio.test('sticky', i) and 1 or 0,
+ node.gpio.test('data', i) and 1 or 0,
+ node.gpio.test('unsol', i) and 1 or 0)
+ return str
+
+ def print_amp_caps(caps):
+ if caps.ofs == None:
+ return "N/A\n"
+ return "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n" % \
+ (caps.ofs, caps.nsteps, caps.stepsize, caps.mute and 1 or 0)
+
+ if not self.afg and not self.mfg:
+ self.analyze()
+ str = 'Codec: %s\n' % self.name
+ str += 'Address: %i\n' % self.device
+ if not self.afg is None:
+ str += 'AFG Function Id: 0x%x (unsol %u)\n' % (self.afg_function_id, self.afg_unsol)
+ if not self.mfg is None:
+ str += 'MFG Function Id: 0x%x (unsol %u)\n' % (self.mfg_function_id, self.mfg_unsol)
+ str += 'Vendor Id: 0x%x\n' % self.vendor_id
+ str += 'Subsystem Id: 0x%x\n' % self.subsystem_id
+ str += 'Revision Id: 0x%x\n' % self.revision_id
+ if self.mfg:
+ str += 'Modem Function Group: 0x%x\n' % self.mfg
+ else:
+ str += 'No Modem Function Group found\n'
+ if self.afg is None: return str
+ str += 'Default PCM:\n'
+ str += print_pcm_caps(self)
+ str += 'Default Amp-In caps: '
+ str += print_amp_caps(self.amp_caps_in)
+ str += 'Default Amp-Out caps: '
+ str += print_amp_caps(self.amp_caps_out)
+
+ if self.base_nid == 0 or self.nodes < 0:
+ str += 'Invalid AFG subtree\n'
+ return str
+
+ str += print_gpio(self)
+
+ if not skip_nodes:
+ for i in self.nodes:
+ str += self.dump_node(self.nodes[i])
+
+ return str
+
+ def dump_node(self, node):
+
+ def print_pcm_rates(node):
+ s = ''
+ for i in node.pcm_rates:
+ s += " %d" % i
+ return " rates [0x%x]:%s\n" % (node.pcm_rate, s)
+
+ def print_pcm_bits(node):
+ s = ''
+ for i in node.pcm_bits:
+ s += " %d" % i
+ return " bits [0x%x]:%s\n" % (node.pcm_bit, s)
+
+ def print_pcm_formats(node):
+ str = " formats [0x%x]:" % node.pcm_stream
+ for i in node.pcm_streams:
+ str += " %s" % i
+ return str + "\n"
+
+ def print_pcm_caps(node):
+ str = print_pcm_rates(node)
+ str += print_pcm_bits(node)
+ return str + print_pcm_formats(node)
+
+ def print_audio_io(node):
+ str = " Converter: stream=%d, channel=%d\n" % (node.aud_stream, node.aud_channel)
+ if node.sdi_select != None:
+ str += " SDI-Select: %d\n" % node.sdi_select
+ return str
+
+ def print_amp_caps(caps):
+ if caps.ofs == None:
+ return "N/A\n"
+ return "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n" % \
+ (caps.ofs, caps.nsteps, caps.stepsize, caps.mute and 1 or 0)
+
+ def print_amp_vals(vals):
+ str = ''
+ idx = 0
+ for val in vals.vals:
+ if vals.stereo and (idx & 1) == 0:
+ str += " [0x%02x" % val
+ else:
+ str += " 0x%02x" % val
+ if vals.stereo and (idx & 1) != 0: str += "]"
+ idx += 1
+ return str + '\n'
+
+ def print_pin_caps(node):
+ str = " Pincap 0x%08x:" % node.pincaps
+ if 'IN' in node.pincap: str += " IN"
+ if 'OUT' in node.pincap: str += " OUT"
+ if 'HP_DRV' in node.pincap: str += " HP"
+ if 'EAPD' in node.pincap: str += " EAPD"
+ if 'PRES_DETECT' in node.pincap: str += " Detect"
+ if 'BALANCE' in node.pincap: str += " Balance"
+ if 'HDMI' in node.pincap:
+ if (self.vendor_id >> 16) == 0x10ec: # Realtek has different meaning
+ str += " (Realtek)R/L"
+ else:
+ if 'HBR' in node.pincap:
+ str += " HBR"
+ str += " HDMI"
+ if 'DP' in node.pincap: str += " DP"
+ if 'TRIG_REQ' in node.pincap: str += " Trigger"
+ if 'IMP_SENSE' in node.pincap: str += " ImpSense"
+ str += '\n'
+ if node.pincap_vref:
+ str += " Vref caps:"
+ if 'HIZ' in node.pincap_vref: str += " HIZ"
+ if '50' in node.pincap_vref: str += " 50"
+ if 'GRD' in node.pincap_vref: str += " GRD"
+ if '80' in node.pincap_vref: str += " 80"
+ if '100' in node.pincap_vref: str += " 100"
+ str += '\n'
+ if 'EAPD' in node.pincap:
+ str += " EAPD 0x%x:" % node.pincap_eapdbtls
+ if 'BALANCED' in node.pincap_eapdbtl: str += " BALANCED"
+ if 'EAPD' in node.pincap_eapdbtl: str += " EAPD"
+ if 'LR_SWAP' in node.pincap_eapdbtl: str += " R/L"
+ str += '\n'
+ str += " Pin Default 0x%08x: [%s] %s at %s %s\n" % (node.defcfg_pincaps,
+ node.jack_conn_name,
+ node.jack_type_name,
+ node.jack_location_name,
+ node.jack_location2_name)
+ str += " Conn = %s, Color = %s\n" % (node.jack_connector_name,
+ node.jack_color_name)
+ str += " DefAssociation = 0x%x, Sequence = 0x%x\n" % \
+ (node.defcfg_assoc, node.defcfg_sequence)
+ if 'NO_PRESENCE' in node.defcfg_misc:
+ str += " Misc = NO_PRESENCE\n"
+ if node.pinctl:
+ str += " Pin-ctls: 0x%02x:" % node.pinctls
+ if 'IN' in node.pinctl: str += " IN"
+ if 'OUT' in node.pinctl: str += " OUT"
+ if 'HP' in node.pinctl: str += " HP"
+ if node.pincap_vref:
+ str += " VREF_%s" % node.pinctl_vref
+ str += '\n'
+ return str
+
+ def print_vol_knob(node):
+ str = " Volume-Knob: delta=%d, steps=%d, " % (node.vol_knb_delta, node.vol_knb_steps)
+ return str + "direct=%d, val=%d\n" % (node.vol_knb_direct, node.vol_knb_val)
+
+ def print_unsol_cap(node):
+ return " Unsolicited: tag=0x%02x, enabled=%d\n" % (node.unsol_tag, node.unsol_enabled and 1 or 0)
+
+ def print_power_state(node):
+ str = ""
+ if node.pwr_states:
+ str = " Power states: %s\n" % ' '.join(node.pwr_states)
+ return " Power: setting=%s, actual=%s\n" % (node.pwr_setting_name, node.pwr_actual_name)
+
+ def print_digital_conv(node):
+ str = " Digital:"
+ if 'ENABLE' in node.dig1: str += " Enabled"
+ if 'VALIDITY' in node.dig1: str += " Validity"
+ if 'VALIDITYCFG' in node.dig1: str += " ValidityCfg"
+ if 'EMPHASIS' in node.dig1: str += " Preemphasis"
+ if 'COPYRIGHT' in node.dig1: str += " Copyright"
+ if 'NONAUDIO' in node.dig1: str += " Non-Audio"
+ if 'PROFESSIONAL' in node.dig1: str += " Pro"
+ if 'GENLEVEL' in node.dig1: str += " GetLevel"
+ str += "\n"
+ return str + " Digital category: 0x%x\n" % ((node.dig1_category >> 8) & 0x7f)
+
+ def print_conn_list(node):
+ str = " Connection: %d\n" % (node.connections and len(node.connections) or 0)
+ if node.connections:
+ str += " "
+ for i in range(len(node.connections)):
+ str += " 0x%02x" % node.connections[i]
+ if i == node.active_connection and len(node.connections) > 1:
+ str += "*"
+ str += '\n'
+ return str
+
+ def print_proc_caps(node):
+ return " Processing caps: benign=%d, ncoeff=%d\n" % (node.proc_benign, node.proc_numcoef)
+
+ def print_realtek_coef(node):
+ str = " Processing Coefficient: 0x%02x\n" % node.realtek_coeff_proc
+ return str + " Coefficient Index: 0x%02x\n" % node.realtek_coeff_index
+
+ str = "Node 0x%02x [%s] wcaps 0x%x:" % (node.nid, node.wtype_name(), node.wcaps)
+ if node.stereo:
+ str += node.channels == 2 and " Stereo" or " %d-Channels" % node.channels
+ else:
+ str += " Mono"
+ if node.digital: str += " Digital"
+ if node.in_amp: str += " Amp-In"
+ if node.out_amp: str += " Amp-Out"
+ if node.stripe: str += " Stripe"
+ if node.lr_swap: str += " R/L"
+ if node.cp_caps: str += " CP"
+ str += '\n'
+ str += self.dump_node_extra(node)
+ if node.in_amp:
+ str += " Amp-In caps: "
+ str += print_amp_caps(node.amp_caps_in)
+ str += " Amp-In vals:"
+ str += print_amp_vals(node.amp_vals_in)
+ if node.out_amp:
+ str += " Amp-Out caps: "
+ str += print_amp_caps(node.amp_caps_out)
+ str += " Amp-Out vals:"
+ str += print_amp_vals(node.amp_vals_out)
+
+ if node.wtype_id == 'PIN':
+ str += print_pin_caps(node)
+ elif node.wtype_id == 'VOL_KNB':
+ str += print_vol_knob(node)
+ elif node.wtype_id in ['AUD_IN', 'AUD_OUT']:
+ str += print_audio_io(node)
+ if node.digital:
+ str += print_digital_conv(node)
+ if node.format_ovrd:
+ str += " PCM:\n"
+ str += print_pcm_caps(node)
+ if node.unsol_cap:
+ str += print_unsol_cap(node)
+ if node.power:
+ str += print_power_state(node)
+ if node.wdelay:
+ str += " Delay: %d samples\n" % node.wdelay
+ if node.conn_list:
+ str += print_conn_list(node)
+ if node.proc_wid:
+ str += print_proc_caps(node)
+ if hasattr(node, 'realtek_coeff_proc'):
+ str += print_realtek_coef(node)
+ return str
+
+ def dump_node_extra(self, node):
+ if self.proc_codec:
+ return self.proc_codec.dump_node_extra(node)
+ return ''
+
+ def get_device(self, nid):
+ if self.proc_codec:
+ return self.proc_codec.get_device(nid)
+ return None
+
+ def get_controls(self, nid):
+ if self.proc_codec:
+ 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, prefer_x=None, prefer_y=None):
+
+ 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:
+ return False
+ 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)
+ return False
+ return None
+
+ error = 0
+ res = []
+ unplaced = []
+ # determine all termination widgets
+ terms = {'AUD_IN':[], 'AUD_OUT':[], 'PIN_IN':[], 'PIN_OUT':[]}
+ mixes = 0
+ for nid in self.nodes:
+ node = self.nodes[nid]
+ if node.wtype_id == 'AUD_MIX':
+ mixes += 1
+ 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
+ if prefer_x:
+ x = prefer_x
+ else:
+ x = max(len(terms['AUD_IN']), len(terms['AUD_OUT'])) + 2
+ if prefer_y:
+ y = prefer_y
+ else:
+ y = max(len(terms['PIN_IN']), len(terms['PIN_OUT'])) + 2
+ if x <= 2 and y <= 2:
+ return None
+ while (x - 2) * (y - 2) < mixes * 9:
+ if x <= y:
+ x += 1
+ else:
+ y += 1
+ 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 and not error:
+ 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:
+ pl = doplace(conn, idx, idx1)
+ if pl is True:
+ unplaced.remove(conn)
+ elif pl is None:
+ error += 1
+ break
+ if error:
+ break
+ 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 not y or not x:
+ continue
+ pl = doplace(nid, y, x)
+ if pl is True:
+ unplaced.remove(nid)
+ break
+ elif pl is None:
+ error += 1
+ break
+ if error:
+ 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
+ if error:
+ return self.graph(dump=dump, prefer_x=x+2, prefer_y=y+2)
+ # 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 = []
+ for name in listdir('/dev/snd/'):
+ if name.startswith('controlC'):
+ try:
+ fd = os.open("/dev/snd/%s" % name, os.O_RDONLY)
+ except OSError, msg:
+ continue
+ info = struct.pack('ii16s16s32s80s16s80s128s', 0, 0, '', '', '', '', '', '', '')
+ res = ioctl(fd, CTL_IOCTL_CARD_INFO, info)
+ a = struct.unpack('ii16s16s32s80s16s80s128s', res)
+ card = a[0]
+ components = a[8].replace('\x00', '')
+ if components.find('HDA:') >= 0:
+ result.append(HDACard(card, ctl_fd=fd))
+ os.close(fd)
+ return result
+
+if __name__ == '__main__':
+ v = HDACodec()
+ v.analyze()
+ print "vendor_id = 0x%x, subsystem_id = 0x%x, revision_id = 0x%x" % (v.vendor_id, v.subsystem_id, v.revision_id)
+ print "afg = %s, mfg = %s" % (v.afg and "0x%x" % v.afg or 'None', v.mfg and "0x%x" % v.mfg or 'None')
+ print
+ print
+ print v.dump()[:-1]
--- /dev/null
+4#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+# 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 DummyScrollEvent:
+
+ def __init__(self, dir):
+ self.direction = dir
+
+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 longdesc(self):
+ return "0x%02x" % self.node.nid
+
+ 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.wronglines = []
+ self.analyze_routes(routes, nodes)
+ src_node.dst_routes.append(self)
+ dst_node.src_routes.append(self)
+ self.highlight = False
+ self.marked = False
+
+ def shortdesc(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 statusdesc(self):
+
+ def niceprint(prefix, val):
+ if val is None:
+ return prefix
+ return ' ' + prefix + ' ' + val
+
+ src = self.src.node
+ dst = self.dst.node
+ vals = src.get_conn_amp_vals_str(dst)
+ src = "%s 0x%02x" % (src.wtype_id.replace('_', '-'), src.nid)
+ dst = "%s 0x%02x" % (dst.wtype_id.replace('_', '-'), dst.nid)
+ res = niceprint("SRC " + src, vals[0]) + ' -> ' + \
+ niceprint("DST " + dst, vals[1])
+ return res
+
+ 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:
+ inactive = self.src.node.is_conn_active(self.dst.node)
+ if inactive is None:
+ cr.set_line_width(0.35)
+ cr.set_source_rgb(0, 0, 0)
+ elif inactive is False:
+ cr.set_line_width(0.35)
+ cr.set_source_rgb(0, 0, 1)
+ else:
+ cr.set_line_width(1.5)
+ cr.set_source_rgb(0, 0, 1)
+ cr.move_to(line[0], line[1])
+ cr.line_to(line[2], line[3])
+ cr.stroke()
+
+ for line in self.wronglines:
+ cr.set_line_width(1.5)
+ cr.set_source_rgb(1, 0, 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()
+ x1 = x + 5 + fixup
+ x2 = sel[0] - fixup
+ if x1 > x2:
+ sub = width/2
+ x1 = x + sub + fixup
+ sub -= 5
+ else:
+ sub = 0
+ for i in range(tryit*extra, (tryit+1)*extra-5-1, 5):
+ possible.append([x1, sel[1]+i, x2, sel[1]+i])
+ sel1 = self.select_line(routes, nodes, possible)
+ if sel1:
+ sel1[0] -= fixup + sub
+ 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.wronglines.append([x+5, y, sel[0], sel[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()
+ sub = width/2
+ x1 = x + sub + fixup
+ x2 = sel[2] - fixup
+ if x1 < x2:
+ x1 = x + 5 + fixup
+ sub = 0
+ else:
+ sub -= 5
+ for i in r:
+ possible.append([x1, sel[3]+i, x2, sel[3]+i])
+ sel1 = self.select_line(routes, nodes, possible)
+ if sel1:
+ sel1[0] -= fixup + sub
+ 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.wronglines.append([x+5, y, sel[2], sel[3]])
+ 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) < 3:
+ if y1 <= y and y2 >= y:
+ return True
+ elif y1 == y2 and abs(y1 - y) < 3:
+ 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, statusbar):
+ 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 |
+ gtk.gdk.LEAVE_NOTIFY_MASK | gtk.gdk.SCROLL_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.mouse_leave_handler = self.connect("leave-notify-event", self.mouse_leave)
+ self.scroll_me_handler = self.connect("scroll-event", self.scroll_me)
+
+ self.popup_win = None
+ self.popup = None
+ self.statusbar = statusbar
+
+ self.codec = codec
+ self.mytitle = mytitle
+ self.graph = codec.graph(dump=False)
+ self.startnode = None
+ self.endnode = None
+
+ self.changed_handler = HDA_SIGNAL.connect("hda-node-changed", self.hda_node_changed)
+
+ 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)
+ total = 0
+ 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:
+ total += 1
+ break
+ total *= 2
+ total += 1
+ position = 0
+ 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)
+ position += 1
+ self.pdialog.set_fraction(float(position) / total)
+ break
+ res = True
+ for route in self.routes:
+ if not route.finish(self.routes, self.nodes):
+ res = False
+ break
+ position += 1
+ self.pdialog.set_fraction(float(position) / total)
+ if not res:
+ return
+ # final step - optimize drawings
+ while 1:
+ size = self.compressx(sx)
+ if not size:
+ break
+ sx -= size
+ position += 1
+ self.pdialog.set_fraction(float(position) / total)
+ while 1:
+ size = self.compressy(sy)
+ if not size:
+ break
+ sy -= size
+ self.set_size(sx, sy)
+ return res
+
+ def build(self, extra=50):
+ self.pdialog = SimpleProgressDialog("Rendering routes")
+ self.pdialog.show_all()
+ res = self.__build(extra)
+ self.pdialog.destroy()
+ self.pdialog = None
+ 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 hda_node_changed(self, obj, widget, node):
+ if widget != self:
+ self.queue_draw()
+
+ 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()
+ screen_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)
+ self.popup_win.move(screen_width + 10, screen_height + 10)
+ self.popup_win.show_all()
+ popup_width, popup_height = self.popup_win.get_size()
+
+ #rootwin = self.get_screen().get_root_window()
+ #x, y, mods = rootwin.get_pointer()
+
+ pos_x = screen_width - popup_width
+ if pos_x < 0:
+ pos_x = 0
+ pos_y = screen_height - popup_height
+ if pos_y < 0:
+ pox_y = 0
+
+ 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
+ redraw = False
+ found = False
+ for route in self.routes:
+ if route.highlight:
+ redraw = True
+ route.highlight = False
+ for node in self.nodes:
+ if node.mouse_move(event.x, event.y, self):
+ self.statusbar.pop(1)
+ self.statusbar.push(1, node.longdesc())
+ found = redraw = True
+ break
+ if not found:
+ for route in self.routes:
+ if route.mouse_move(event.x, event.y, self):
+ self.statusbar.pop(1)
+ self.statusbar.push(1, route.statusdesc())
+ found = redraw = True
+ break
+ if not found:
+ self.statusbar.pop(1)
+ if redraw:
+ self.queue_draw()
+ if self.popup:
+ if oldpopup != self.popup:
+ self.show_popup(event)
+ else:
+ if self.popup_win:
+ self.popup_win.destroy()
+ self.popup_win = None
+
+ def mouse_leave(self, widget, data=None):
+ for route in self.routes:
+ if route.highlight:
+ redraw = True
+ route.highlight = False
+ if self.popup_win:
+ self.popup_win.destroy()
+ self.popup_win = None
+
+ 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):
+ TRACKER.close(node.win)
+ 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
+ TRACKER.add(win)
+ else:
+ node.win.present()
+
+ def button_click(self, widget, event):
+ if event.button != 3:
+ if event.button == 8:
+ self.scroll_me(self, DummyScrollEvent(gtk.gdk.SCROLL_LEFT))
+ elif event.button == 9:
+ self.scroll_me(self, DummyScrollEvent(gtk.gdk.SCROLL_RIGHT))
+ 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
+
+ def scroll_me(self, widget, event):
+ if event.direction == gtk.gdk.SCROLL_UP:
+ adj = self.get_vadjustment()
+ adj.set_value(adj.get_value()-40)
+ elif event.direction == gtk.gdk.SCROLL_DOWN:
+ adj = self.get_vadjustment()
+ adj.set_value(adj.get_value()+40)
+ elif event.direction == gtk.gdk.SCROLL_LEFT:
+ adj = self.get_hadjustment()
+ adj.set_value(adj.get_value()-40)
+ elif event.direction == gtk.gdk.SCROLL_RIGHT:
+ adj = self.get_hadjustment()
+ adj.set_value(adj.get_value()+40)
+
+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, 3, False)
+ self.add(table)
+
+ statusbar = gtk.Statusbar()
+ self.layout = CodecGraphLayout(None, None, codec, self.get_title(), statusbar)
+ 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)
+ table.attach(statusbar, 0, 2, 2, 3, gtk.FILL|gtk.SHRINK,
+ gtk.FILL|gtk.SHRINK, 0, 0)
+ self.show_all()
+ GRAPH_WINDOWS[codec] = self
+ TRACKER.add(self)
+
+ def __destroy(self, widget):
+ del GRAPH_WINDOWS[self.codec]
+ TRACKER.close(self)
+
+def create_graph(codec):
+ if codec in GRAPH_WINDOWS:
+ GRAPH_WINDOWS[codec].present()
+ else:
+ CodecGraph(codec)
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+# 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
+
+DIFF_FILE = "/tmp/hda-analyze.diff"
+
+CODEC_TREE = {}
+DIFF_TREE = {}
+
+def get_fixed_font():
+ return pango.FontDescription("Misc Fixed,Courier Bold 9")
+
+class HDASignal(gobject.GObject):
+
+ def __init__(self):
+ self.__gobject_init__()
+
+gobject.signal_new("hda-codec-changed", HDASignal,
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT))
+gobject.signal_new("hda-node-changed", HDASignal,
+ gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ (gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT))
+
+HDA_SIGNAL = HDASignal()
+
+def do_diff1(codec, diff1):
+ from difflib import unified_diff
+ diff = unified_diff(diff1.split('\n'), codec.dump().split('\n'), n=8, lineterm='')
+ diff = '\n'.join(list(diff))
+ if len(diff) > 0:
+ diff = 'Diff for codec %i/%i (%s):\n' % (codec.card, codec.device, codec.name) + diff
+ return diff
+
+def do_diff():
+ diff = ''
+ hw = 0
+ for card in CODEC_TREE:
+ for codec in CODEC_TREE[card]:
+ c = CODEC_TREE[card][codec]
+ if c.hwaccess:
+ hw += 1
+ diff += do_diff1(c, DIFF_TREE[card][codec])
+ if len(diff) > 0:
+ open(DIFF_FILE, "w+").write(diff + '\n')
+ print "Diff was stored to: %s" % DIFF_FILE
+ return (diff and hw > 0) and True or False
+
+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)
+ self.read_all = self.__read_all_none
+ self.node = None
+ self.codec = None
+ self.popups = []
+ self.tooltips = gtk.Tooltips()
+ 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.connect("destroy", self.__destroy)
+ self.codec_changed = HDA_SIGNAL.connect("hda-codec-changed", self.hda_codec_changed)
+ self.node_changed = HDA_SIGNAL.connect("hda-node-changed", self.hda_node_changed)
+ self.read_all()
+ self.show_all()
+
+ def __destroy(self, widget):
+ HDA_SIGNAL.handler_disconnect(self.codec_changed)
+ HDA_SIGNAL.handler_disconnect(self.node_changed)
+
+ def __read_all_none(self):
+ pass
+
+ def hda_codec_changed(self, obj, widget, codec):
+ if widget != self:
+ if self.read_all and self.codec == codec:
+ self.read_all()
+
+ def hda_node_changed(self, obj, widget, node):
+ if widget != self:
+ if self.read_all and self.node == node:
+ self.read_all()
+
+ def show_popup(self, text):
+ screen_width = gtk.gdk.screen_width()
+ screen_height = gtk.gdk.screen_height()
+
+ popup_win = gtk.Window(gtk.WINDOW_POPUP)
+ popup_win.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0xffff, 0xd700, 0))
+ frame = gtk.Frame()
+ popup_win.add(frame)
+ label = gtk.Label()
+ label.modify_font(get_fixed_font())
+ if text[-1] == '\n':
+ text = text[:-1]
+ label.set_text(text)
+ frame.add(label)
+ popup_win.move(screen_width + 10, screen_height + 10)
+ popup_win.show_all()
+ popup_width, popup_height = 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
+ if pos_x + popup_width > screen_width:
+ pos_x = screen_width - popup_width
+ pos_y = y + 16
+ if pos_y < 0:
+ pox_y = 0
+ if pos_y + popup_height > screen_height:
+ pos_y = screen_height - popup_height
+
+ popup_win.move(int(pos_x), int(pos_y))
+ return popup_win
+
+ def __popup_motion_notify(self, widget, event=None):
+ for popup in self.popups:
+ if popup[1] == widget and not popup[0]:
+ popup[0] = self.show_popup(popup[2](*popup[3]))
+
+ def __popup_leave_notify(self, widget, event=None):
+ for popup in self.popups:
+ if popup[1] == widget and popup[0]:
+ popup[0].destroy()
+ popup[0] = None
+
+ def make_popup(self, widget, gettext, data):
+ widget.connect("motion-notify-event", self.__popup_motion_notify)
+ widget.connect("leave-notify-event", self.__popup_leave_notify)
+ self.popups.append([None, widget, gettext, data])
+
+ 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]:
+ if node.set_active_connection(int(row)):
+ HDA_SIGNAL.emit("hda-node-changed", self, node)
+ 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
+ self.connection_model = model
+ 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 not node.active_connection is 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("Source 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()
+ if vals.set_mute(idx, val):
+ HDA_SIGNAL.emit("hda-node-changed", self, vals.node)
+ button.set_active(vals.vals[idx] & 0x80)
+
+ def __amp_value_changed(self, adj, data):
+ caps, vals, idx = data
+ val = int(adj.get_value())
+ if vals.set_value(idx, val):
+ HDA_SIGNAL.emit("hda-node-changed", self, vals.node)
+ adj.set_value(vals.vals[idx] & 0x7f)
+
+ def __ctl_mute_toggled(self, adj, data):
+ ctl, idx = data
+
+ def __ctl_value_changed(self, adj, data):
+ ctl, idx = data
+
+ def __popup_show_ctl(self, ctl, idx):
+ return ctl.get_text_info(idx - ctl.hdactl.amp_idx)
+
+ def __build_amps(self, node):
+
+ def build_caps(title, caps, vals):
+ if caps and caps.cloned:
+ 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
+ self.amp_checkbuttons[caps.dir] = []
+ self.amp_adjs[caps.dir] = []
+ self.mixer_elems[caps.dir] = []
+ ctls = node.get_mixercontrols()
+ 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.connect("toggled", self.__amp_mute_toggled, (caps, vals, idx))
+ self.amp_checkbuttons[caps.dir].append(checkbutton)
+ hbox.pack_start(checkbutton, False, False)
+ else:
+ self.amp_checkbuttons[caps.dir].append(None)
+ 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))
+ self.amp_adjs[caps.dir].append(adj)
+ hbox.pack_start(scale, True, True)
+ else:
+ self.amp_adjs[caps.dir].append(None)
+ sep = False
+ for ctl in ctls:
+ if ctl.hdactl.amp_index_match(idx):
+ if ctl.stype == 'boolean':
+ if not sep:
+ hbox.pack_start(gtk.VSeparator(), False, False)
+ hbox.pack_start(gtk.Label('CTLIFC'), False, False)
+ sep = True
+ checkbutton = gtk.CheckButton('Mute')
+ checkbutton.connect("toggled", self.__ctl_mute_toggled, (ctl, idx))
+ self.make_popup(checkbutton, self.__popup_show_ctl, (ctl, idx))
+ hbox.pack_start(checkbutton, False, False)
+ for ctl in ctls:
+ if ctl.hdactl.amp_index_match(idx):
+ if ctl.stype.startswith('integer'):
+ if not sep:
+ hbox.pack_start(gtk.VSeparator(), False, False)
+ sep = True
+ adj = gtk.Adjustment(0, ctl.min, ctl.max, ctl.step, ctl.step, ctl.step)
+ scale = gtk.HScale(adj)
+ scale.set_digits(0)
+ scale.set_value_pos(gtk.POS_RIGHT)
+ adj.connect("value_changed", self.__ctl_value_changed, (ctl, idx))
+ self.make_popup(scale, self.__popup_show_ctl, (ctl, 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
+
+ self.amp_checkbuttons = {}
+ self.amp_adjs = {}
+ self.mixer_elems = {}
+ 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
+ if node.eapdbtl_set_value(name, button.get_active()):
+ HDA_SIGNAL.emit("hda-node-changed", self, node)
+ button.set_active(name in node.pincap_eapdbtl)
+
+ def __pinctls_toggled(self, button, data):
+ node, name = data
+ if node.pin_widget_control_set_value(name, button.get_active()):
+ HDA_SIGNAL.emit("hda-node-changed", self, node)
+ 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:
+ if node.pin_widget_control_set_value('vref', name):
+ HDA_SIGNAL.emit("hda-node-changed", self, node)
+ 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)
+ self.pincap_eapdbtls_checkbuttons = []
+ for name in EAPDBTL_BITS:
+ checkbutton = gtk.CheckButton(name)
+ checkbutton.connect("toggled", self.__pincap_eapdbtl_toggled, (node, name))
+ self.pincap_eapdbtls_checkbuttons.append(checkbutton)
+ 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)
+ self.pin_checkbuttons = []
+ for name in PIN_WIDGET_CONTROL_BITS:
+ checkbutton = gtk.CheckButton(name)
+ checkbutton.connect("toggled", self.__pinctls_toggled, (node, name))
+ self.pin_checkbuttons.append(checkbutton)
+ vbox1.pack_start(checkbutton, False, False)
+ if node.pincap_vref:
+ combobox = gtk.combo_box_new_text()
+ for name in PIN_WIDGET_CONTROL_VREF:
+ if name:
+ combobox.append_text(name)
+ combobox.connect("changed", self.__pinctls_vref_change, node)
+ self.pincap_vref_combobox = combobox
+ 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())
+ if node.sdi_select_set_value(val):
+ HDA_SIGNAL.emit("hda-node-changed", self, node)
+ adj.set_value(node.sdi_select)
+
+ def __dig1_toggled(self, button, data):
+ node, name = data
+ val = button.get_active()
+ if node.dig1_set_value(name, val):
+ HDA_SIGNAL.emit("hda-node-changed", self, node)
+ 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
+ if node.dig1_set_value('category', val):
+ HDA_SIGNAL.emit("hda-node-changed", self, node)
+ 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)
+ self.sdi_select_adj = adj
+ 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)
+ self.digital_checkbuttons = []
+ for name in DIG1_BITS:
+ checkbutton = gtk.CheckButton(name)
+ checkbutton.connect("toggled", self.__dig1_toggled, (node, name))
+ self.digital_checkbuttons.append(checkbutton)
+ vbox1.pack_start(checkbutton, False, False)
+ frame.add(vbox1)
+ hbox1.pack_start(frame)
+ frame = gtk.Frame('Digital Converter Category')
+ entry = gtk.Entry()
+ self.dig_category_entry = entry
+ 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 __read_all_node(self):
+ node = self.node
+ if node.wtype_id in ['AUD_IN', 'AUD_OUT']:
+ if not node.sdi_select is None:
+ self.sdi_select_adj.set_value(node.sdi_select)
+ if node.digital:
+ idx = 0
+ for name in DIG1_BITS:
+ checkbutton = self.digital_checkbuttons[idx]
+ checkbutton.set_active(node.digi1 & (1 << DIG1_BITS[name]))
+ idx += 1
+ self.dig_category_entry.set_text("0x%x" % node.dig1_category)
+ elif node.wtype_id == 'PIN':
+ if 'EAPD' in node.pincap:
+ idx = 0
+ for name in EAPDBTL_BITS:
+ checkbutton = self.pincap_eapdbtls_checkbuttons[idx]
+ checkbutton.set_active(node.pincap_eapdbtls & (1 << EAPDBTL_BITS[name]))
+ idx += 1
+ idx = 0
+ for name in PIN_WIDGET_CONTROL_BITS:
+ checkbutton = self.pin_checkbuttons[idx]
+ checkbutton.set_active(node.pinctls & (1 << PIN_WIDGET_CONTROL_BITS[name]))
+ idx += 1
+ idx = active = 0
+ for name in PIN_WIDGET_CONTROL_VREF:
+ if name == node.pinctl_vref: active = idx
+ if name: idx += 1
+ if node.pincap_vref:
+ self.pincap_vref_combobox.set_active(active)
+ a = []
+ if node.in_amp:
+ a.append((HDA_INPUT, node.amp_caps_in, node.amp_vals_in))
+ if node.out_amp:
+ a.append((HDA_OUTPUT, node.amp_caps_out, node.amp_vals_out))
+ for dir, caps, vals in a:
+ for idx in range(len(vals.vals)):
+ val = vals.vals[idx]
+ checkbutton = self.amp_checkbuttons[dir][idx]
+ if checkbutton:
+ checkbutton.set_active(val & 0x80 and True or False)
+ adj = self.amp_adjs[dir][idx]
+ if adj:
+ adj.set_value((val & 0x7f) % (caps.nsteps+1))
+ idx += 1
+ if hasattr(self, 'connection_model'):
+ for r in self.connection_model:
+ r[0] = False
+ idx = 0
+ for r in self.connection_model:
+ r[0] = node.active_connection == idx
+ idx += 1
+
+ def __build_node(self, node, doframe=False):
+ self.node = node
+ 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)
+
+ self.read_all = self.__read_all_node
+
+ 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")
+ if codec.afg:
+ str += 'AFG Function Id: 0x%02x (unsol %u)\n' % (codec.afg_function_id, codec.afg_unsol)
+ str += 'Modem Fcn Group: %s\n' % (codec.mfg and "0x%02x" % codec.mfg or "N/A")
+ if codec.mfg:
+ str += 'MFG Function Id: 0x%02x (unsol %u)\n' % (codec.mfg_function_id, codec.mfg_unsol)
+ 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)):
+ if codec.gpio.set(id, idx, button.get_active()):
+ HDA_SIGNAL.emit("hda-codec-changed", self, codec)
+ 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)
+ self.gpio_checkbuttons = []
+ 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)
+ self.gpio_checkbuttons.append([])
+ for i in range(codec.gpio_max):
+ checkbutton = checkbutton = gtk.CheckButton('[%d]' % i)
+ checkbutton.connect("toggled", self.__gpio_toggled, (codec, id, i))
+ self.gpio_checkbuttons[-1].append(checkbutton)
+ vbox1.pack_start(checkbutton, False, False)
+ frame1.add(vbox1)
+ hbox.pack_start(frame1, False, False)
+ return frame
+
+ def __read_all_codec(self):
+ idx = 0
+ for id in GPIO_IDS:
+ for i in range(self.codec.gpio_max):
+ self.gpio_checkbuttons[idx][i].set_active(self.codec.gpio.test(id, i))
+ idx += 1
+
+ def __build_codec(self, codec, doframe=False):
+ self.codec = codec
+ 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)
+ self.read_all = self.__read_all_codec
+
+ 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)
+
+class SimpleProgressDialog(gtk.Dialog):
+
+ def __init__(self, title):
+ gtk.Dialog.__init__(self, title, None, gtk.DIALOG_MODAL, None)
+ self.set_deletable(False)
+
+ box = self.get_child()
+
+ box.pack_start(gtk.Label(), False, False, 0)
+ self.progressbar = gtk.ProgressBar()
+ box.pack_start(self.progressbar, False, False, 0)
+
+ def set_fraction(self, fraction):
+ self.progressbar.set_fraction(fraction)
+ while gtk.events_pending():
+ gtk.main_iteration_do(False)
+
+class TrackWindows:
+
+ def __init__(self):
+ self.windows = []
+
+ def add(self, win):
+ if not win in self.windows:
+ self.windows.append(win)
+
+ def close(self, win):
+ if win in self.windows:
+ self.windows.remove(win)
+ if not self.windows:
+ self.do_diff(win)
+ gtk.main_quit()
+
+ def do_diff(self, widget):
+ if do_diff():
+ dialog = gtk.MessageDialog(widget,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
+ "HDA-Analyzer: Would you like to revert\n"
+ "settings for all HDA codecs?")
+ response = dialog.run()
+ dialog.destroy()
+
+ if response == gtk.RESPONSE_YES:
+ for card in CODEC_TREE:
+ for codec in CODEC_TREE[card]:
+ CODEC_TREE[card][codec].revert()
+ print "Settings for all codecs were reverted..."
+
+TRACKER = TrackWindows()
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+# 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 os
+import struct
+from fcntl import ioctl
+
+def __ioctl_val1(val):
+ # workaround for OverFlow bug in python 2.4
+ if val & 0x80000000:
+ return -((val^0xffffffff)+1)
+ return val
+
+CTL_IOCTL_CARD_INFO = __ioctl_val1(0x81785501)
+CTL_IOCTL_ELEM_INFO = __ioctl_val1(0xc1105511)
+CTL_IOCTL_ELEM_READ = __ioctl_val1(0xc4c85512)
+CTL_IOCTL_ELEM_WRITE = __ioctl_val1(0xc4c85513)
+
+CTL_ELEM_TYPE_BOOLEAN = 1
+CTL_ELEM_TYPE_INTEGER = 2
+CTL_ELEM_TYPE_ENUMERATED = 3
+CTL_ELEM_TYPE_BYTES = 4
+CTL_ELEM_TYPE_IEC958 = 5
+CTL_ELEM_TYPE_INTEGER64 = 6
+
+CTL_ELEM_TYPEs = {
+ 'boolean': CTL_ELEM_TYPE_BOOLEAN,
+ 'integer': CTL_ELEM_TYPE_INTEGER,
+ 'enumerated': CTL_ELEM_TYPE_ENUMERATED,
+ 'bytes':CTL_ELEM_TYPE_BYTES,
+ 'iec958':CTL_ELEM_TYPE_IEC958,
+ 'integer64':CTL_ELEM_TYPE_INTEGER64
+}
+CTL_ELEM_RTYPEs = {}
+for i in CTL_ELEM_TYPEs:
+ CTL_ELEM_RTYPEs[CTL_ELEM_TYPEs[i]] = i
+
+CTL_ELEM_IFACE_MIXER = 2
+CTL_ELEM_IFACEs = {
+ "mixer": 2,
+}
+CTL_ELEM_RIFACEs = {}
+for i in CTL_ELEM_IFACEs:
+ CTL_ELEM_RIFACEs[CTL_ELEM_IFACEs[i]] = i
+
+CTL_ELEM_ACCESS_READ = (1<<0)
+CTL_ELEM_ACCESS_WRITE = (1<<1)
+CTL_ELEM_ACCESS_VOLATILE = (1<<2)
+CTL_ELEM_ACCESS_TIMESTAMP = (1<<3)
+CTL_ELEM_ACCESS_TLV_READ = (1<<4)
+CTL_ELEM_ACCESS_TLV_WRITE = (1<<5)
+CTL_ELEM_ACCESS_TLV_COMMAND = (1<<6)
+CTL_ELEM_ACCESS_INACTIVE = (1<<8)
+CTL_ELEM_ACCESS_LOCK = (1<<9)
+CTL_ELEM_ACCESS_OWNER = (1<<10)
+
+CTL_ELEM_ACCESSs = {
+ 'read': CTL_ELEM_ACCESS_READ,
+ 'write': CTL_ELEM_ACCESS_WRITE,
+ 'volatile': CTL_ELEM_ACCESS_VOLATILE,
+ 'timestamp': CTL_ELEM_ACCESS_TIMESTAMP,
+ 'tlv_read': CTL_ELEM_ACCESS_TLV_READ,
+ 'tlv_write': CTL_ELEM_ACCESS_TLV_WRITE,
+ 'tlv_command': CTL_ELEM_ACCESS_TLV_COMMAND,
+ 'inactive': CTL_ELEM_ACCESS_INACTIVE,
+ 'lock': CTL_ELEM_ACCESS_LOCK,
+ 'owner': CTL_ELEM_ACCESS_OWNER
+}
+
+UINTSIZE = len(struct.pack("I", 0))
+LONGSIZE = len(struct.pack("l", 0))
+LONGLONGSIZE = len(struct.pack("q", 0))
+
+class AlsaMixerElemId:
+
+ def __init__(self,
+ numid=0,
+ iface=CTL_ELEM_IFACE_MIXER,
+ device=0,
+ subdevice=0,
+ name=None,
+ index=0):
+ self.numid = numid
+ self.iface = iface
+ self.device = device
+ self.subdevice = subdevice
+ self.name = name
+ self.index = index
+ self.binsize = len(self.pack())
+
+ def pack(self):
+ return struct.pack('IiII44sI',
+ self.numid, self.iface, self.device, self.subdevice,
+ self.name, self.index)
+
+ def unpack(self, binid):
+ self.numid, self.iface, self.device, self.subdevice, \
+ self.name, self.index = struct.unpack('IiII44sI', binid)
+ self.name = self.name.replace('\x00', '')
+
+ def get_text_info(self):
+ return 'iface="%s",name="%s",index=%s,device=%s,subdevice=%s' % \
+ (CTL_ELEM_RIFACEs[self.iface], self.name, self.index,
+ self.device, self.subdevice)
+
+class AlsaMixerElem:
+
+ def __init__(self, mixer, id):
+ self.mixer = mixer
+ self.id = id
+ info = self.__info()
+ self.type = info['type']
+ self.stype = CTL_ELEM_RTYPEs[self.type]
+ self.access = info['access']
+ self.count = info['count']
+ self.owner = info['owner']
+ if info['type'] in [CTL_ELEM_TYPE_INTEGER, CTL_ELEM_TYPE_INTEGER64]:
+ self.min = info['min']
+ self.max = info['max']
+ self.step = info['step']
+ elif info['type'] == CTL_ELEM_TYPE_ENUMERATED:
+ self.items = info['items']
+ self.dimen = info['dimen']
+
+ def __info(self):
+ bin = self.id.pack()+struct.pack('iIIi128s8s64s', 0, 0, 0, 0, '', '', '')
+ res = ioctl(self.mixer.fd, CTL_IOCTL_ELEM_INFO, bin)
+ self.id.unpack(res[:self.id.binsize])
+ a = struct.unpack('iIIi128s8s64s', res[self.id.binsize:])
+ b = {}
+ b['id'] = self.id
+ b['type'] = a[0]
+ b['access'] = []
+ for i in CTL_ELEM_ACCESSs:
+ if CTL_ELEM_ACCESSs[i] & a[1]:
+ b['access'].append(i)
+ b['count'] = a[2]
+ b['owner'] = a[3]
+ if b['type'] == CTL_ELEM_TYPE_INTEGER:
+ b['min'], b['max'], b['step'] = \
+ struct.unpack("lll", a[4][:LONGSIZE*3])
+ elif b['type'] == CTL_ELEM_TYPE_INTEGER64:
+ b['min'], b['max'], b['step'] = \
+ struct.unpack("qqq", a[4][:LONGLONGSIZE*3])
+ elif b['type'] == CTL_ELEM_TYPE_ENUMERATED:
+ b['items'], b['item'], b['name'] = \
+ struct.unpack("II64s", a[4][:UINTSIZE*2+64])
+ b['dimen'] = struct.unpack("HHHH", a[5])
+ return b
+
+ def read(self):
+ bin = self.id.pack() + struct.pack('I512s128s', 0, '', '')
+ startoff = self.id.binsize + UINTSIZE
+ if LONGSIZE == 8:
+ bin += '\x00\x00\x00\x00'
+ startoff += 4
+ res = ioctl(self.mixer.fd, CTL_IOCTL_ELEM_READ, bin)
+ if self.type == CTL_ELEM_TYPE_BOOLEAN:
+ return map(lambda x: x != 0, struct.unpack("l"*self.count, res[startoff:startoff+self.count*LONGSIZE]))
+ elif self.type == CTL_ELEM_TYPE_INTEGER:
+ return struct.unpack("l"*self.count, res[startoff:startoff+self.count*LONGSIZE])
+ elif self.type == CTL_ELEM_TYPE_INTEGER64:
+ return struct.unpack("q"*self.count, res[startoff:startoff+self.count*LONGLONGSIZE])
+ elif self.type == CTL_ELEM_TYPE_ENUMERATED:
+ return struct.unpack("I"*self.count, res[startoff:startoff+self.count*UINTSIZE])
+ elif self.type == CTL_ELEM_TYPE_BYTES:
+ return res[startoff:startoff+self.count]
+ else:
+ raise ValueError, "Unsupported type %s" % CTL_ELEM_RTYPEs[self.type]
+
+ def get_text_info(self, idx=None):
+ res = self.id.get_text_info() + '\n'
+ res += ' type="%s",access=%s,count=%s,owner=%s,dimen=%s\n' % \
+ (self.stype, repr(self.access), self.count, self.owner, self.dimen)
+ if self.stype.startswith('integer'):
+ res += ' min=%s,max=%s,step=%s\n' % (self.min, self.max, self.step)
+ elif self.stype == 'enumerated':
+ res += ' items=%s\n' % (self.items)
+ return res
+
+class AlsaMixer:
+
+ def __init__(self, card, ctl_fd=None):
+ self.card = card
+ if ctl_fd is None:
+ self.fd = os.open("/dev/snd/controlC%s" % card, os.O_RDONLY)
+ else:
+ self.fd = os.dup(ctl_fd)
+
+ def __del__(self):
+ if not self.fd is None:
+ os.close(self.fd)
+
+if __name__ == '__main__':
+ mixer = AlsaMixer(0)
+ elem = AlsaMixerElem(mixer, AlsaMixerElemId(name="Mic Boost"))
+ print elem.read()
+ elem = AlsaMixerElem(mixer, AlsaMixerElemId(name="Capture Volume"))
+ print elem.read()
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+# 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.
+
+from hda_codec import *
+
+SET_VERBS = {
+ VERBS['SET_SDI_SELECT']: VERBS['GET_SDI_SELECT'],
+ VERBS['SET_PIN_WIDGET_CONTROL']: VERBS['GET_PIN_WIDGET_CONTROL'],
+ VERBS['SET_CONNECT_SEL']: VERBS['GET_CONNECT_SEL'],
+ VERBS['SET_EAPD_BTLENABLE']: VERBS['GET_EAPD_BTLENABLE'],
+ VERBS['SET_POWER_STATE']: VERBS['GET_POWER_STATE'],
+}
+
+def DecodeProcFile(proc_file):
+ if len(proc_file) < 256:
+ fd = open(proc_file)
+ proc_file = fd.read(1024*1024)
+ fd.close()
+ if proc_file.find('Subsystem Id:') < 0:
+ p = None
+ try:
+ from gzip import GzipFile
+ from StringIO import StringIO
+ s = StringIO(proc_file)
+ gz = GzipFile(mode='r', fileobj=s)
+ p = gz.read(1024*1024)
+ gz.close()
+ except:
+ pass
+ if p is None:
+ try:
+ from bz2 import decompress
+ p = decompress(proc_file)
+ except:
+ pass
+ if not p is None:
+ proc_file = p
+ return proc_file
+
+def DecodeAlsaInfoFile(proc_file):
+ if proc_file.find("ALSA Information Script") < 0:
+ return [proc_file]
+ pos = proc_file.find('HDA-Intel Codec information')
+ if pos < 0:
+ return [proc_file]
+ proc_file = proc_file[pos:]
+ pos = proc_file.find('--startcollapse--')
+ proc_file = proc_file[pos+18:]
+ pos = proc_file.find('--endcollapse--')
+ proc_file = proc_file[:pos]
+ res = []
+ while 1:
+ pos = proc_file.find('\nCodec: ')
+ if pos < 0:
+ break
+ proc_file = proc_file[pos:]
+ pos = proc_file[1:].find('\nCodec: ')
+ if pos < 0:
+ pos = len(proc_file)-2
+ res.append(proc_file[:pos+2])
+ proc_file = proc_file[pos:]
+ return res
+
+class HDACardProc:
+
+ def __init__(self, card):
+ self.card = card
+ self.id = 'ProcId'
+ self.driver = 'ProcDriver'
+ self.name = 'ProcName'
+ self.longname = 'ProcLongName'
+ self.components = 'ProcComponents'
+
+class HDABaseProc:
+
+ delim = ' "[],:*'
+
+ def decodestrw(self, str, prefix):
+ if str.startswith(prefix):
+ res = str[len(prefix):].strip()
+ delim = self.delim
+ if res[0] == '"':
+ res = res[1:]
+ delim = '"'
+ rem = res
+ for a in delim:
+ pos = res.find(a)
+ if pos >= 0:
+ if rem == res:
+ rem = rem[pos:]
+ res = res[:pos]
+ if rem == res:
+ rem = ''
+ ok = True
+ while ok:
+ ok = False
+ for a in self.delim:
+ if rem and rem[0] == a:
+ rem = rem[1:]
+ ok = True
+ return rem.strip(), res.strip()
+ self.wrongfile('string decode %s' % repr(str))
+
+ def decodeintw(self, str, prefix='', forcehex=False):
+ if str.startswith(prefix):
+ res = str[len(prefix):].strip()
+ rem = res
+ for a in self.delim:
+ pos = res.find(a)
+ if pos >= 0:
+ if rem == res:
+ rem = rem[pos:]
+ res = res[:pos]
+ if rem == res:
+ rem = ''
+ ok = True
+ while ok:
+ ok = False
+ for a in self.delim:
+ if rem and rem[0] == a:
+ rem = rem[1:]
+ ok = True
+ if res and forcehex:
+ if not res.startswith('0x'):
+ res = '0x' + res
+ if res.startswith('0x'):
+ return rem.strip(), int(res[2:], 16)
+ return rem.strip(), int(res)
+ self.wrongfile('integer decode %s (%s)' % (repr(str), repr(prefix)))
+
+ def wrongfile(self, msg=''):
+ raise ValueError, "wrong proc file format (%s)" % msg
+
+class HDApcmDevice:
+
+ def __init__(self, name, type, device):
+ self.name = name
+ self.type = type
+ self.device = device
+
+ def dump_extra(self):
+ return ' Device: name="%s", type="%s", device=%s\n' % (self.name, self.type, self.device)
+
+class HDApcmControl:
+
+ def __init__(self, name, index, device):
+ self.name = name
+ self.index = index
+ self.device = device
+ self.amp_chs = None
+
+ def dump_extra(self):
+ str = ' Control: name="%s", index=%s, device=%s\n' % (self.name, self.index, self.device)
+ if not self.amp_chs is None:
+ str += ' ControlAmp: chs=%s, dir=%s, idx=%s, ofs=%s\n' % (self.amp_chs, self.amp_dir, self.amp_idx, self.amp_ofs)
+ return str
+
+ def amp_index_match(self, idx):
+ if not self.amp_chs is None:
+ count = (self.amp_chs & 1) + ((self.amp_chs >> 1) & 1)
+ return idx >= self.amp_idx and idx < self.amp_idx + count
+ else:
+ return False
+
+class ProcNode(HDABaseProc):
+
+ def __init__(self, codec, nid, wcaps):
+ self.codec = codec
+ codec.proc_nids[nid] = self
+ self.nid = nid
+ self.wcaps = wcaps
+ self.wtype = (self.wcaps >> 20) & 0x0f
+ self.wtype_id = WIDGET_TYPE_IDS[self.wtype]
+ self.device = None
+ self.amp_vals = [[], []]
+ self.connections = []
+ self.params = {}
+ self.verbs = {}
+ self.controls = []
+ if wcaps & (1 << 6):
+ self.add_param(PARAMS['PROC_CAP'], 0)
+ if wcaps & (1 << 7):
+ self.add_verb(VERBS['GET_UNSOLICITED_RESPONSE'], 0)
+ if wcaps & (1 << 9):
+ self.add_verb(VERBS['GET_DIGI_CONVERT_1'], 0)
+ if wcaps & (1 << 10):
+ self.add_param(PARAMS['POWER_STATE'], 0)
+ if self.wtype_id in ['AUD_IN', 'AUD_OUT']:
+ self.add_verb(VERBS['GET_CONV' ], 0)
+ if self.wtype_id == 'AUD_IN':
+ self.add_verb(VERBS['GET_SDI_SELECT'], 0)
+
+ def rw(self, verb, param):
+ if verb in self.verbs:
+ return self.verbs[verb]
+ elif verb == VERBS['GET_CONNECT_LIST']:
+ f1 = self.connections[param]
+ f2 = 0
+ if param + 1 < len(self.connections):
+ f2 = self.connections[param+1]
+ return f1 | (f2 << 16)
+ elif verb == VERBS['GET_AMP_GAIN_MUTE']:
+ dir = param & (1<<15) and HDA_OUTPUT or HDA_INPUT
+ idx = param & (1<<13) and 1 or 0
+ val = param & 0x7f
+ if val >= len(self.amp_vals[dir]):
+ print "AMP index out of range (%s >= %s)" % (val, len(self.amp_vals[dir]))
+ while val >= len(self.amp_vals[dir]):
+ self.amp_vals[dir].append([128, 128])
+ return self.amp_vals[dir][val][idx]
+ elif verb == VERBS['SET_AMP_GAIN_MUTE']:
+ dir = param & (1<<15) and HDA_OUTPUT or HDA_INPUT
+ idx = (param >> 8) & 0x0f
+ if param & (1<<12):
+ self.amp_vals[dir][idx][0] = param & 0xff
+ if param & (1<<13) and len(self.amp_vals[dir][idx]) > 1:
+ self.amp_vals[dir][idx][1] = param & 0xff
+ return param
+ elif verb == VERBS['SET_DIGI_CONVERT_1']:
+ self.verbs[VERBS['GET_DIGI_CONVERT_1']] &= ~0xff
+ self.verbs[VERBS['GET_DIGI_CONVERT_1']] |= param & 0xff
+ return param
+ elif verb == VERBS['SET_DIGI_CONVERT_2']:
+ self.verbs[VERBS['GET_DIGI_CONVERT_1']] &= ~0xff00
+ self.verbs[VERBS['GET_DIGI_CONVERT_1']] |= (param & 0xff) << 8
+ return param
+ elif verb in SET_VERBS:
+ self.verbs[SET_VERBS[verb]] = param
+ return param
+ raise ValueError, "unimplemented node rw (0x%x, 0x%x, 0x%x)" % (self.nid, verb, param)
+
+ def param_read(self, param):
+ if param in self.params:
+ return self.params[param]
+ elif param == PARAMS['CONNLIST_LEN']:
+ return len(self.connections) + (1 << 7) # use long format
+ raise ValueError, "unimplemented node param read (0x%x, 0x%x)" % (self.nid, param)
+
+ def add_verb(self, verb, param, do_or=False):
+ if do_or and verb in self.verbs:
+ self.verbs[verb] |= param
+ else:
+ self.verbs[verb] = param
+
+ def add_param(self, param, value):
+ self.params[param] = value
+
+ def add_device(self, line):
+ line, name = self.decodestrw(line, 'name=')
+ line, type = self.decodestrw(line, 'type=')
+ line, device = self.decodeintw(line, 'device=')
+ if self.device:
+ self.wrongfile('more than one PCM device?')
+ self.device = HDApcmDevice(name, type, device)
+
+ def get_device(self):
+ return self.device
+
+ def add_converter(self, line):
+ line, stream = self.decodeintw(line, 'stream=')
+ line, channel = self.decodeintw(line, 'channel=')
+ self.add_verb(VERBS['GET_CONV'],
+ ((stream & 0x0f) << 4) | (channel & 0x0f))
+
+ def add_digital(self, line):
+ bits = {
+ 'Enabled': DIG1_BITS['ENABLE'],
+ 'Validity': DIG1_BITS['VALIDITY'],
+ 'ValidityCfg': DIG1_BITS['VALIDITYCFG'],
+ 'Preemphasis': DIG1_BITS['EMPHASIS'],
+ 'Copyright': DIG1_BITS['COPYRIGHT'],
+ 'Non-Audio': DIG1_BITS['NONAUDIO'],
+ 'Pro': DIG1_BITS['PROFESSIONAL'],
+ 'GenLevel': DIG1_BITS['LEVEL']
+ }
+ xbits = 0
+ a = line.split(' ')
+ for b in a:
+ b = b.strip()
+ if not b:
+ return
+ if not b in bits:
+ self.wrongfile('unknown dig1 bit %s' % repr(b))
+ xbits |= 1 << bits[b]
+ self.add_verb(VERBS['GET_DIGI_CONVERT_1'], xbits)
+
+ def add_digitalcategory(self, line):
+ line, res = self.decodeintw(line)
+ self.add_verb(VERBS['GET_DIGI_CONVERT_1'], (res & 0x7f) << 8, do_or=True)
+
+ def add_sdiselect(self, line):
+ line, res = self.decodeintw(line)
+ self.add_verb(VERBS['GET_SDI_SELECT'], res)
+
+ def add_pcm(self, line1, line2, line3):
+ line1, tmp1 = self.decodeintw(line1, ' rates [')
+ line2, tmp2 = self.decodeintw(line2, ' bits [')
+ self.add_param(PARAMS['PCM'],
+ (tmp1 & 0xffff) | ((tmp2 & 0xffff) << 16))
+ line3, tmp1 = self.decodeintw(line3, ' formats [')
+ self.add_param(PARAMS['STREAM'], tmp1)
+
+ def add_control(self, line):
+ line, name = self.decodestrw(line, 'name=')
+ line, index = self.decodeintw(line, 'index=')
+ line, device = self.decodeintw(line, 'device=')
+ self.controls.append(HDApcmControl(name, index, device))
+
+ def add_controlamp(self, line):
+ ctl = self.controls[-1]
+ line, ctl.amp_chs = self.decodeintw(line, 'chs=')
+ line, ctl.amp_dir = self.decodestrw(line, 'dir=')
+ line, ctl.amp_idx = self.decodeintw(line, 'idx=')
+ line, ctl.amp_ofs = self.decodeintw(line, 'ofs=')
+ ctl.amp_dir = ctl.amp_dir == 'In' and HDA_INPUT or HDA_OUTPUT
+
+ def get_controls(self):
+ return self.controls
+
+ def add_ampcaps(self, line, dir):
+ line = line.strip()
+ par = PARAMS[dir == HDA_INPUT and 'AMP_IN_CAP' or 'AMP_OUT_CAP']
+ if line == 'N/A':
+ self.add_param(par, 0)
+ return
+ line, ofs = self.decodeintw(line, 'ofs=')
+ line, nsteps = self.decodeintw(line, 'nsteps=')
+ line, stepsize = self.decodeintw(line, 'stepsize=')
+ line, mute = self.decodeintw(line, 'mute=')
+ self.add_param(par,
+ (ofs & 0x7f) | ((nsteps & 0x7f) << 8) | \
+ ((stepsize & 0x7f) << 16) | ((mute & 1) << 31))
+
+ def add_ampvals(self, line, dir):
+ line = line.strip()
+ self.amp_vals[dir] = []
+ while len(line):
+ if not line[0] == '[':
+ self.wrongfile('amp vals [')
+ pos = line.find(']')
+ if pos <= 0:
+ self.wrongfile('amp vals ]')
+ str = line[1:pos]
+ line = line[len(str)+2:].strip()
+ val = []
+ while str.startswith('0x'):
+ str, val1 = self.decodeintw(str)
+ val.append(val1)
+ self.amp_vals[dir].append(val)
+
+ def add_connection(self, line, conn):
+ line, count = self.decodeintw(line)
+ if count == -22: # driver was not able to read connections
+ count = 0
+ res = 0
+ if conn.startswith(' '):
+ conn = conn.strip()
+ res = 1
+ else:
+ conn = ''
+ conns = []
+ sel = -1
+ while len(conn):
+ if len(conn) > 4 and conn[4] == '*':
+ sel = len(conns)
+ conn, val = self.decodeintw(conn)
+ conns.append(val)
+ if count != len(conns):
+ self.wrongfile('connections %s != %s' % (count, len(conns)))
+ self.connections = conns
+ self.add_verb(VERBS['GET_CONNECT_SEL'], sel)
+ return res
+
+ def add_unsolicited(self, line):
+ line, tag = self.decodeintw(line, 'tag=', forcehex=True)
+ line, enabled = self.decodeintw(line, 'enabled=')
+ self.add_verb(VERBS['GET_UNSOLICITED_RESPONSE'],
+ (tag & 0x3f) | ((enabled & 1) << 7))
+
+ def add_pincap(self, line):
+ line = line.strip()
+ # workaround for bad 0x08%x format string in hda_proc.c
+ a = line.split(':')
+ if line.startswith('0x08') and len(a[0]) != 10:
+ line = "0x" + line[4:]
+ line, tmp1 = self.decodeintw(line, '')
+ self.add_param(PARAMS['PIN_CAP'], tmp1)
+
+ def add_pindefault(self, line):
+ line, tmp1 = self.decodeintw(line, '')
+ self.add_verb(VERBS['GET_CONFIG_DEFAULT'], tmp1)
+
+ def add_pinctls(self, line):
+ line, tmp1 = self.decodeintw(line, '')
+ self.add_verb(VERBS['GET_PIN_WIDGET_CONTROL'], tmp1)
+
+ def add_eapd(self, line):
+ line, tmp1 = self.decodeintw(line, '')
+ self.add_verb(VERBS['GET_EAPD_BTLENABLE'], tmp1)
+
+ def add_power(self, line):
+ line, setting = self.decodestrw(line, 'setting=')
+ line, actual = self.decodestrw(line, 'actual=')
+ if setting in POWER_STATES:
+ setting = POWER_STATES.index(setting)
+ else:
+ self.wrongfile('power setting %s' % setting)
+ if actual in POWER_STATES:
+ actual = POWER_STATES.index(actual)
+ else:
+ self.wrongfile('power actual %s' % actual)
+ self.add_verb(VERBS['GET_POWER_STATE'], (setting & 0x0f) | ((actual & 0x0f) << 4))
+
+ def add_powerstates(self, line):
+ a = line.strip().split(' ')
+ tmp1 = 0
+ for b in a:
+ if b in POWER_STATES:
+ tmp1 |= 1 << POWER_STATES.index(b)
+ self.add_param(PARAMS['POWER_STATE'], tmp1)
+
+ def add_processcaps(self, line):
+ line, benign = self.decodeintw(line, 'benign=')
+ line, ncoeff = self.decodeintw(line, 'ncoeff=')
+ self.add_param(PARAMS['PROC_CAP'],
+ (benign & 1) | ((ncoeff & 0xff) << 8))
+
+ def add_processcoef(self, line):
+ line, coef = self.decodeintw(line, '')
+ self.add_verb(VERBS['GET_PROC_COEF'], coef)
+
+ def add_processindex(self, line):
+ line, idx = self.decodeintw(line, '')
+ self.add_verb(VERBS['GET_COEF_INDEX'], idx)
+
+ def add_volknob(self, line):
+ line, delta = self.decodeintw(line, 'delta=')
+ line, steps = self.decodeintw(line, 'steps=')
+ line, direct = self.decodeintw(line, 'direct=')
+ line, val = self.decodeintw(line, 'val=')
+ self.add_param(PARAMS['VOL_KNB_CAP'], ((delta & 1) << 7) | (steps & 0x7f))
+ self.add_verb(VERBS['GET_VOLUME_KNOB_CONTROL'], ((direct & 1) << 7) | (val & 0x7f))
+
+ def dump_extra(self):
+ str = ''
+ if self.device:
+ str += self.device.dump_extra()
+ for c in self.controls:
+ str += c.dump_extra()
+ return str
+
+class HDACodecProc(HDACodec, HDABaseProc):
+
+ def __init__(self, card, device, proc_file):
+ self.hwaccess = False
+ self.fd = None
+ self.proc_codec = None
+ self.card = card
+ self.device = device
+ self.mcard = HDACardProc(card)
+ self.proc_codec_id = None
+ self.mixer = None
+ self.parse(proc_file)
+ if self.proc_codec_id:
+ self.mcard.name = self.proc_codec_id
+
+ def parse(self, str):
+
+ def lookfor(idx, prefix):
+ while idx < len(lines) and not lines[idx].startswith(prefix):
+ idx += 1
+ if idx >= len(lines):
+ return idx, None
+ idx += 1
+ return idx, lines[idx-1][len(prefix):].strip()
+
+ def lookforint(idx, prefix):
+ idx, res = lookfor(idx, prefix)
+ if res:
+ if res.startswith('0x'):
+ return idx, int(res[2:], 16)
+ return idx, int(res)
+
+ def decodeint(idx, prefix):
+ str, res = self.decodeintw(lines[idx], prefix)
+ return idx+1, res
+
+ def decodefcnid(idx, prefix):
+ if lines[idx].startswith(prefix):
+ str, res = self.decodeintw(lines[idx][len(prefix):])
+ if str.startswith('(unsol '):
+ str, res1 = self.decodeintw(str[:-1], '(unsol ')
+ else:
+ res1 = 0
+ return idx + 1, ((res1 & 1) << 8) | (res & 0xff)
+ return idx, 0
+
+ def decodeampcap(idx, prefix):
+ if lines[idx].startswith(prefix):
+ res = lines[idx][len(prefix):].strip()
+ if res == 'N/A':
+ return idx+1, 0
+ res, ofs = self.decodeintw(res, 'ofs=')
+ res, nsteps = self.decodeintw(res, 'nsteps=')
+ res, stepsize = self.decodeintw(res, 'stepsize=')
+ res, mute = self.decodeintw(res, 'mute=')
+ return idx+1, \
+ (ofs & 0x7f) | ((nsteps & 0x7f) << 8) | \
+ ((stepsize & 0x7f) << 16) | ((mute & 1) << 31)
+ self.wrongfile('amp caps expected')
+
+ def decodegpiocap(idx, prefix):
+ if lines[idx].startswith(prefix):
+ res = lines[idx][len(prefix):].strip()
+ res, io = self.decodeintw(res, 'io=')
+ res, o = self.decodeintw(res, 'o=')
+ res, i = self.decodeintw(res, 'i=')
+ res, unsol = self.decodeintw(res, 'unsolicited=')
+ res, wake = self.decodeintw(res, 'wake=')
+ return idx+1, \
+ (io & 0xff) | ((o & 0xff) << 8) | \
+ ((i & 0xff) << 16) | ((unsol & 1) << 30) | ((wake & 1) << 31)
+ self.wrongfile('gpio caps expected')
+
+ def decodegpio(idx, prefix):
+
+ def writeval(str, idx, var):
+ res, val = self.decodeintw(str, var + '=')
+ if val:
+ self.proc_gpio[var] |= 1 << idx
+ else:
+ self.proc_gpio[var] &= ~(1 << idx)
+ return res
+
+ res = lines[idx]
+ res, idx1 = self.decodeintw(res, prefix)
+ if res.startswith(': '):
+ res = res[2:]
+ for a in ['enable', 'dir', 'wake', 'sticky', 'data']:
+ res = writeval(res, idx1, a)
+ if res.find('unsol=') >= 0:
+ res = writeval(res, idx1, 'unsol')
+ return idx + 1
+
+ self.proc_afg = -1
+ self.proc_mfg = -1
+ self.proc_nids = {}
+ self.proc_afg_function_id = 0
+ self.proc_mfg_function_id = 0
+ self.proc_vendor_id = 0
+ self.proc_subsystem_id = 0
+ self.proc_revision_id =0
+ function_id = 0
+ lines = str.splitlines()
+ idx = 0
+ idx, self.proc_codec_id = lookfor(idx, 'Codec: ')
+ if not self.proc_codec_id:
+ print "Proc Text Contents is not valid"
+ return
+ idx, tmp = lookforint(idx, 'Address: ')
+ self.device = tmp # really?
+ idx, function_id = decodefcnid(idx, 'Function Id: ')
+ idx, self.proc_afg_function_id = decodefcnid(idx, 'AFG Function Id: ')
+ idx, self.proc_mfg_function_id = decodefcnid(idx, 'MFG Function Id: ')
+ idx, self.proc_vendor_id = lookforint(idx, 'Vendor Id: ')
+ idx, self.proc_subsystem_id = lookforint(idx, 'Subsystem Id: ')
+ idx, self.proc_revision_id = lookforint(idx, 'Revision Id:' )
+ if idx >= len(lines):
+ self.wrongfile('id strings expected')
+ nomfg = lines[idx].strip() == 'No Modem Function Group found'
+ if nomfg:
+ self.proc_afg = 1
+ if self.proc_afg_function_id == 0:
+ self.proc_afg_function_id = function_id
+ idx += 1
+ elif lines[idx].startswith('Default PCM:'):
+ self.proc_afg = 1
+ if self.proc_afg_function_id == 0:
+ self.proc_afg_function_id = function_id
+ else:
+ idx, self.proc_mfg = lookforint(idx, 'Modem Function Group: ')
+ if not self.proc_mfg is None:
+ if self.proc_mfg_function_id == 0:
+ self.proc_mfg_function_id = function_id
+ else:
+ self.proc_mfg = -1
+ if idx < len(lines) and lines[idx].startswith('Default PCM:'):
+ self.proc_afg = 1
+ if self.proc_afg >= 0 and self.proc_afg_function_id == 0:
+ self.proc_afg_function_id = 1
+ if self.proc_mfg >= 0 and self.proc_mfg_function_id == 0:
+ self.proc_mfg_function_id = 2
+ if idx >= len(lines):
+ return # probably only modem codec
+ if not lines[idx].startswith('Default PCM:'):
+ self.wrongfile('default pcm expected')
+ if lines[idx+1].strip() == "N/A":
+ idx += 2
+ self.proc_pcm_bits = 0
+ self.proc_pcm_stream = 0
+ else:
+ idx, tmp1 = decodeint(idx+1, ' rates [')
+ idx, tmp2 = decodeint(idx, ' bits [')
+ self.proc_pcm_bits = (tmp1 & 0xffff) | ((tmp2 & 0xffff) << 16)
+ idx, self.proc_pcm_stream = decodeint(idx, ' formats [')
+ idx, self.proc_amp_caps_in = decodeampcap(idx, 'Default Amp-In caps: ')
+ idx, self.proc_amp_caps_out = decodeampcap(idx, 'Default Amp-Out caps: ')
+ self.proc_gpio = {
+ 'enable': 0,
+ 'dir': 0,
+ 'wake': 0,
+ 'sticky': 0,
+ 'data': 0,
+ 'unsol': 0
+ }
+ self.proc_gpio_cap = 0
+ if lines[idx].startswith('GPIO: '):
+ idx, self.proc_gpio_cap = decodegpiocap(idx, 'GPIO: ')
+ while idx < len(lines) and lines[idx].startswith(' IO['):
+ idx = decodegpio(idx, ' IO[')
+ if idx >= len(lines):
+ return
+ line = lines[idx].strip()
+ if line == 'Invalid AFG subtree':
+ print "Invalid AFG subtree for codec %s?" % self.proc_codec_id
+ return
+ while line.startswith('Power-Map: ') or \
+ line.startswith('Analog Loopback: '):
+ print 'Sigmatel specific "%s" verb ignored for the moment' % line
+ idx += 1
+ line = lines[idx].strip()
+ node = None
+ while idx < len(lines):
+ line = lines[idx]
+ idx += 1
+ line, nid = self.decodeintw(line, 'Node ')
+ pos = line.find('wcaps ')
+ if pos < 0:
+ self.wrongfile('node wcaps expected')
+ line, wcaps = self.decodeintw(line[pos:], 'wcaps ')
+ node = ProcNode(self, nid, wcaps)
+ if idx >= len(lines):
+ break
+ line = lines[idx]
+ while not line.startswith('Node '):
+ if line.startswith(' Device: '):
+ node.add_device(line[10:])
+ elif line.startswith(' Control: '):
+ node.add_control(line[11:])
+ elif line.startswith(' ControlAmp: '):
+ node.add_controlamp(line[16:])
+ elif line.startswith(' Converter: '):
+ node.add_converter(line[13:])
+ elif line.startswith(' SDI-Select: '):
+ node.add_sdiselect(line[14:])
+ elif line.startswith(' Digital:'):
+ node.add_digital(line[11:])
+ elif line.startswith(' Unsolicited:'):
+ node.add_unsolicited(line[15:])
+ elif line.startswith(' Digital category:'):
+ node.add_digitalcategory(line[20:])
+ elif line.startswith(' Amp-In caps: '):
+ node.add_ampcaps(line[15:], HDA_INPUT)
+ elif line.startswith(' Amp-Out caps: '):
+ node.add_ampcaps(line[16:], HDA_OUTPUT)
+ elif line.startswith(' Amp-In vals: '):
+ node.add_ampvals(line[15:], HDA_INPUT)
+ elif line.startswith(' Amp-Out vals: '):
+ node.add_ampvals(line[17:], HDA_OUTPUT)
+ elif line.startswith(' Connection: '):
+ if idx + 1 < len(lines):
+ idx += node.add_connection(line[13:], lines[idx+1])
+ else:
+ idx += node.add_connection(line[13:], '')
+ elif line == ' PCM:':
+ node.add_pcm(lines[idx+1], lines[idx+2], lines[idx+3])
+ idx += 3
+ elif line.startswith(' Pincap '):
+ node.add_pincap(line[9:])
+ elif line.startswith(' Vref caps: '):
+ pass
+ elif line.startswith(' Pin Default '):
+ node.add_pindefault(line[14:])
+ elif line.startswith(' Conn = '):
+ pass
+ elif line.startswith(' DefAssociation = '):
+ pass
+ elif line.startswith(' Misc = '):
+ pass
+ elif line.startswith(' Delay: '):
+ pass
+ elif line.startswith(' Pin-ctls: '):
+ node.add_pinctls(line[12:])
+ elif line.startswith(' EAPD '):
+ node.add_eapd(line[7:])
+ elif line.startswith(' Power states: '):
+ node.add_powerstates(line[16:])
+ elif line.startswith(' Power: '):
+ node.add_power(line[9:])
+ elif line.startswith(' Processing caps: '):
+ node.add_processcaps(line[19:])
+ elif line.startswith(' Processing Coefficient: '):
+ node.add_processcoef(line[26:])
+ elif line.startswith(' Coefficient Index: '):
+ node.add_processindex(line[21:])
+ elif line.startswith(' Volume-Knob: '):
+ node.add_volknob(line[15:])
+ else:
+ self.wrongfile(line)
+ idx += 1
+ if idx < len(lines):
+ line = lines[idx]
+ else:
+ break
+
+ def param_read(self, nid, param):
+ if nid == AC_NODE_ROOT:
+ if param == PARAMS['VENDOR_ID']:
+ return self.proc_vendor_id
+ elif param == PARAMS['SUBSYSTEM_ID']:
+ return self.proc_subsystem_id
+ elif param == PARAMS['REV_ID']:
+ return self.proc_revision_id
+ elif nid == self.proc_afg:
+ if param == PARAMS['FUNCTION_TYPE']:
+ return self.proc_afg_function_id
+ elif param == PARAMS['PCM']:
+ return self.proc_pcm_bits
+ elif param == PARAMS['STREAM']:
+ return self.proc_pcm_stream
+ elif param == PARAMS['AMP_OUT_CAP']:
+ return self.proc_amp_caps_out
+ elif param == PARAMS['AMP_IN_CAP']:
+ return self.proc_amp_caps_in
+ elif param == PARAMS['GPIO_CAP']:
+ return self.proc_gpio_cap
+ elif nid == self.proc_mfg:
+ if param == PARAMS['FUNCTION_TYPE']:
+ return self.proc_mfg_function_id
+ else:
+ if nid is None:
+ return 0
+ node = self.proc_nids[nid]
+ return node.param_read(param)
+ raise ValueError, "unimplemented param_read(0x%x, 0x%x)" % (nid, param)
+
+ def get_sub_nodes(self, nid):
+ if nid == AC_NODE_ROOT:
+ if self.proc_mfg >= 0 and self.proc_afg >= 0:
+ return 2, self.proc_afg
+ if self.proc_mfg >= 0:
+ return 1, self.proc_mfg
+ return 1, self.proc_afg
+ elif nid == self.proc_afg:
+ if self.proc_nids:
+ return len(self.proc_nids), self.proc_nids.keys()[0]
+ return 0, 0
+ elif nid is None:
+ return 0, 0
+ raise ValueError, "unimplemented get_sub_nodes(0x%x)" % nid
+
+ def get_wcap(self, nid):
+ node = self.proc_nids[nid]
+ return node.wcaps
+
+ def get_raw_wcap(self, nid):
+ return get_wcap(self, nid)
+
+ def rw(self, nid, verb, param):
+ if nid == self.proc_afg:
+ for i, j in GPIO_IDS.iteritems():
+ if verb == j[0] or verb == j[1]:
+ if i == 'direction':
+ i = 'dir'
+ return self.proc_gpio[i]
+ if verb == VERBS['GET_SUBSYSTEM_ID']:
+ return self.proc_subsystem_id
+ else:
+ if nid is None:
+ return 0
+ node = self.proc_nids[nid]
+ return node.rw(verb, param)
+ raise ValueError, "unimplemented rw(0x%x, 0x%x, 0x%x)" % (nid, verb, param)
+
+ def dump_node_extra(self, node):
+ if not node or not node.nid in self.proc_nids:
+ return ''
+ node = self.proc_nids[node.nid]
+ return node.dump_extra()
+
+ def get_device(self, nid):
+ if not nid in self.proc_nids:
+ return None
+ node = self.proc_nids[nid]
+ return node.get_device()
+
+ def get_controls(self, nid):
+ if not nid in self.proc_nids:
+ return None
+ node = self.proc_nids[nid]
+ return node.get_controls()
+
+#
+# test section
+#
+
+def dotest1(base):
+ l = listdir(base)
+ for f in l:
+ file = base + '/' + f
+ if os.path.isdir(file):
+ dotest1(file)
+ else:
+ print file
+ file = DecodeProcFile(file)
+ file = DecodeAlsaInfoFile(file)
+ for a in file:
+ c = HDACodecProc(0, 0, a)
+ c.analyze()
+
+def dotest():
+ import sys
+ if len(sys.argv) < 2:
+ raise ValueError, "Specify directory with codec dumps"
+ dotest1(sys.argv[1])
+
+if __name__ == '__main__':
+ import os
+ from dircache import listdir
+ dotest()
--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+# 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 errno import EAGAIN
+from subprocess import Popen, PIPE, STDOUT
+from fcntl import fcntl, F_SETFL, F_GETFL
+from signal import SIGKILL
+import os
+
+CHANNELS = [
+ "Front Left",
+ "Front Right",
+ "Rear Left",
+ "Rear Right",
+ "Center",
+ "LFE/Woofer",
+ "Side Left",
+ "Side Right"
+]
+
+def set_fd_nonblocking(fd):
+ flags = fcntl(fd, F_GETFL)
+ fcntl(fd, F_SETFL, flags | os.O_NONBLOCK)
+
+class Monitor(gtk.Window):
+
+ channel = 0
+ channels = 0
+ device = ''
+ generate_num = 0
+
+ def __init__(self, parent=None, device=None):
+ self.device = device
+ if not self.device:
+ self.device = 'plughw:0'
+ gtk.Window.__init__(self)
+ try:
+ self.set_screen(parent.get_screen())
+ except AttributeError:
+ pass
+ self.connect('destroy', self.__destroy)
+ self.set_default_size(600, 400)
+ self.set_title(self.__class__.__name__)
+ self.set_border_width(10)
+ vbox = gtk.VBox(False, 0)
+ text_view = gtk.TextView()
+ fontName = pango.FontDescription("Misc Fixed,Courier 10")
+ text_view.modify_font(fontName)
+ text_view.set_border_width(4)
+ text_view.set_size_request(580, 350)
+ buffer = gtk.TextBuffer(None)
+ self.info_buffer = buffer
+ iter = buffer.get_iter_at_offset(0)
+ buffer.insert(iter, 'Please, select channel to play or number channels to record...')
+ text_view.set_buffer(buffer)
+ text_view.set_editable(False)
+ text_view.set_cursor_visible(False)
+ vbox.pack_start(text_view)
+ self.statusbar = gtk.Statusbar()
+ vbox.pack_start(self.statusbar, True, False)
+ separator = gtk.HSeparator()
+ vbox.pack_start(separator, expand=False)
+ frame = gtk.Frame('Playback')
+ frame.set_border_width(4)
+ hbox = gtk.HBox(False, 0)
+ hbox.set_border_width(4)
+ idx = 0
+ for name in CHANNELS:
+ button = gtk.Button(name)
+ button.connect("clicked", self.__channel_change, idx)
+ hbox.pack_start(button, False, False)
+ idx += 1
+ frame.add(hbox)
+ vbox.pack_start(frame, False, False)
+ frame = gtk.Frame('Capture')
+ frame.set_border_width(4)
+ hbox = gtk.HBox(False, 0)
+ hbox.set_border_width(4)
+ for idx in [2, 4, 6, 8]:
+ button = gtk.Button("%s channels" % idx)
+ button.connect("clicked", self.__channels_change, idx)
+ hbox.pack_start(button, False, False)
+ idx += 1
+ frame.add(hbox)
+ vbox.pack_start(frame, False, False)
+ self.add(vbox)
+ self.generate_p = None
+ self.record_p = None
+ self.set_title('ALSA Monitor for %s' % self.device)
+ self.show_all()
+
+ def __destroy(self, e):
+ self.generate_cleanup()
+ self.record_cleanup()
+ gtk.main_quit()
+
+ def __channel_change(self, button, idx):
+ if self.channel != idx or self.generate_p == None:
+ self.set_text('Switching to playback...')
+ self.channel = idx
+ self.record_cleanup()
+ self.generate_cleanup()
+ self.generate_sound()
+
+ def __channels_change(self, button, idx):
+ if self.channels != idx or self.record_p == None:
+ self.set_text('Switching to record...')
+ self.channels = idx
+ self.generate_cleanup()
+ self.record_cleanup()
+ self.record_sound()
+
+ def set_text(self, text):
+ buffer = self.info_buffer
+ start, end = buffer.get_bounds()
+ buffer.delete(start, end)
+ if not text: return
+ iter = buffer.get_iter_at_offset(0)
+ buffer.insert(iter, text)
+
+ def set_status(self, text):
+ context_id = self.statusbar.get_context_id("SCTX")
+ self.statusbar.pop(context_id)
+ self.statusbar.push(context_id, text)
+
+ def generate_sound(self):
+ self.set_status('Playing sound #%s on channel %s...' % \
+ (self.generate_num, CHANNELS[self.channel]))
+ self.generate_num += 1
+ channels = 2
+ if self.channel >= 6:
+ channels = 8
+ elif self.channel >= 4:
+ channels = 6
+ elif self.channel >= 2:
+ channels = 4
+ self.cmd = ["speaker-test", "-D", self.device,
+ "-c", str(channels),
+ "-s", str(self.channel + 1)]
+ p = Popen(self.cmd,
+ shell=False, bufsize=0, stdin=None, stdout=PIPE, stderr=PIPE,
+ close_fds=True)
+ for fd in [p.stdout.fileno(), p.stderr.fileno()]:
+ set_fd_nonblocking(fd)
+ self.generate_p = p
+ self.generate_stdout_id = gobject.io_add_watch(p.stdout, gobject.IO_IN|gobject.IO_HUP|gobject.IO_NVAL, self.generate_io_stdout)
+ self.generate_stderr_id = gobject.io_add_watch(p.stderr, gobject.IO_IN|gobject.IO_HUP|gobject.IO_NVAL, self.generate_io_stderr)
+ self.generate_timeout_id = gobject.timeout_add(5000, self.generate_timeout)
+ self.generate_stdout = ''
+ self.generate_stderr = ''
+
+ def generate_cleanup(self):
+ if not self.generate_p:
+ return
+ if self.generate_p.poll() == None:
+ try:
+ os.kill(self.generate_p.pid, SIGKILL)
+ except:
+ pass
+ self.generate_p.wait()
+ gobject.source_remove(self.generate_timeout_id)
+ gobject.source_remove(self.generate_stdout_id)
+ gobject.source_remove(self.generate_stderr_id)
+ del self.generate_p
+ self.generate_p = None
+
+ def generate_timeout(self):
+ if self.generate_stdout == '' or self.generate_p.poll() != None:
+ if self.generate_stdout == '':
+ self.set_text('Cannot play. Device is busy...')
+ else:
+ self.set_text(' '.join(self.cmd) + '\n\n' + self.generate_stdout)
+ self.generate_cleanup()
+ self.generate_sound()
+ return False
+ return True
+
+ def generate_io_stdout(self, source, condition):
+ if condition & gobject.IO_IN:
+ self.generate_stdout += source.read(1024)
+ self.set_text(' '.join(self.cmd) + '\n\n' + self.generate_stdout)
+ return True
+
+ def generate_io_stderr(self, source, condition):
+ if condition & gobject.IO_IN:
+ self.generate_stderr += source.read(1024)
+ return True
+
+ def record_sound(self):
+ self.set_status('Recording sound - %s channels...' % self.channels)
+ self.cmd = ["arecord", "-D", self.device,
+ "-f", "dat", "-c", str(self.channels),
+ "-t", "raw", "-vvv", "/dev/null"]
+ p = Popen(self.cmd,
+ shell=False, bufsize=0, stdin=None, stdout=PIPE, stderr=PIPE,
+ close_fds=True)
+ for fd in [p.stdout.fileno(), p.stderr.fileno()]:
+ set_fd_nonblocking(fd)
+ self.record_p = p
+ self.record_stdout_id = gobject.io_add_watch(p.stdout, gobject.IO_IN|gobject.IO_HUP|gobject.IO_NVAL, self.record_io_stdout)
+ self.record_stderr_id = gobject.io_add_watch(p.stderr, gobject.IO_IN|gobject.IO_HUP|gobject.IO_NVAL, self.record_io_stderr)
+ self.record_timeout_id = gobject.timeout_add(5000, self.record_timeout)
+ self.record_stdout = ''
+ self.record_stderr = ''
+ self.record_count = 0
+ self.record_vols = []
+ self.record_data = ''
+
+ def record_cleanup(self):
+ if not self.record_p:
+ return
+ if self.record_p.poll() == None:
+ try:
+ os.kill(self.record_p.pid, SIGKILL)
+ except:
+ pass
+ self.record_p.wait()
+ gobject.source_remove(self.record_timeout_id)
+ gobject.source_remove(self.record_stdout_id)
+ gobject.source_remove(self.record_stderr_id)
+ del self.record_p
+ self.record_p = None
+
+ def record_timeout(self):
+ if self.record_count == 0 or self.record_p.poll() != None:
+ if self.record_count == '':
+ self.set_text('Cannot record. Device is busy...')
+ else:
+ self.set_text(' '.join(self.cmd) + '\n\n' + self.record_stdout)
+ self.record_cleanup()
+ self.record_sound()
+ return False
+ return True
+
+ def record_io_stdout(self, source, condition):
+ if condition & gobject.IO_IN:
+ while 1:
+ try:
+ data = source.read(128)
+ except IOError, e:
+ if e.errno == EAGAIN:
+ self.show_record_vols()
+ break
+ raise IOError, e
+ self.record_data += data
+ self.record_count += len(data)
+ pos = self.record_data.find('\n')
+ if pos >= 0:
+ line = self.record_data[:pos]
+ self.record_data = self.record_data[pos+1:]
+ pos = line.find('%')
+ if pos >= 0:
+ pos1 = pos - 1
+ while line[pos1] >= '0' and line[pos1] <= '9':
+ pos1 -= 1
+ self.record_vols.append(int(line[pos1:pos]))
+ if len(self.record_vols) > 24:
+ del self.record_vols[0]
+ #print data
+ return True
+
+ def record_io_stderr(self, source, condition):
+ if condition & gobject.IO_IN:
+ self.record_stderr += source.read(1024)
+ return True
+
+ def show_record_vols(self):
+ txt = 'Volume bars (# = volume, . = empty)\n'
+ max = 60
+ for i in self.record_vols:
+ limit = (i * max) / 100
+ for c in range(max):
+ txt += c < limit and '#' or '.'
+ txt += '\n'
+ self.set_text(txt)
+
+def main():
+ Monitor()
+ gtk.main()
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+#!/usr/bin/env python
+
+URL="http://git.alsa-project.org/?p=alsa.git;a=blob_plain;f=hda-analyzer/"
+FILES=["hda_analyzer.py", "hda_guilib.py", "hda_codec.py", "hda_proc.py",
+ "hda_graph.py", "hda_mixer.py"]
+
+try:
+ import gobject
+ import gtk
+ import pango
+except:
+ print "Please, install pygtk2 or python-gtk package"
+
+import os
+import sys
+from urllib import splithost
+from httplib import HTTP
+
+if os.path.exists("/dev/shm"):
+ TMPDIR="/dev/shm"
+else:
+ TMPDIR="/tmp"
+TMPDIR += "/hda-analyzer"
+print "Using temporary directory: %s" % TMPDIR
+print "You may remove this directory when finished or if you like to"
+print "download the most recent copy of hda-analyzer tool."
+if not os.path.exists(TMPDIR):
+ os.mkdir(TMPDIR)
+for f in FILES:
+ dest = TMPDIR + '/' + f
+ if os.path.exists(dest):
+ print "File cached " + dest
+ continue
+ print "Downloading file %s" % f
+ host, selector = splithost(URL[5:])
+ h = HTTP(host)
+ h.putrequest('GET', URL + f)
+ h.endheaders()
+ h.getreply()
+ contents = h.getfile().read(2*1024*1024)
+ h.close()
+ open(dest, "w+").write(contents)
+print "Downloaded all files, executing %s" % FILES[0]
+os.system("python %s" % TMPDIR + '/' + FILES[0] + ' ' + ' '.join(sys.argv[1:]))
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+version 0.3:
+ - Add a brief description about hwdep in README
+ - Add COPYING before someone blaming me
+
+version 0.2
+ - public release
+
+version 0.1
+ - embryo
--- /dev/null
+VERSION = 0.3
+
+CC = gcc
+CFLAGS = -Wall -O2 -g
+
+hda-verb: hda-verb.o
+ $(CC) -o $@ hda-verb.o
+
+clean:
+ rm -f hda-verb *.o
+
+dist:
+ cd ..; mv hda-verb hda-verb-$(VERSION); \
+ tar cfz hda-verb-$(VERSION).tar.gz --exclude='.git*' hda-verb-$(VERSION); \
+ mv hda-verb-$(VERSION) hda-verb
--- /dev/null
+HDA-VERB -- Send a HD-audio command
+
+hda-verb is a small program to send HD-audio commands to the given
+ALSA hwdep device on the hd-audio interface.
+
+First off, build HD-audio driver with hwdep support. For the kernel
+config, set CONFIG_SND_HDA_HWDEP=y. When you build ALSA drivers from
+alsa-driver tarball, usually this is set automatically.
+
+Once snd-hda-intel driver is built with the hwdep support, you should
+have a hwdep device such as /dev/snd/hwC0D0.
+
+The program takes four arguments, the hwdep device name, the widget NID,
+the verb and the parameter. For example,
+
+ % hda-verb /dev/snd/hwC0D0 0x12 0x701 2
+
+The verb argument can be a string like "PARAMETERS". Also the
+parameter argument can be a string like "VENDOR_ID" as well.
+
+ % hda-verb /dev/snd/hwC0D0 0x0 PARAMETERS VENDOR_ID
+
+The string is case insensitive. Also, it doesn't have to be the full
+string but only has to be unique. E.g. "par" is enough to mean
+"PARAMETER", and "set_a" is enough as "SET_AMP_GAIN_MUTE".
+
+ % hda-verb /dev/snd/hwC0D0 2 set_a 0xb080
+
+The program executs the given verb, shows the result and quits.
+Usually you need to be root to run this command.
+
+
+*WARNING*
+Use this program carefully. Sending an invalid verb may screw up the
+codec communication, which requires either a reboot or reloading of
+the sound driver eventually.
--- /dev/null
+/*
+ * Accessing HD-audio verbs via hwdep interface
+ * Version 0.3
+ *
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ *
+ * Licensed under GPL v2 or later.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/io.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+
+#include <stdint.h>
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+#include "hda_hwdep.h"
+
+#define AC_VERB_GET_STREAM_FORMAT 0x0a00
+#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
+#define AC_VERB_GET_PROC_COEF 0x0c00
+#define AC_VERB_GET_COEF_INDEX 0x0d00
+#define AC_VERB_PARAMETERS 0x0f00
+#define AC_VERB_GET_CONNECT_SEL 0x0f01
+#define AC_VERB_GET_CONNECT_LIST 0x0f02
+#define AC_VERB_GET_PROC_STATE 0x0f03
+#define AC_VERB_GET_SDI_SELECT 0x0f04
+#define AC_VERB_GET_POWER_STATE 0x0f05
+#define AC_VERB_GET_CONV 0x0f06
+#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
+#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
+#define AC_VERB_GET_PIN_SENSE 0x0f09
+#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
+#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
+#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e
+#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
+#define AC_VERB_GET_GPIO_DATA 0x0f15
+#define AC_VERB_GET_GPIO_MASK 0x0f16
+#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
+#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
+#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
+#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
+#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
+#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
+
+#define AC_VERB_SET_STREAM_FORMAT 0x200
+#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
+#define AC_VERB_SET_PROC_COEF 0x400
+#define AC_VERB_SET_COEF_INDEX 0x500
+#define AC_VERB_SET_CONNECT_SEL 0x701
+#define AC_VERB_SET_PROC_STATE 0x703
+#define AC_VERB_SET_SDI_SELECT 0x704
+#define AC_VERB_SET_POWER_STATE 0x705
+#define AC_VERB_SET_CHANNEL_STREAMID 0x706
+#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
+#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define AC_VERB_SET_PIN_SENSE 0x709
+#define AC_VERB_SET_BEEP_CONTROL 0x70a
+#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
+#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
+#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
+#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
+#define AC_VERB_SET_GPIO_DATA 0x715
+#define AC_VERB_SET_GPIO_MASK 0x716
+#define AC_VERB_SET_GPIO_DIRECTION 0x717
+#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
+#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
+#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
+#define AC_VERB_SET_CODEC_RESET 0x7ff
+
+#define AC_PAR_VENDOR_ID 0x00
+#define AC_PAR_SUBSYSTEM_ID 0x01
+#define AC_PAR_REV_ID 0x02
+#define AC_PAR_NODE_COUNT 0x04
+#define AC_PAR_FUNCTION_TYPE 0x05
+#define AC_PAR_AUDIO_FG_CAP 0x08
+#define AC_PAR_AUDIO_WIDGET_CAP 0x09
+#define AC_PAR_PCM 0x0a
+#define AC_PAR_STREAM 0x0b
+#define AC_PAR_PIN_CAP 0x0c
+#define AC_PAR_AMP_IN_CAP 0x0d
+#define AC_PAR_CONNLIST_LEN 0x0e
+#define AC_PAR_POWER_STATE 0x0f
+#define AC_PAR_PROC_CAP 0x10
+#define AC_PAR_GPIO_CAP 0x11
+#define AC_PAR_AMP_OUT_CAP 0x12
+#define AC_PAR_VOL_KNB_CAP 0x13
+
+/*
+ */
+#define VERBSTR(x) { .val = AC_VERB_##x, .str = #x }
+#define PARMSTR(x) { .val = AC_PAR_##x, .str = #x }
+
+struct strtbl {
+ int val;
+ const char *str;
+};
+
+static struct strtbl hda_verbs[] = {
+ VERBSTR(GET_STREAM_FORMAT),
+ VERBSTR(GET_AMP_GAIN_MUTE),
+ VERBSTR(GET_PROC_COEF),
+ VERBSTR(GET_COEF_INDEX),
+ VERBSTR(PARAMETERS),
+ VERBSTR(GET_CONNECT_SEL),
+ VERBSTR(GET_CONNECT_LIST),
+ VERBSTR(GET_PROC_STATE),
+ VERBSTR(GET_SDI_SELECT),
+ VERBSTR(GET_POWER_STATE),
+ VERBSTR(GET_CONV),
+ VERBSTR(GET_PIN_WIDGET_CONTROL),
+ VERBSTR(GET_UNSOLICITED_RESPONSE),
+ VERBSTR(GET_PIN_SENSE),
+ VERBSTR(GET_BEEP_CONTROL),
+ VERBSTR(GET_EAPD_BTLENABLE),
+ VERBSTR(GET_DIGI_CONVERT_1),
+ VERBSTR(GET_DIGI_CONVERT_2),
+ VERBSTR(GET_VOLUME_KNOB_CONTROL),
+ VERBSTR(GET_GPIO_DATA),
+ VERBSTR(GET_GPIO_MASK),
+ VERBSTR(GET_GPIO_DIRECTION),
+ VERBSTR(GET_GPIO_WAKE_MASK),
+ VERBSTR(GET_GPIO_UNSOLICITED_RSP_MASK),
+ VERBSTR(GET_GPIO_STICKY_MASK),
+ VERBSTR(GET_CONFIG_DEFAULT),
+ VERBSTR(GET_SUBSYSTEM_ID),
+
+ VERBSTR(SET_STREAM_FORMAT),
+ VERBSTR(SET_AMP_GAIN_MUTE),
+ VERBSTR(SET_PROC_COEF),
+ VERBSTR(SET_COEF_INDEX),
+ VERBSTR(SET_CONNECT_SEL),
+ VERBSTR(SET_PROC_STATE),
+ VERBSTR(SET_SDI_SELECT),
+ VERBSTR(SET_POWER_STATE),
+ VERBSTR(SET_CHANNEL_STREAMID),
+ VERBSTR(SET_PIN_WIDGET_CONTROL),
+ VERBSTR(SET_UNSOLICITED_ENABLE),
+ VERBSTR(SET_PIN_SENSE),
+ VERBSTR(SET_BEEP_CONTROL),
+ VERBSTR(SET_EAPD_BTLENABLE),
+ VERBSTR(SET_DIGI_CONVERT_1),
+ VERBSTR(SET_DIGI_CONVERT_2),
+ VERBSTR(SET_VOLUME_KNOB_CONTROL),
+ VERBSTR(SET_GPIO_DATA),
+ VERBSTR(SET_GPIO_MASK),
+ VERBSTR(SET_GPIO_DIRECTION),
+ VERBSTR(SET_GPIO_WAKE_MASK),
+ VERBSTR(SET_GPIO_UNSOLICITED_RSP_MASK),
+ VERBSTR(SET_GPIO_STICKY_MASK),
+ VERBSTR(SET_CONFIG_DEFAULT_BYTES_0),
+ VERBSTR(SET_CONFIG_DEFAULT_BYTES_1),
+ VERBSTR(SET_CONFIG_DEFAULT_BYTES_2),
+ VERBSTR(SET_CONFIG_DEFAULT_BYTES_3),
+ VERBSTR(SET_CODEC_RESET),
+ { }, /* end */
+};
+
+static struct strtbl hda_params[] = {
+ PARMSTR(VENDOR_ID),
+ PARMSTR(SUBSYSTEM_ID),
+ PARMSTR(REV_ID),
+ PARMSTR(NODE_COUNT),
+ PARMSTR(FUNCTION_TYPE),
+ PARMSTR(AUDIO_FG_CAP),
+ PARMSTR(AUDIO_WIDGET_CAP),
+ PARMSTR(PCM),
+ PARMSTR(STREAM),
+ PARMSTR(PIN_CAP),
+ PARMSTR(AMP_IN_CAP),
+ PARMSTR(CONNLIST_LEN),
+ PARMSTR(POWER_STATE),
+ PARMSTR(PROC_CAP),
+ PARMSTR(GPIO_CAP),
+ PARMSTR(AMP_OUT_CAP),
+ PARMSTR(VOL_KNB_CAP),
+ { }, /* end */
+};
+
+static void list_keys(struct strtbl *tbl)
+{
+ int c = 0;
+ for (; tbl->str; tbl++) {
+ int len = strlen(tbl->str) + 2;
+ if (c + len >= 80) {
+ fprintf(stderr, "\n");
+ c = 0;
+ }
+ if (!c)
+ fprintf(stderr, " %s", tbl->str);
+ else
+ fprintf(stderr, ", %s", tbl->str);
+ c += 2 + len;
+ }
+ fprintf(stderr, "\n");
+}
+
+/* look up a value from the given string table */
+static int lookup_str(struct strtbl *tbl, const char *str)
+{
+ struct strtbl *p, *found;
+ int len = strlen(str);
+
+ found = NULL;
+ for (p = tbl; p->str; p++) {
+ if (!strncmp(str, p->str, len)) {
+ if (found) {
+ fprintf(stderr, "No unique key '%s'\n", str);
+ return -1;
+ }
+ found = p;
+ }
+ }
+ if (!found) {
+ fprintf(stderr, "No key matching with '%s'\n", str);
+ return -1;
+ }
+ return found->val;
+}
+
+/* convert a string to upper letters */
+static void strtoupper(char *str)
+{
+ for (; *str; str++)
+ *str = toupper(*str);
+}
+
+int main(int argc, char **argv)
+{
+ int version;
+ int fd;
+ int nid, verb, param;
+ struct hda_verb_ioctl val;
+
+ if (argc < 5) {
+ fprintf(stderr, "usage: hda-verb hwdep-device nid verb param\n");
+ fprintf(stderr, "known verbs:\n");
+ list_keys(hda_verbs);
+ fprintf(stderr, "known parameters:\n");
+ list_keys(hda_params);
+ return 1;
+ }
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ version = 0;
+ if (ioctl(fd, HDA_IOCTL_PVERSION, &version) < 0) {
+ perror("ioctl(PVERSION)");
+ fprintf(stderr, "Looks like an invalid hwdep device...\n");
+ return 1;
+ }
+ if (version < HDA_HWDEP_VERSION) {
+ fprintf(stderr, "Invalid version number 0x%x\n", version);
+ fprintf(stderr, "Looks like an invalid hwdep device...\n");
+ return 1;
+ }
+
+ nid = strtol(argv[2], NULL, 0);
+ if (nid < 0 || nid > 0xff) {
+ fprintf(stderr, "invalid nid 0x%x\n", nid);
+ return 1;
+ }
+
+ if (!isdigit(*argv[3])) {
+ strtoupper(argv[3]);
+ verb = lookup_str(hda_verbs, argv[3]);
+ if (verb < 0)
+ return 1;
+ } else {
+ verb = strtol(argv[3], NULL, 0);
+ if (verb < 0 || verb > 0xfff) {
+ fprintf(stderr, "invalid verb 0x%x\n", verb);
+ return 1;
+ }
+ }
+ if (!isdigit(*argv[4])) {
+ strtoupper(argv[4]);
+ param = lookup_str(hda_params, argv[4]);
+ if (param < 0)
+ return 1;
+ } else {
+ param = strtol(argv[4], NULL, 0);
+ if (param < 0 || param > 0xffff) {
+ fprintf(stderr, "invalid param 0x%x\n", param);
+ return 1;
+ }
+ }
+ fprintf(stderr, "nid = 0x%x, verb = 0x%x, param = 0x%x\n",
+ nid, verb, param);
+
+ val.verb = HDA_VERB(nid, verb, param);
+ if (ioctl(fd, HDA_IOCTL_VERB_WRITE, &val) < 0)
+ perror("ioctl");
+ printf("value = 0x%x\n", val.res);
+ close(fd);
+ return 0;
+}
--- /dev/null
+/*
+ * HWDEP Interface for HD-audio codec
+ *
+ * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver 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 driver 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SOUND_HDA_HWDEP_H
+#define __SOUND_HDA_HWDEP_H
+
+#define HDA_HWDEP_VERSION ((1 << 16) | (0 << 8) | (0 << 0)) /* 1.0.0 */
+
+/* verb */
+#define HDA_REG_NID_SHIFT 24
+#define HDA_REG_VERB_SHIFT 8
+#define HDA_REG_VAL_SHIFT 0
+#define HDA_VERB(nid,verb,param) ((nid)<<24 | (verb)<<8 | (param))
+
+struct hda_verb_ioctl {
+ u32 verb; /* HDA_VERB() */
+ u32 res; /* response */
+};
+
+/*
+ * ioctls
+ */
+#define HDA_IOCTL_PVERSION _IOR('H', 0x10, int)
+#define HDA_IOCTL_VERB_WRITE _IOWR('H', 0x11, struct hda_verb_ioctl)
+#define HDA_IOCTL_GET_WCAP _IOWR('H', 0x12, struct hda_verb_ioctl)
+
+#endif