policy-device-profile.lua 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. -- WirePlumber
  2. --
  3. -- Copyright © 2022 Collabora Ltd.
  4. -- @author Julian Bouzas <julian.bouzas@collabora.com>
  5. --
  6. -- SPDX-License-Identifier: MIT
  7. local self = {}
  8. self.config = ... or {}
  9. self.config.persistent = self.config.persistent or {}
  10. self.active_profiles = {}
  11. self.default_profile_plugin = Plugin.find("default-profile")
  12. -- Preprocess persisten profiles and create Interest objects
  13. for _, p in ipairs(self.config.persistent or {}) do
  14. p.interests = {}
  15. for _, i in ipairs(p.matches) do
  16. local interest_desc = { type = "properties" }
  17. for _, c in ipairs(i) do
  18. c.type = "pw"
  19. table.insert(interest_desc, Constraint(c))
  20. end
  21. local interest = Interest(interest_desc)
  22. table.insert(p.interests, interest)
  23. end
  24. p.matches = nil
  25. end
  26. -- Checks whether a device profile is persistent or not
  27. function isProfilePersistent(device_props, profile_name)
  28. for _, p in ipairs(self.config.persistent or {}) do
  29. if p.profile_names then
  30. for _, interest in ipairs(p.interests) do
  31. if interest:matches(device_props) then
  32. for _, pn in ipairs(p.profile_names) do
  33. if pn == profile_name then
  34. return true
  35. end
  36. end
  37. end
  38. end
  39. end
  40. end
  41. return false
  42. end
  43. function parseParam(param, id)
  44. local parsed = param:parse()
  45. if parsed.pod_type == "Object" and parsed.object_id == id then
  46. return parsed.properties
  47. else
  48. return nil
  49. end
  50. end
  51. function setDeviceProfile (device, dev_id, dev_name, profile)
  52. if self.active_profiles[dev_id] and
  53. self.active_profiles[dev_id].index == profile.index then
  54. Log.info ("Profile " .. profile.name .. " is already set in " .. dev_name)
  55. return
  56. end
  57. local param = Pod.Object {
  58. "Spa:Pod:Object:Param:Profile", "Profile",
  59. index = profile.index,
  60. }
  61. Log.info ("Setting profile " .. profile.name .. " on " .. dev_name)
  62. device:set_param("Profile", param)
  63. end
  64. function findDefaultProfile (device)
  65. local def_name = nil
  66. if self.default_profile_plugin ~= nil then
  67. def_name = self.default_profile_plugin:call ("get-profile", device)
  68. end
  69. if def_name == nil then
  70. return nil
  71. end
  72. for p in device:iterate_params("EnumProfile") do
  73. local profile = parseParam(p, "EnumProfile")
  74. if profile.name == def_name then
  75. return profile
  76. end
  77. end
  78. return nil
  79. end
  80. function findBestProfile (device)
  81. local off_profile = nil
  82. local best_profile = nil
  83. local unk_profile = nil
  84. for p in device:iterate_params("EnumProfile") do
  85. profile = parseParam(p, "EnumProfile")
  86. if profile and profile.name ~= "pro-audio" then
  87. if profile.name == "off" then
  88. off_profile = profile
  89. elseif profile.available == "yes" then
  90. if best_profile == nil or profile.priority > best_profile.priority then
  91. best_profile = profile
  92. end
  93. elseif profile.available ~= "no" then
  94. if unk_profile == nil or profile.priority > unk_profile.priority then
  95. unk_profile = profile
  96. end
  97. end
  98. end
  99. end
  100. if best_profile ~= nil then
  101. return best_profile
  102. elseif unk_profile ~= nil then
  103. return unk_profile
  104. elseif off_profile ~= nil then
  105. return off_profile
  106. end
  107. return nil
  108. end
  109. function handleProfiles (device, new_device)
  110. local dev_id = device["bound-id"]
  111. local dev_name = device.properties["device.name"]
  112. local def_profile = findDefaultProfile (device)
  113. -- Do not do anything if active profile is both persistent and default
  114. if not new_device and
  115. self.active_profiles[dev_id] ~= nil and
  116. isProfilePersistent (device.properties, self.active_profiles[dev_id].name) and
  117. def_profile ~= nil and
  118. self.active_profiles[dev_id].name == def_profile.name
  119. then
  120. local active_profile = self.active_profiles[dev_id].name
  121. Log.info ("Device profile " .. active_profile .. " is persistent for " .. dev_name)
  122. return
  123. end
  124. if def_profile ~= nil then
  125. if def_profile.available == "no" then
  126. Log.info ("Default profile " .. def_profile.name .. " unavailable for " .. dev_name)
  127. else
  128. Log.info ("Found default profile " .. def_profile.name .. " for " .. dev_name)
  129. setDeviceProfile (device, dev_id, dev_name, def_profile)
  130. return
  131. end
  132. else
  133. Log.info ("Default profile not found for " .. dev_name)
  134. end
  135. local best_profile = findBestProfile (device)
  136. if best_profile ~= nil then
  137. Log.info ("Found best profile " .. best_profile.name .. " for " .. dev_name)
  138. setDeviceProfile (device, dev_id, dev_name, best_profile)
  139. else
  140. Log.info ("Best profile not found on " .. dev_name)
  141. end
  142. end
  143. function onDeviceParamsChanged (device, param_name)
  144. if param_name == "EnumProfile" then
  145. handleProfiles (device, false)
  146. end
  147. end
  148. self.om = ObjectManager {
  149. Interest {
  150. type = "device",
  151. Constraint { "device.name", "is-present", type = "pw-global" },
  152. }
  153. }
  154. self.om:connect("object-added", function (_, device)
  155. device:connect ("params-changed", onDeviceParamsChanged)
  156. handleProfiles (device, true)
  157. end)
  158. self.om:connect("object-removed", function (_, device)
  159. local dev_id = device["bound-id"]
  160. self.active_profiles[dev_id] = nil
  161. end)
  162. self.om:activate()