fwupd/plugins/synapticsmst/fu-plugin-synapticsmst.c
Mario Limonciello 57816a7907 synapticsmst: blacklist plugin when using amdgpu (Fixes: #1121)
An undiagnosed kernel issue is leading to a kernel crash involving
DP aux reads with amdgpu involved.

It's been reproduced in kernel 4.15 with a DKMS version of amdgpu as
well as kernel 4.19 with amdgpu within the kernel.

```
[16862.519947] [drm:generic_reg_wait [amdgpu]] *ERROR* REG_WAIT timeout 10us * 160 tries - submit_channel_request line:246
[16862.519983] WARNING: CPU: 5 PID: 3015 at drivers/gpu/drm/amd/amdgpu/../display/dc/dc_helper.c:254 generic_reg_wait+0xe2/0x160 [amdgpu]
[16862.519984] Modules linked in: rfcomm snd_hda_codec_realtek snd_hda_codec_generic cmac bnep arc4 nls_iso8859_1 amdkfd amd_iommu_v2 amdgpu i915 dell_rbtn iwlmvm snd_soc_skl wmi_bmof dell_wmi intel_rapl x86_pkg_temp_thermal dell_laptop intel_powerclamp snd_soc_skl_ipc dell_smbios coretemp snd_soc_sst_ipc dell_wmi_descriptor kvm_intel dcdbas snd_soc_sst_dsp snd_hda_ext_core kvmgt dell_smm_hwmon snd_soc_acpi_intel_match mac80211 vfio_mdev crct10dif_pclmul mdev vfio_iommu_type1 crc32_pclmul snd_soc_acpi vfio ghash_clmulni_intel snd_soc_core kvm pcbc snd_compress ac97_bus aesni_intel snd_hda_codec_hdmi snd_pcm_dmaengine chash gpu_sched aes_x86_64 snd_hda_intel crypto_simd snd_hda_codec cryptd ttm glue_helper pcmcia irqbypass snd_hda_core drm_kms_helper intel_cstate intel_rapl_perf snd_hwdep uvcvideo snd_pcm
[16862.520008]  drm cdc_ether iwlwifi cdc_mbim videobuf2_vmalloc snd_seq_midi videobuf2_memops cdc_ncm snd_seq_midi_event videobuf2_v4l2 snd_rawmidi videobuf2_common videodev input_leds serio_raw btusb qmi_wwan btrtl r8152 snd_seq btbcm media cdc_wdm btintel usbnet option mii usb_wwan bluetooth usbserial cdc_acm hid_multitouch joydev snd_seq_device snd_timer cfg80211 snd yenta_socket pcmcia_rsrc idma64 pcmcia_core soundcore virt_dma i2c_algo_bit fb_sys_fops syscopyarea mei_me sysfillrect ecdh_generic sysimgblt mei ucsi_acpi intel_lpss_pci processor_thermal_device typec_ucsi intel_lpss intel_soc_dts_iosf intel_pch_thermal typec wmi soc_button_array mac_hid intel_hid int3400_thermal video int3403_thermal dell_smo8800 sparse_keymap acpi_thermal_rel int340x_thermal_zone acpi_pad sch_fq_codel parport_pc
[16862.520033]  ppdev lp parport ip_tables x_tables autofs4 hid_generic usbhid uas usb_storage e1000e ahci libahci i2c_hid hid
[16862.520040] CPU: 5 PID: 3015 Comm: fwupd Not tainted 4.19.0 #1
[16862.520040] Hardware name: Dell Inc. Latitude 7424 Rugged Extreme/, BIOS 1.0.0 09/12/2018
[16862.520062] RIP: 0010:generic_reg_wait+0xe2/0x160 [amdgpu]
[16862.520063] Code: ab 44 8b 45 20 48 8b 4d 18 44 89 e6 8b 55 10 48 c7 c7 f0 bf 12 c1 44 89 4d d4 e8 79 1d 65 ff 41 83 7d 18 01 44 8b 4d d4 74 02 <0f> 0b 48 83 c4 18 44 89 c8 5b 41 5c 41 5d 41 5e 41 5f 5d c3 41 0f
[16862.520064] RSP: 0018:ffffbdff831ffbe0 EFLAGS: 00010297
[16862.520065] RAX: 0000000000000000 RBX: 00000000000000a1 RCX: 0000000000000000
[16862.520065] RDX: 0000000000000000 RSI: ffff965fee356428 RDI: ffff965fee356428
[16862.520066] RBP: ffffbdff831ffc20 R08: 00000000000004d5 R09: 00000000ffffffff
[16862.520066] R10: fffff464915dbd00 R11: ffffffff981923ed R12: 000000000000000a
[16862.520067] R13: ffff965fdea5d480 R14: 0000000000000001 R15: 0000000000000000
[16862.520068] FS:  00007f17444485c0(0000) GS:ffff965fee340000(0000) knlGS:0000000000000000
[16862.520068] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[16862.520069] CR2: 00007f1732987150 CR3: 000000045f4fe006 CR4: 00000000003606e0
[16862.520070] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[16862.520070] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[16862.520070] Call Trace:
[16862.520097]  submit_channel_request+0x394/0x480 [amdgpu]
[16862.520118]  dc_link_aux_transfer+0xca/0x160 [amdgpu]
[16862.520142]  dm_dp_aux_transfer+0x59/0x120 [amdgpu]
[16862.520146]  drm_dp_dpcd_access+0x75/0x110 [drm_kms_helper]
[16862.520148]  drm_dp_dpcd_read+0x33/0xc0 [drm_kms_helper]
[16862.520152]  auxdev_read_iter+0x10f/0x1d0 [drm_kms_helper]
[16862.520154]  __vfs_read+0x106/0x180
[16862.520155]  vfs_read+0x8e/0x130
[16862.520156]  ksys_read+0x55/0xc0
[16862.520157]  __x64_sys_read+0x1a/0x20
[16862.520159]  do_syscall_64+0x5a/0x120
[16862.520161]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[16862.520162] RIP: 0033:0x7f1741b3a384
[16862.520163] Code: 84 00 00 00 00 00 41 54 55 49 89 d4 53 48 89 f5 89 fb 48 83 ec 10 e8 8b fc ff ff 4c 89 e2 41 89 c0 48 89 ee 89 df 31 c0 0f 05 <48> 3d 00 f0 ff ff 77 38 44 89 c7 48 89 44 24 08 e8 c7 fc ff ff 48
[16862.520163] RSP: 002b:00007fffadd980f0 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
[16862.520164] RAX: ffffffffffffffda RBX: 0000000000000013 RCX: 00007f1741b3a384
[16862.520165] RDX: 0000000000000001 RSI: 00007fffadd98154 RDI: 0000000000000013
[16862.520165] RBP: 00007fffadd98154 R08: 0000000000000000 R09: 000055fd7e9c7010
[16862.520166] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000001
[16862.520166] R13: 00007fffadd981c0 R14: 00007fffadd98154 R15: 000055fd7eae35b0
[16862.520167] ---[ end trace 8e8b280a234718a5 ]---
[16862.520176] BUG: unable to handle kernel NULL pointer dereference at 0000000000000008
[16862.520177] PGD 0 P4D 0
[16862.520179] Oops: 0000 [#1] SMP PTI
[16862.520180] CPU: 5 PID: 3015 Comm: fwupd Tainted: G        W         4.19.0 #1
[16862.520181] Hardware name: Dell Inc. Latitude 7424 Rugged Extreme/, BIOS 1.0.0 09/12/2018
[16862.520205] RIP: 0010:dal_ddc_close+0xd/0x30 [amdgpu]
[16862.520207] Code: f1 00 e8 06 f5 ff ff 48 8b 55 f8 65 48 33 14 25 28 00 00 00 75 02 c9 c3 e8 70 09 65 d5 0f 1f 44 00 00 55 48 89 e5 53 48 89 fb <48> 8b 7f 08 e8 da f5 ff ff 48 8b 3b e8 d2 f5 ff ff 5b 5d c3 66 2e
[16862.520208] RSP: 0018:ffffbdff831ffc58 EFLAGS: 00010246
[16862.520209] RAX: ffffffffc103f410 RBX: 0000000000000000 RCX: 0000000000000000
[16862.520210] RDX: ffffffffc10de660 RSI: 0000000000005c58 RDI: 0000000000000000
[16862.520211] RBP: ffffbdff831ffc60 R08: 0000000000000001 R09: 00000000ffffffff
[16862.520211] R10: fffff464915dbd00 R11: 0000000000000001 R12: 0000000000000000
[16862.520212] R13: ffffbdff831ffdd0 R14: ffffbdff831ffd3d R15: 0000000000000000
[16862.520213] FS:  00007f17444485c0(0000) GS:ffff965fee340000(0000) knlGS:0000000000000000
[16862.520214] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[16862.520215] CR2: 0000000000000008 CR3: 000000045f4fe006 CR4: 00000000003606e0
[16862.520216] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[16862.520217] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[16862.520218] Call Trace:
[16862.520242]  release_engine+0x16/0x70 [amdgpu]
[16862.520263]  dc_link_aux_transfer+0xfd/0x160 [amdgpu]
[16862.520287]  dm_dp_aux_transfer+0x59/0x120 [amdgpu]
[16862.520290]  drm_dp_dpcd_access+0x75/0x110 [drm_kms_helper]
[16862.520293]  drm_dp_dpcd_read+0x33/0xc0 [drm_kms_helper]
[16862.520296]  auxdev_read_iter+0x10f/0x1d0 [drm_kms_helper]
[16862.520298]  __vfs_read+0x106/0x180
[16862.520300]  vfs_read+0x8e/0x130
[16862.520301]  ksys_read+0x55/0xc0
[16862.520302]  __x64_sys_read+0x1a/0x20
[16862.520304]  do_syscall_64+0x5a/0x120
[16862.520306]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[16862.520307] RIP: 0033:0x7f1741b3a384
[16862.520308] Code: 84 00 00 00 00 00 41 54 55 49 89 d4 53 48 89 f5 89 fb 48 83 ec 10 e8 8b fc ff ff 4c 89 e2 41 89 c0 48 89 ee 89 df 31 c0 0f 05 <48> 3d 00 f0 ff ff 77 38 44 89 c7 48 89 44 24 08 e8 c7 fc ff ff 48
[16862.520309] RSP: 002b:00007fffadd980f0 EFLAGS: 00000246 ORIG_RAX: 0000000000000000
[16862.520310] RAX: ffffffffffffffda RBX: 0000000000000013 RCX: 00007f1741b3a384
[16862.520311] RDX: 0000000000000001 RSI: 00007fffadd98154 RDI: 0000000000000013
[16862.520312] RBP: 00007fffadd98154 R08: 0000000000000000 R09: 000055fd7e9c7010
[16862.520312] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000001
[16862.520313] R13: 00007fffadd981c0 R14: 00007fffadd98154 R15: 000055fd7eae35b0
[16862.520314] Modules linked in: rfcomm snd_hda_codec_realtek snd_hda_codec_generic cmac bnep arc4 nls_iso8859_1 amdkfd amd_iommu_v2 amdgpu i915 dell_rbtn iwlmvm snd_soc_skl wmi_bmof dell_wmi intel_rapl x86_pkg_temp_thermal dell_laptop intel_powerclamp snd_soc_skl_ipc dell_smbios coretemp snd_soc_sst_ipc dell_wmi_descriptor kvm_intel dcdbas snd_soc_sst_dsp snd_hda_ext_core kvmgt dell_smm_hwmon snd_soc_acpi_intel_match mac80211 vfio_mdev crct10dif_pclmul mdev vfio_iommu_type1 crc32_pclmul snd_soc_acpi vfio ghash_clmulni_intel snd_soc_core kvm pcbc snd_compress ac97_bus aesni_intel snd_hda_codec_hdmi snd_pcm_dmaengine chash gpu_sched aes_x86_64 snd_hda_intel crypto_simd snd_hda_codec cryptd ttm glue_helper pcmcia irqbypass snd_hda_core drm_kms_helper intel_cstate intel_rapl_perf snd_hwdep uvcvideo snd_pcm
[16862.520332]  drm cdc_ether iwlwifi cdc_mbim videobuf2_vmalloc snd_seq_midi videobuf2_memops cdc_ncm snd_seq_midi_event videobuf2_v4l2 snd_rawmidi videobuf2_common videodev input_leds serio_raw btusb qmi_wwan btrtl r8152 snd_seq btbcm media cdc_wdm btintel usbnet option mii usb_wwan bluetooth usbserial cdc_acm hid_multitouch joydev snd_seq_device snd_timer cfg80211 snd yenta_socket pcmcia_rsrc idma64 pcmcia_core soundcore virt_dma i2c_algo_bit fb_sys_fops syscopyarea mei_me sysfillrect ecdh_generic sysimgblt mei ucsi_acpi intel_lpss_pci processor_thermal_device typec_ucsi intel_lpss intel_soc_dts_iosf intel_pch_thermal typec wmi soc_button_array mac_hid intel_hid int3400_thermal video int3403_thermal dell_smo8800 sparse_keymap acpi_thermal_rel int340x_thermal_zone acpi_pad sch_fq_codel parport_pc
[16862.520351]  ppdev lp parport ip_tables x_tables autofs4 hid_generic usbhid uas usb_storage e1000e ahci libahci i2c_hid hid
[16862.520356] CR2: 0000000000000008
[16862.520357] ---[ end trace 8e8b280a234718a6 ]---
[16862.520380] RIP: 0010:dal_ddc_close+0xd/0x30 [amdgpu]
[16862.520382] Code: f1 00 e8 06 f5 ff ff 48 8b 55 f8 65 48 33 14 25 28 00 00 00 75 02 c9 c3 e8 70 09 65 d5 0f 1f 44 00 00 55 48 89 e5 53 48 89 fb <48> 8b 7f 08 e8 da f5 ff ff 48 8b 3b e8 d2 f5 ff ff 5b 5d c3 66 2e
[16862.520382] RSP: 0018:ffffbdff831ffc58 EFLAGS: 00010246
[16862.520384] RAX: ffffffffc103f410 RBX: 0000000000000000 RCX: 0000000000000000
[16862.520384] RDX: ffffffffc10de660 RSI: 0000000000005c58 RDI: 0000000000000000
[16862.520385] RBP: ffffbdff831ffc60 R08: 0000000000000001 R09: 00000000ffffffff
[16862.520386] R10: fffff464915dbd00 R11: 0000000000000001 R12: 0000000000000000
[16862.520387] R13: ffffbdff831ffdd0 R14: ffffbdff831ffd3d R15: 0000000000000000
[16862.520388] FS:  00007f17444485c0(0000) GS:ffff965fee340000(0000) knlGS:0000000000000000
[16862.520389] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[16862.520390] CR2: 0000000000000008 CR3: 000000045f4fe006 CR4: 00000000003606e0
[16862.520391] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[16862.520392] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
```
2019-03-29 08:13:16 -05:00

504 lines
15 KiB
C

/*
* Copyright (C) 2017 Mario Limonciello <mario.limonciello@dell.com>
* Copyright (C) 2017 Peichen Huang <peichenhuang@tw.synaptics.com>
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "synapticsmst-device.h"
#include "synapticsmst-common.h"
#include "fu-plugin-vfuncs.h"
#include "fu-device-metadata.h"
#define SYNAPTICS_FLASH_MODE_DELAY 3
#define SYNAPTICS_UPDATE_ENUMERATE_TRIES 3
static gboolean
syanpticsmst_check_amdgpu_safe (GError **error)
{
gsize bufsz = 0;
g_autofree gchar *buf = NULL;
g_auto(GStrv) lines = NULL;
if (!g_file_get_contents ("/proc/modules", &buf, &bufsz, error))
return FALSE;
lines = g_strsplit (buf, "\n", -1);
for (guint i = 0; lines[i] != NULL; i++) {
if (g_str_has_prefix (lines[i], "amdgpu ")) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"amdgpu has known issues with synapticsmst");
return FALSE;
}
}
return TRUE;
}
static gboolean
synapticsmst_common_check_supported_system (FuPlugin *plugin, GError **error)
{
if (g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR") != NULL) {
g_debug ("Running Synaptics plugin in test mode");
return TRUE;
}
/* See https://github.com/hughsie/fwupd/issues/1121 for more details */
if (!syanpticsmst_check_amdgpu_safe (error))
return FALSE;
if (!g_file_test (SYSFS_DRM_DP_AUX, G_FILE_TEST_IS_DIR)) {
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
"MST firmware updating not supported, missing kernel support.");
return FALSE;
}
return TRUE;
}
/* creates MST-$str-$BOARDID */
static void
fu_plugin_synapticsmst_create_simple_guid (FuDevice *fu_device,
SynapticsMSTDevice *device,
const gchar *str)
{
guint16 board_id = synapticsmst_device_get_board_id (device);
g_autofree gchar *devid = g_strdup_printf ("MST-%s-%u", str, board_id);
fu_device_add_instance_id (fu_device, devid);
}
/* creates MST-$str-$chipid-$BOARDID */
static void
fu_plugin_synapticsmst_create_complex_guid (FuDevice *fu_device,
SynapticsMSTDevice *device,
const gchar *device_kind)
{
const gchar *chip_id_str = synapticsmst_device_get_chip_id_str (device);
g_autofree gchar *chip_id_down = g_ascii_strdown (chip_id_str, -1);
g_autofree gchar *tmp = g_strdup_printf ("%s-%s", device_kind, chip_id_down);
fu_plugin_synapticsmst_create_simple_guid (fu_device, device, tmp);
}
static gboolean
fu_plugin_synapticsmst_lookup_device (FuPlugin *plugin,
FuDevice *fu_device,
SynapticsMSTDevice *device,
GError **error)
{
const gchar *board_str;
const gchar *guid_template;
guint16 board_id = synapticsmst_device_get_board_id (device);
const gchar *chip_id_str = synapticsmst_device_get_chip_id_str (device);
g_autofree gchar *group = NULL;
g_autofree gchar *name = NULL;
/* GUIDs used only for test mode */
if (g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR") != NULL) {
g_autofree gchar *tmp = NULL;
tmp = g_strdup_printf ("test-%s", chip_id_str);
fu_plugin_synapticsmst_create_simple_guid (fu_device, device, tmp);
return TRUE;
}
/* set up the device name via quirks */
group = g_strdup_printf ("SynapticsMSTBoardID=%u", board_id);
board_str = fu_plugin_lookup_quirk_by_id (plugin, group,
FU_QUIRKS_NAME);
if (board_str == NULL)
board_str = "Unknown Platform";
name = g_strdup_printf ("Synaptics %s inside %s", synapticsmst_device_get_chip_id_str (device),
board_str);
fu_device_set_name (fu_device, name);
/* build the GUIDs for the device */
guid_template = fu_plugin_lookup_quirk_by_id (plugin, group, "DeviceKind");
/* no quirks defined for this board */
if (guid_template == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Unknown board_id %x",
board_id);
return FALSE;
/* this is a host system, use system ID */
} else if (g_strcmp0 (guid_template, "system") == 0) {
const gchar *system_type = fu_plugin_get_dmi_value (plugin,
FU_HWIDS_KEY_PRODUCT_SKU);
fu_plugin_synapticsmst_create_simple_guid (fu_device, device,
system_type);
/* docks or something else */
} else {
g_auto(GStrv) templates = NULL;
templates = g_strsplit (guid_template, ",", -1);
for (guint i = 0; templates[i] != NULL; i++) {
fu_plugin_synapticsmst_create_complex_guid (fu_device,
device,
templates[i]);
}
}
return TRUE;
}
static gboolean
fu_plugin_synaptics_add_device (FuPlugin *plugin,
SynapticsMSTDevice *device,
GError **error)
{
g_autoptr(FuDevice) dev = NULL;
const gchar *kind_str = NULL;
g_autofree gchar *dev_id_str = NULL;
g_autofree gchar *layer_str = NULL;
g_autofree gchar *rad_str = NULL;
const gchar *aux_node;
guint8 layer;
guint16 rad;
aux_node = synapticsmst_device_get_aux_node (device);
if (!synapticsmst_device_enumerate_device (device,
error)) {
g_prefix_error (error, "Error enumerating device at %s: ", aux_node);
return FALSE;
}
/* create the device */
dev = fu_device_new ();
/* Store $KIND-$AUXNODE-$LAYER-$RAD as device ID */
layer = synapticsmst_device_get_layer (device);
rad = synapticsmst_device_get_rad (device);
kind_str = synapticsmst_device_kind_to_string (synapticsmst_device_get_kind (device));
dev_id_str = g_strdup_printf ("MST-%s-%s-%u-%u",
kind_str, aux_node, layer, rad);
fu_device_set_id (dev, dev_id_str);
fu_device_set_physical_id (dev, aux_node);
fu_device_set_metadata (dev, "SynapticsMSTKind", kind_str);
fu_device_set_metadata (dev, "SynapticsMSTAuxNode", aux_node);
layer_str = g_strdup_printf ("%u", layer);
fu_device_set_metadata (dev, "SynapticsMSTLayer", layer_str);
rad_str = g_strdup_printf ("%u", rad);
fu_device_set_metadata (dev, "SynapticsMSTRad", rad_str);
fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_set_vendor (dev, "Synaptics");
fu_device_set_summary (dev, "Multi-Stream Transport Device");
fu_device_add_icon (dev, "computer");
fu_device_set_version (dev, synapticsmst_device_get_version (device));
fu_device_set_quirks (dev, fu_plugin_get_quirks (plugin));
/* create GUIDs and name */
if (!fu_plugin_synapticsmst_lookup_device (plugin, dev, device, error))
return FALSE;
fu_plugin_device_add (plugin, dev);
fu_plugin_cache_add (plugin, dev_id_str, dev);
/* inhibit the idle sleep of the daemon */
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE,
"SynapticsMST can cause the screen to flash when probing");
return TRUE;
}
static gboolean
fu_plugin_synaptics_scan_cascade (FuPlugin *plugin,
SynapticsMSTDevice *device,
GError **error)
{
g_autoptr(SynapticsMSTDevice) cascade_device = NULL;
FuDevice *fu_dev = NULL;
const gchar *aux_node;
aux_node = synapticsmst_device_get_aux_node (device);
if (!synapticsmst_device_open (device, error)) {
g_prefix_error (error,
"failed to open aux node %s again",
aux_node);
return FALSE;
}
for (guint8 j = 0; j < 2; j++) {
guint8 layer = synapticsmst_device_get_layer (device) + 1;
guint16 rad = synapticsmst_device_get_rad (device) | (j << (2 * (layer - 1)));
g_autofree gchar *dev_id_str = NULL;
dev_id_str = g_strdup_printf ("MST-REMOTE-%s-%u-%u",
aux_node, layer, rad);
fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str);
/* run the scan */
if (!synapticsmst_device_scan_cascade_device (device, error, j))
return FALSE;
/* check if cascaded device was found */
if (!synapticsmst_device_get_cascade (device)) {
/* not found, nothing new to see here, move along */
if (fu_dev == NULL)
continue;
/* not found, but should have existed - remove it */
else {
fu_plugin_device_remove (plugin, fu_dev);
fu_plugin_cache_remove (plugin, dev_id_str);
/* don't scan any deeper on this node */
continue;
}
/* Found a device, add it */
} else {
cascade_device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_REMOTE,
aux_node, layer, rad);
/* new device */
if (fu_dev == NULL) {
g_debug ("Adding remote device %s", dev_id_str);
if (!fu_plugin_synaptics_add_device (plugin, cascade_device, error))
return FALSE;
}
else
g_debug ("Skipping previously added device %s",
dev_id_str);
/* check recursively for more devices */
if (!fu_plugin_synaptics_scan_cascade (plugin, cascade_device, error))
return FALSE;
}
}
return TRUE;
}
static void
fu_plugin_synapticsmst_remove_cascaded (FuPlugin *plugin, const gchar *aux_node)
{
FuDevice *fu_dev = NULL;
for (guint8 i = 0; i < 8; i++) {
for (guint16 j = 0; j < 256; j++) {
g_autofree gchar *dev_id_str = NULL;
dev_id_str = g_strdup_printf ("MST-REMOTE-%s-%u-%u",
aux_node, i, j);
fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str);
if (fu_dev != NULL) {
fu_plugin_device_remove (plugin, fu_dev);
fu_plugin_cache_remove (plugin, dev_id_str);
continue;
}
break;
}
}
}
static gboolean
fu_plugin_synapticsmst_enumerate (FuPlugin *plugin,
GError **error)
{
g_autoptr(GDir) dir = NULL;
const gchar *dp_aux_dir;
const gchar *aux_node = NULL;
dp_aux_dir = g_getenv ("FWUPD_SYNAPTICSMST_FW_DIR");
if (dp_aux_dir == NULL)
dp_aux_dir = SYSFS_DRM_DP_AUX;
else
g_debug ("Using %s to look for MST devices", dp_aux_dir);
dir = g_dir_open (dp_aux_dir, 0, NULL);
do {
g_autofree gchar *dev_id_str = NULL;
g_autoptr(GError) error_local = NULL;
g_autoptr(SynapticsMSTDevice) device = NULL;
FuDevice *fu_dev = NULL;
aux_node = g_dir_read_name (dir);
if (aux_node == NULL)
break;
dev_id_str = g_strdup_printf ("MST-DIRECT-%s-0-0", aux_node);
fu_dev = fu_plugin_cache_lookup (plugin, dev_id_str);
/* If we open succesfully a device exists here */
device = synapticsmst_device_new (SYNAPTICSMST_DEVICE_KIND_DIRECT, aux_node, 0, 0);
if (!synapticsmst_device_open (device, &error_local)) {
/* No device exists here, but was there - remove from DB */
if (fu_dev != NULL) {
g_debug ("Removing devices on %s", aux_node);
fu_plugin_device_remove (plugin, fu_dev);
fu_plugin_cache_remove (plugin, dev_id_str);
fu_plugin_synapticsmst_remove_cascaded (plugin,
aux_node);
} else {
/* Nothing to see here - move on*/
g_debug ("No device found on %s: %s", aux_node, error_local->message);
g_clear_error (&error_local);
}
continue;
}
/* Add direct devices */
if (fu_dev == NULL) {
g_debug ("Adding direct device %s", dev_id_str);
if (!fu_plugin_synaptics_add_device (plugin, device, &error_local))
g_debug ("failed to add device: %s", error_local->message);
} else {
g_debug ("Skipping previously added device %s", dev_id_str);
}
/* recursively look for cascade devices */
if (!fu_plugin_synaptics_scan_cascade (plugin, device, error))
return FALSE;
} while(TRUE);
return TRUE;
}
static void
fu_synapticsmst_write_progress_cb (goffset current, goffset total, gpointer user_data)
{
FuDevice *device = FU_DEVICE (user_data);
fu_device_set_progress_full (device, current, total);
}
gboolean
fu_plugin_update (FuPlugin *plugin,
FuDevice *dev,
GBytes *blob_fw,
FwupdInstallFlags flags,
GError **error)
{
g_autoptr(SynapticsMSTDevice) device = NULL;
SynapticsMSTDeviceKind kind;
const gchar *aux_node;
guint8 layer;
guint8 rad;
gboolean reboot;
gboolean install_force;
/* extract details to build a new device */
kind = synapticsmst_device_kind_from_string (fu_device_get_metadata (dev, "SynapticsMSTKind"));
aux_node = fu_device_get_metadata (dev, "SynapticsMSTAuxNode");
layer = g_ascii_strtoull (fu_device_get_metadata (dev, "SynapticsMSTLayer"), NULL, 0);
rad = g_ascii_strtoull (fu_device_get_metadata (dev, "SynapticsMSTRad"), NULL, 0);
/* sleep to allow device wakeup to complete */
g_debug ("waiting %d seconds for MST hub wakeup",
SYNAPTICS_FLASH_MODE_DELAY);
fu_device_set_status (dev, FWUPD_STATUS_DEVICE_BUSY);
g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000);
device = synapticsmst_device_new (kind, aux_node, layer, rad);
if (!synapticsmst_device_enumerate_device (device, error))
return FALSE;
reboot = !fu_device_has_custom_flag (dev, "skip-restart");
install_force = (flags & FWUPD_INSTALL_FLAG_FORCE) != 0 ||
fu_device_has_custom_flag (dev, "ignore-board-id");
fu_device_set_status (dev, FWUPD_STATUS_DEVICE_WRITE);
if (!synapticsmst_device_write_firmware (device, blob_fw,
fu_synapticsmst_write_progress_cb,
dev,
reboot,
install_force,
error)) {
g_prefix_error (error, "failed to flash firmware: ");
return FALSE;
}
if (!reboot) {
g_debug ("Skipping device restart per quirk request");
return TRUE;
}
/* Re-run device enumeration to find the new device version */
fu_device_set_status (dev, FWUPD_STATUS_DEVICE_RESTART);
for (guint i = 1; i <= SYNAPTICS_UPDATE_ENUMERATE_TRIES; i++) {
g_autoptr(GError) error_local = NULL;
g_usleep (SYNAPTICS_FLASH_MODE_DELAY * 1000000);
if (!synapticsmst_device_enumerate_device (device,
&error_local)) {
g_warning ("Unable to find device after %u seconds: %s",
SYNAPTICS_FLASH_MODE_DELAY * i,
error_local->message);
if (i == SYNAPTICS_UPDATE_ENUMERATE_TRIES) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"%s",
error_local->message);
return FALSE;
}
}
}
fu_device_set_version (dev, synapticsmst_device_get_version (device));
return TRUE;
}
gboolean
fu_plugin_device_removed (FuPlugin *plugin, FuDevice *device, GError **error)
{
const gchar *aux_node;
const gchar *kind_str;
const gchar *layer_str;
const gchar *rad_str;
g_autofree gchar *dev_id_str = NULL;
aux_node = fu_device_get_metadata (device, "SynapticsMSTAuxNode");
if (aux_node == NULL)
return TRUE;
kind_str = fu_device_get_metadata (device, "SynapticsMSTKind");
if (kind_str == NULL)
return TRUE;
layer_str = fu_device_get_metadata (device, "SynapticsMSTLayer");
if (layer_str == NULL)
return TRUE;
rad_str = fu_device_get_metadata (device, "SynapticsMSTRad");
if (rad_str == NULL)
return TRUE;
dev_id_str = g_strdup_printf ("MST-%s-%s-%s-%s",
kind_str, aux_node, layer_str, rad_str);
if (fu_plugin_cache_lookup (plugin, dev_id_str) != NULL) {
g_debug ("Removing %s from cache", dev_id_str);
fu_plugin_cache_remove (plugin, dev_id_str);
} else {
g_debug ("%s constructed but not found in cache", dev_id_str);
}
return TRUE;
}
static gboolean
fu_plugin_synapticsmst_coldplug (FuPlugin *plugin, GError **error)
{
g_autoptr(GError) error_local = NULL;
/* verify that this is a supported system */
if (!synapticsmst_common_check_supported_system (plugin, error))
return FALSE;
/* look for host devices or already plugged in dock devices */
if (!fu_plugin_synapticsmst_enumerate (plugin, &error_local))
g_debug ("error enumerating: %s", error_local->message);
return TRUE;
}
gboolean
fu_plugin_coldplug (FuPlugin *plugin, GError **error)
{
return fu_plugin_synapticsmst_coldplug (plugin, error);
}
gboolean
fu_plugin_recoldplug (FuPlugin *plugin, GError **error)
{
return fu_plugin_synapticsmst_coldplug (plugin, error);
}
void
fu_plugin_init (FuPlugin *plugin)
{
/* make sure dell is already coldplugged */
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "dell");
fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_SUPPORTS_PROTOCOL, "com.synaptics.mst");
fu_plugin_set_build_hash (plugin, FU_BUILD_HASH);
}